## https://sploitus.com/exploit?id=11E8CD3B-3967-5005-9757-30C2B9C0E571
# UniFi OS Server Unauth RCE Chain Detection Script
A safe detector for the unauthenticated remote code execution chain in UniFi OS Server โค 5.0.6, disclosed in Ubiquiti
[Security Advisory Bulletin 064](https://community.ui.com/releases/Security-Advisory-Bulletin-064-064/84811c09-4cf4-42ab-bd61-cc994445963b):
| CVE | Class | Role in the chain |
|-----|-------|-------------------|
| CVE-2026-34908 / CVE-2026-34909 | Improper access control / path traversal | Auth-gateway bypass (the foothold) |
| CVE-2026-34910 | Improper input validation โ command injection | Unauthenticated RCE reached via the bypass |
Fixed in UniFi OS Server 5.0.8.
## Is it Safe to Run?
Yes. The detector is designed for production and assessment use:
- **No command is executed.** The probe reaches the vulnerable endpoint without a required
parameter, so the handler rejects the missing input and we confirm the bug from that
validation error.
- **No target state is changed.** Every request is a plain `GET`.
- **False-positive guard.** A `VULNERABLE` verdict requires both that the auth-bypass reached
the vulnerable handler and that a baseline request (same endpoint, no bypass) was correctly
rejected with `401`.
## How it Works
UniFi OS fronts its services with nginx, which enforces authentication via a subrequest. That
auth check treats any request whose **raw** URI begins with `/api/auth/validate-sso/` as public,
but nginx routes to the backend using the **normalized** URI (it percent-decodes `%2f`โ`/` and
collapses `../`). A request like:
```text
GET /api/auth/validate-sso/..%2f..%2f..%2fproxy/users/api/v2/ucs/update/latest_package
```
is therefore treated as public (raw prefix) yet routed to the authenticated internal
package-update endpoint (normalized path). With no `pkg_name`, that endpoint replies
`query param pkg_name required` โ proving the bypass works and the vulnerable handler is
reachable.
| Probe response | Verdict |
|----------------|---------|
| `200` + handler error markers | `VULNERABLE` |
| `400` (nginx rejects the divergence) | `PATCHED` (5.0.8+) |
| `401`, UniFi OS fingerprint present | `INCONCLUSIVE` (auth enforced / bypass blocked) |
| `401`, no UniFi OS fingerprint | `UNAFFECTED` (not a UniFi OS Server) |
| anything else, no UniFi OS fingerprint | `UNAFFECTED` (not a UniFi OS Server) |
| anything else, UniFi OS fingerprint present | `INCONCLUSIVE` (unexpected response) |
The detector also sends a baseline request to the same endpoint without the bypass and
requires it to return `401` before declaring a host vulnerable. The check tests the
vulnerable behavior directly โ a `VULNERABLE` verdict means the bypass actually reached the sink.
### Confirming the target is UniFi OS before any not-vulnerable verdict
Whenever the probe does not positively confirm `VULNERABLE` or `PATCHED`, the detector fetches
the root page and looks for the UniFi OS management-portal fingerprint (
`UniFi OS`, `window.UNIFI_OS_MANIFEST`,`id="portal-root"`,
`id="site-manager_portal-container"`). This applies to a `401` too: a UniFi OS host that
enforces auth on the bypass is `INCONCLUSIVE`, but a non-UniFi proxy that simply `401`s
everything carries no fingerprint and is reported `UNAFFECTED`. So every "not vulnerable"
outcome is grounded in the fingerprint: markers present โ `INCONCLUSIVE`; markers
absent โ `UNAFFECTED` (the CVE does not apply).
## Requirements
- Python 3.7+, standard library only โ no third-party packages.
## Usage
```bash
# single host (default port 11443)
./cve_2026_34908_check.py 192.168.1.10
# explicit port / URL forms
./cve_2026_34908_check.py host-a:11443 https://host-b
# scan a list, one target per line ('#' comments allowed), compact output
./cve_2026_34908_check.py -f targets.txt --brief
# machine-readable output for pipelines
./cve_2026_34908_check.py -f targets.txt --json > results.json
```
### Options
| Flag | Description |
|------|-------------|
| `targets` | One or more `host`, `host:port`, or `https://host:port` |
| `-f, --targets-file FILE` | Read targets from a file (one per line; `#` comments) |
| `--brief` | Single aligned line per target โ ideal for scanning many hosts |
| `--json` | Emit structured JSON results |
| `--timeout SECS` | Per-request timeout (default: 10) |
| `--no-color` | Disable coloured output (also honours `NO_COLOR` and non-TTY) |
### Examples
**A vulnerable console** (verbose, the default). The second line is the verdict's
detail; the `[!]` marker and `VULNERABLE` render red on a TTY:
```console
$ ./cve_2026_34908_check.py 192.168.1.10
[!] 192.168.1.10:11443: VULNERABLE
auth bypass reached the vulnerable handler (no command executed); baseline correctly 401
```
**A patched console** (5.0.8+):
```console
$ ./cve_2026_34908_check.py 192.168.1.11
[+] 192.168.1.11:11443: PATCHED
nginx rejected the normalized-path divergence (HTTP 400) โ 5.0.8+ behavior
```
**A confirmed UniFi OS host the probe couldn't classify** โ `INCONCLUSIVE`. The detail
line distinguishes the two cases (auth enforced vs. unexpected response):
```console
$ ./cve_2026_34908_check.py 192.168.1.12 192.168.1.13
[?] 192.168.1.12:11443: INCONCLUSIVE
bypass blocked / auth enforced (HTTP 401) on a confirmed UniFi OS host โ not confirmed vulnerable
[?] 192.168.1.13:11443: INCONCLUSIVE
UniFi OS detected, but probe returned an unexpected response: HTTP 302 text/html
```
**A host that isn't UniFi OS at all** โ `UNAFFECTED` (the advisory does not apply):
```console
$ ./cve_2026_34908_check.py 203.0.113.9:443
[-] 203.0.113.9:443: UNAFFECTED
no UniFi OS web fingerprint on / (probe HTTP 404) โ target is not a UniFi OS Server
```
**Scan a list, one aligned line per host** (`--brief`). Exit status is `1` if any host
is `VULNERABLE`, else `0` โ handy in scripts:
```console
$ ./cve_2026_34908_check.py -f targets.txt --brief; echo "exit: $?"
VULNERABLE 192.168.1.10:11443
PATCHED 192.168.1.11:11443
INCONCLUSIVE 192.168.1.12:11443
UNAFFECTED 203.0.113.9:443
ERROR 10.0.0.1:11443 timeout
exit: 1
```
**Machine-readable output for pipelines** (`--json`). Each result carries the
`verdict` plus the `bypass.state`/`bypass.detail` behind it; `ERROR` results carry an
`error` field instead of `bypass`:
```console
$ ./cve_2026_34908_check.py 192.168.1.10 192.168.1.11 10.0.0.1 --json
[
{
"target": "192.168.1.10:11443",
"bypass": {
"state": "vulnerable",
"detail": "auth bypass reached the vulnerable handler (no command executed); baseline correctly 401"
},
"verdict": "VULNERABLE"
},
{
"target": "192.168.1.11:11443",
"bypass": {
"state": "patched",
"detail": "nginx rejected the normalized-path divergence (HTTP 400) โ 5.0.8+ behavior"
},
"verdict": "PATCHED"
},
{
"target": "10.0.0.1:11443",
"verdict": "ERROR",
"error": "timeout"
}
]
```
## Verdicts
| Verdict | Meaning |
|---------|---------|
| `VULNERABLE` | Auth bypass reached the vulnerable handler unauthenticated. Patch to 5.0.8+. |
| `PATCHED` | nginx rejected the normalized-path divergence (5.0.8+ behavior). |
| `UNAFFECTED` | No UniFi OS portal fingerprint on the root page โ the target is not a UniFi OS Server, so the advisory does not apply. Includes hosts that return `401` without the fingerprint (e.g. an unrelated proxy that rejects everything). |
| `INCONCLUSIVE` | Confirmed UniFi OS host that couldn't be classified โ verify the version manually. The verdict's detail line says which case: (a) the bypass was blocked / auth enforced (`401`), or (b) the probe returned an unexpected response. |
| `ERROR` | Connection/timeout/TLS failure. |
## Exit codes
| Code | Meaning |
|------|---------|
| `0` | No target returned `VULNERABLE` |
| `1` | At least one target is `VULNERABLE` |
| `2` | Usage error (bad arguments / unreadable targets file) |
## Limitations
- `INCONCLUSIVE` is not a guarantee of safety โ it confirms a UniFi OS host but couldn't classify
it (auth enforced on the bypass, or an unexpected response). A host that is heavily filtered or
fronted by an unexpected proxy may not be classifiable from the network alone. Confirm the
running version where possible.
- `UNAFFECTED` means the root page carried no UniFi OS portal fingerprint (including hosts that
`401` without it). A UniFi OS console hidden behind a reverse proxy that strips or rewrites the
portal HTML could therefore be misreported; confirm directly if the target is expected to be
UniFi OS.
- The detector reports reachability of the vulnerable behavior.
## Remediation
Update UniFi OS Server to version 5.0.8 or later. The fix adds an nginx raw-vs-normalized
URI comparison (closing the auth bypass) and input validation in the package-update path
(closing the command injection).
## 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
- [Ubiquiti Security Advisory Bulletin 064](https://community.ui.com/releases/Security-Advisory-Bulletin-064-064/84811c09-4cf4-42ab-bd61-cc994445963b)
- [Bishop Fox Blog: Full Technical Analysis](https://bishopfox.com/blog/popping-root-on-unifi-os-server-unauthenticated-rce-chain-detection-analysis)