Edit File by line
/home/barbar84/public_h.../wp-conte.../plugins/sujqvwi/AnonR/anonr.TX.../proc/self/root/opt/sharedra...
File: cmspass.py
#!/opt/imh-python/bin/python3
[0] Fix | Delete
"""Scans a cPanel account for WordPress and Joomla! configuration files, then
[1] Fix | Delete
uses whmapi1 to reset database passwords to match what was found in them.
[2] Fix | Delete
This script logs to /var/log/messages"""
[3] Fix | Delete
[4] Fix | Delete
import subprocess
[5] Fix | Delete
import re
[6] Fix | Delete
import sys
[7] Fix | Delete
from argparse import ArgumentParser
[8] Fix | Delete
from pathlib import Path
[9] Fix | Delete
from typing import Union
[10] Fix | Delete
import rads
[11] Fix | Delete
import logging
[12] Fix | Delete
from logging.handlers import SysLogHandler
[13] Fix | Delete
from cpapis import whmapi1, CpAPIError
[14] Fix | Delete
[15] Fix | Delete
WP_CONF = re.compile(r"DB_(USER|PASSWORD).*['\"](.+)['\"]")
[16] Fix | Delete
JM_CONF = re.compile(r"\b(user|password) = '(.+)'")
[17] Fix | Delete
[18] Fix | Delete
[19] Fix | Delete
def setup_logging() -> logging.Logger:
[20] Fix | Delete
logger = logging.getLogger('cmspass.py')
[21] Fix | Delete
logger.setLevel(logging.DEBUG)
[22] Fix | Delete
out_fmt = logging.Formatter(fmt='%(levelname)s: %(message)s')
[23] Fix | Delete
log_fmt = logging.Formatter(fmt='cmspass.py: %(levelname)s: %(message)s')
[24] Fix | Delete
stdout = logging.StreamHandler(stream=sys.stdout)
[25] Fix | Delete
stdout.setFormatter(out_fmt)
[26] Fix | Delete
stdout.setLevel(logging.DEBUG)
[27] Fix | Delete
logger.addHandler(stdout)
[28] Fix | Delete
syslog = SysLogHandler(address='/dev/log')
[29] Fix | Delete
syslog.setFormatter(log_fmt)
[30] Fix | Delete
syslog.setLevel(logging.WARNING)
[31] Fix | Delete
logger.addHandler(syslog)
[32] Fix | Delete
return logger
[33] Fix | Delete
[34] Fix | Delete
[35] Fix | Delete
def conf_parse(
[36] Fix | Delete
regex: re.Pattern, path: Path
[37] Fix | Delete
) -> tuple[Union[str, None], Union[str, None]]:
[38] Fix | Delete
"""Will parse db conf variables"""
[39] Fix | Delete
db_user, db_pass = None, None
[40] Fix | Delete
with open(path, encoding='utf-8') as file:
[41] Fix | Delete
for line in file:
[42] Fix | Delete
if match := regex.search(line):
[43] Fix | Delete
key, val = match.groups()
[44] Fix | Delete
key: str
[45] Fix | Delete
val: str
[46] Fix | Delete
if key.lower() == 'user':
[47] Fix | Delete
db_user = val
[48] Fix | Delete
elif key.lower() == 'password':
[49] Fix | Delete
db_pass = val
[50] Fix | Delete
return db_user, db_pass
[51] Fix | Delete
[52] Fix | Delete
[53] Fix | Delete
def set_pass(logger: logging.Logger, cpuser: str, db_user: str, db_pass: str):
[54] Fix | Delete
"""Will pass the reset variables to the cPanel API"""
[55] Fix | Delete
if db_user is None or db_pass is None:
[56] Fix | Delete
return
[57] Fix | Delete
try:
[58] Fix | Delete
whmapi1(
[59] Fix | Delete
'set_mysql_password',
[60] Fix | Delete
args={'cpuser': cpuser, 'user': db_user, 'password': db_pass},
[61] Fix | Delete
check=True,
[62] Fix | Delete
)
[63] Fix | Delete
except CpAPIError as exc:
[64] Fix | Delete
logger.error("Failed to reset password for %s: %s", db_user, exc)
[65] Fix | Delete
else:
[66] Fix | Delete
logger.warning("Reset password for %s", db_user)
[67] Fix | Delete
[68] Fix | Delete
[69] Fix | Delete
def parse_args() -> str:
[70] Fix | Delete
"""Will parse input for user and ensure the user exists"""
[71] Fix | Delete
parser = ArgumentParser(description=__doc__)
[72] Fix | Delete
parser.add_argument('-u', '--user', required=True, help='cPanel username')
[73] Fix | Delete
user = parser.parse_args().user
[74] Fix | Delete
if not rads.cpuser_safe(user):
[75] Fix | Delete
sys.exit(f"{user} does not exist or is restricted")
[76] Fix | Delete
return user
[77] Fix | Delete
[78] Fix | Delete
[79] Fix | Delete
def find_files(homedir: str) -> list[Path]:
[80] Fix | Delete
"""Find all config files"""
[81] Fix | Delete
# fmt: off
[82] Fix | Delete
cmd = [
[83] Fix | Delete
'find', homedir, '-not', '(', '-path', f"{homedir}/mail", '-prune', ')',
[84] Fix | Delete
'(', '-name', 'wp-config.php', '-o', '-name', 'configuration.php', ')',
[85] Fix | Delete
'-type', 'f', '-print0',
[86] Fix | Delete
]
[87] Fix | Delete
# fmt: on
[88] Fix | Delete
ret = subprocess.run(
[89] Fix | Delete
cmd, stdout=subprocess.PIPE, encoding='utf-8', check=False
[90] Fix | Delete
)
[91] Fix | Delete
return [Path(x) for x in ret.stdout.split('\0') if x]
[92] Fix | Delete
[93] Fix | Delete
[94] Fix | Delete
def conf_is_joomla(path: Path):
[95] Fix | Delete
if path.name != 'configuration.php':
[96] Fix | Delete
return False
[97] Fix | Delete
with open(path, encoding='utf-8') as conf:
[98] Fix | Delete
for line in conf:
[99] Fix | Delete
if 'class JConfig' in line:
[100] Fix | Delete
return True
[101] Fix | Delete
return False
[102] Fix | Delete
[103] Fix | Delete
[104] Fix | Delete
def main():
[105] Fix | Delete
user = parse_args()
[106] Fix | Delete
logger = setup_logging()
[107] Fix | Delete
try:
[108] Fix | Delete
homedir = rads.get_homedir(user)
[109] Fix | Delete
except rads.CpuserError as exc:
[110] Fix | Delete
sys.exit(exc)
[111] Fix | Delete
for path in find_files(homedir):
[112] Fix | Delete
db_user, db_pass = None, None
[113] Fix | Delete
if path.name == 'wp-config.php':
[114] Fix | Delete
logger.debug('Scanning %s', path)
[115] Fix | Delete
db_user, db_pass = conf_parse(WP_CONF, path)
[116] Fix | Delete
elif conf_is_joomla(path):
[117] Fix | Delete
logger.debug('Scanning %s', path)
[118] Fix | Delete
db_user, db_pass = conf_parse(JM_CONF, path)
[119] Fix | Delete
if db_user and db_pass:
[120] Fix | Delete
set_pass(logger, user, db_user, db_pass)
[121] Fix | Delete
[122] Fix | Delete
[123] Fix | Delete
if __name__ == "__main__":
[124] Fix | Delete
main()
[125] Fix | Delete
[126] Fix | Delete
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function