BishopFox/CVE-2026-22557-check

GitHub: BishopFox/CVE-2026-22557-check

Stars: 0 | Forks: 0

# CVE-2026-22557 Vulnerability Assessment Tool Safely detect whether a [UniFi Network Application](https://ui.com/download/software/network-application) controller is vulnerable to CVE-2026-22557 without causing any disruption. See the [full write-up](https://bishopfox.com/blog/looting-unifi-controllers-detecting-and-weaponizing-cve-2026-22557) at the Bishop Fox blog. ## Description CVE-2026-22557 is an unauthenticated path-traversal vulnerability in the UniFi Network Application's guest captive portal that allows remote attackers to read arbitrary files as the controller process user (CVSS 10.0). The vulnerability affects UniFi Network Application 10.1.85 and earlier on the 10.1.x branch, and corresponding earlier releases on the 10.2.x and 9.0.x branches. Ubiquiti shipped fixes in 10.1.89, 10.2.97, and 9.0.118 ([SAB-062](https://community.ui.com/releases/Security-Advisory-Bulletin-062-062/c29719c0-405e-4d4a-8f26-e343e99f931b)). This tool performs non-destructive vulnerability testing by: 1. Reading the controller's reported version from `/status` and comparing it against the SAB-062 fix lines 2. Confirming the guest portal is dispatched on the supplied URL 3. Probing the `page_error` parameter on `/guest//wechat/sign` at `../` depths 1-8 against a dual-branch filename, to determine whether the traversal fires at all 4. If the traversal fires, performing one additional read against a non-sensitive runtime catalog file (`firmware.json`) to determine whether the customized-portal filesystem branch is active The tool requests only the runtime catalog file as a branch-active oracle. It does **not** read credentials, TLS material, database state, or backups, and does not write any file content to stdout or disk. Only the verdict is printed. - **Vulnerable controllers** with `portal_customized: true` on the queried site return the catalog file at the calibrated depth, confirming `VULNERABLE`. - **Partially exposed controllers** running a vulnerable build but with `portal_customized: false` on the queried site trigger the traversal at the calibration step but do not return disk content at the confirmation step → `PARTIALLY EXPOSED`. The application binary is still vulnerable; patching is required even though this site is not in the exploitable state. - **Patched controllers** (10.1.89+ / 10.2.97+ / 9.0.118+) reject the traversal at every depth → `NOT VULNERABLE`. - **Unreachable** sites or connectors yield `NOT EXPOSED`. ## Installation git clone https://github.com/BishopFox/CVE-2026-22557-check cd CVE-2026-22557-check Python 3.9+ is required. The tool uses only the Python standard library, so no `pip install` step is needed. ## Usage Test a UniFi Network Application controller at ``. Point the tool at whichever URL reaches the controller's web UI; the vulnerable handler is dispatched on the admin port (default 8443/TCP) as well as the dedicated guest-portal ports (8843, 8880). python3 cve_2026_22557_check.py [--site NAME] The default site slug is `default`. UniFi deployments with multiple sites configured should re-run the tool with `--site ` for each one. A controller is exploitable as soon as *any* one of its sites has `portal_customized: true`. The examples below use `--brief` for a one-line-per-host result. Run without `--brief` to get the full banner, step-by-step progress, and a result block that includes recommended remediation actions. ### Example: Vulnerable Controller The controller is on a vulnerable build and the customized-portal precondition is active for the queried site, so the unauthenticated read path is reachable. $ python3 cve_2026_22557_check.py https://192.168.1.100:8443 --brief [VULNERABLE] https://192.168.1.100:8443 site=default ### Example: Partially Exposed Controller The controller is on a vulnerable build, but the queried site does not have `portal_customized: true` enabled, so the disk-read precondition is not met for this site. The application binary still needs to be patched; another site on the same controller may still be in the exploitable state. $ python3 cve_2026_22557_check.py https://192.168.1.100:8443 --brief [PARTIAL] https://192.168.1.100:8443 site=default ### Example: Patched Controller The controller is on a fixed build (10.1.89, 10.2.97, 9.0.118, or later), so the traversal does not fire on any queried depth. $ python3 cve_2026_22557_check.py https://192.168.1.200:8443 --brief [NOT VULNERABLE] https://192.168.1.200:8443 site=default ### Options --site NAME UniFi site slug to probe (default: "default") --max-depth N maximum ../ traversal depth to try (default: 8) --no-tls-recon skip TLS certificate subject/SAN display --no-color disable ANSI color output (also honored: NO_COLOR env var) --timeout N per-request timeout in seconds (default: 20) --brief print a single-line result, no banner or steps Exit codes: `0` VULNERABLE, `1` partially exposed / not vulnerable / not exposed, `2` transport error. ## Preconditions The tool can only return a `VULNERABLE` verdict if the following conditions are met: - The target is running a vulnerable UniFi Network Application build (10.1.85 or earlier on the 10.1.x branch, or the equivalent earlier point releases on 10.2.x / 9.0.x). - The guest portal is reachable at the supplied URL. The `/guest/*` handler is dispatched on the admin port (8443) as well as the dedicated guest-portal ports (8843, 8880); the tool only probes the URL it is given. - The queried site has `portal_customized: true` in its `guest_access` setting, which is the precondition for the disk-read code path. A controller with multiple sites is exploitable if *any* of its sites has this enabled. If any condition is not met, the tool falls through to a more conservative verdict. A `PARTIALLY EXPOSED` result still means the application is vulnerable and must be patched; the queried site simply happens not to expose the disk-read path. ## Remediation 1. **Upgrade** to UniFi Network Application 10.1.89, 10.2.97, 9.0.118, or later. 2. **If you cannot patch immediately**, restrict network access to ports 8443 / 8843 / 8880 (and any reverse proxy fronting them) so they are reachable only from trusted management networks. 3. **After patching, rotate any credentials that may have been disclosed** while the controller was reachable: administrator passwords, the SSH credential the controller pushes to managed devices, the controller's TLS keystore, RADIUS shared secrets, VPN / WireGuard keys, SSL-inspection CA material, SMTP relay passwords, and any payment-gateway keys. ## Detection in your logs The vulnerable request pattern is: GET /guest/s//wechat/sign?page_error= Legitimate clients reach `/guest/s//wechat/sign` only via `POST` (which the dispatcher accepts without raising the failure-page renderer). Any `GET` of this endpoint carrying a `page_error=` query parameter, particularly one containing `../`, `%2e%2e`, or a leading `/`, is anomalous and should be treated as an exploitation attempt. Tomcat's standard access log captures the full request line including the query string. ## License This code is distributed under an [MIT license](LICENSE). ## Legal Disclaimer Usage of this tool for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state, and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program. ## See Also