D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
proc
/
2
/
root
/
opt
/
dedrads
/
Filename :
cmspass.py
back
Copy
#!/usr/lib/rads/venv/bin/python3 """Scans a cPanel account for WordPress and Joomla! configuration files, then uses whmapi1 to reset database passwords to match what was found in them. This script logs to /var/log/messages""" import subprocess import re import sys from argparse import ArgumentParser from pathlib import Path import rads import logging from logging.handlers import SysLogHandler from cpapis import whmapi1, CpAPIError WP_CONF = re.compile(r"DB_(USER|PASSWORD).*['\"](.+)['\"]") JM_CONF = re.compile(r"\b(user|password) = '(.+)'") def setup_logging() -> logging.Logger: logger = logging.getLogger('cmspass.py') logger.setLevel(logging.DEBUG) out_fmt = logging.Formatter(fmt='%(levelname)s: %(message)s') log_fmt = logging.Formatter(fmt='cmspass.py: %(levelname)s: %(message)s') stdout = logging.StreamHandler(stream=sys.stdout) stdout.setFormatter(out_fmt) stdout.setLevel(logging.DEBUG) logger.addHandler(stdout) syslog = SysLogHandler(address='/dev/log') syslog.setFormatter(log_fmt) syslog.setLevel(logging.WARNING) logger.addHandler(syslog) return logger def conf_parse(regex: re.Pattern, path: Path) -> tuple[str | None, str | None]: """Will parse db conf variables""" db_user, db_pass = None, None with open(path, encoding='utf-8') as file: for line in file: if match := regex.search(line): key, val = match.groups() key: str val: str if key.lower() == 'user': db_user = val elif key.lower() == 'password': db_pass = val return db_user, db_pass def set_pass(logger: logging.Logger, cpuser: str, db_user: str, db_pass: str): """Will pass the reset variables to the cPanel API""" if db_user is None or db_pass is None: return try: whmapi1( 'set_mysql_password', args={'cpuser': cpuser, 'user': db_user, 'password': db_pass}, check=True, ) except CpAPIError as exc: logger.error("Failed to reset password for %s: %s", db_user, exc) else: logger.warning("Reset password for %s", db_user) def parse_args() -> str: """Will parse input for user and ensure the user exists""" parser = ArgumentParser(description=__doc__) parser.add_argument('-u', '--user', required=True, help='cPanel username') user = parser.parse_args().user if not rads.cpuser_safe(user): sys.exit(f"{user} does not exist or is restricted") return user def find_files(homedir: str) -> list[Path]: """Find all config files""" # fmt: off cmd = [ 'find', homedir, '-not', '(', '-path', f"{homedir}/mail", '-prune', ')', '(', '-name', 'wp-config.php', '-o', '-name', 'configuration.php', ')', '-type', 'f', '-print0', ] # fmt: on ret = subprocess.run( cmd, stdout=subprocess.PIPE, encoding='utf-8', check=False ) return [Path(x) for x in ret.stdout.split('\0') if x] def conf_is_joomla(path: Path): if path.name != 'configuration.php': return False with open(path, encoding='utf-8') as conf: for line in conf: if 'class JConfig' in line: return True return False def main(): user = parse_args() logger = setup_logging() try: homedir = rads.get_homedir(user) except rads.CpuserError as exc: sys.exit(exc) for path in find_files(homedir): db_user, db_pass = None, None if path.name == 'wp-config.php': logger.debug('Scanning %s', path) db_user, db_pass = conf_parse(WP_CONF, path) elif conf_is_joomla(path): logger.debug('Scanning %s', path) db_user, db_pass = conf_parse(JM_CONF, path) if db_user and db_pass: set_pass(logger, user, db_user, db_pass) if __name__ == "__main__": main()