
Rileva in modo sicuro se un server UniFi OS è vulnerabile a CVE-2026-34908
Un rilevatore sicuro per la catena di esecuzione remota di codice non autenticata in UniFi OS Server ≤ 5.0.6, divulgato in Ubiquiti Security Advisory Bulletin 064:
| CVE | Classe | Ruolo nella catena |
|---|---|---|
| CVE-2026-34908 / CVE-2026-34909 | Controllo di accesso improprio / path traversal | Bypass del gateway di autenticazione (il punto d'appoggio) |
| CVE-2026-34910 | Validazione di input impropria → command injection | RCE non autenticata raggiunta tramite il bypass |
Risolto in UniFi OS Server 5.0.8.
Sì. Il rilevatore è progettato per uso in produzione e assessment:
GET.VULNERABLE richiede sia che l'auth-bypass abbia
raggiunto il gestore vulnerabile, sia che una richiesta di baseline (stesso endpoint, senza bypass) sia
stata correttamente rifiutata con 401.UniFi OS mette i propri servizi dietro nginx, che applica l'autenticazione tramite una subrequest. Quel
controllo di autenticazione tratta come pubblica qualsiasi richiesta il cui URI grezzo inizi con
/api/auth/validate-sso/, ma nginx instrada al backend usando l'URI normalizzato (decodifica
percentuale %2f→/ e comprime ../). Una richiesta come:
GET /api/auth/validate-sso/..%2f..%2f..%2fproxy/users/api/v2/ucs/update/latest_package
viene quindi trattata come pubblica (prefisso grezzo) ma instradata all'endpoint interno autenticato
di aggiornamento pacchetti (path normalizzato). Senza pkg_name, quell'endpoint risponde
query param pkg_name required — dimostrando che il bypass funziona e che il gestore vulnerabile è
raggiungibile.
| Risposta del probe | Verdetto |
|---|---|
200 + marcatori di errore del gestore | VULNERABLE |
400 (nginx rifiuta la divergenza) | PATCHED (5.0.8+) |
401, impronta UniFi OS presente | INCONCLUSIVE (autenticazione applicata / bypass bloccato) |
401, nessuna impronta UniFi OS | UNAFFECTED (non è un server UniFi OS) |
| qualsiasi altra risposta, nessuna impronta UniFi OS | UNAFFECTED (non è un server UniFi OS) |
| qualsiasi altra risposta, impronta UniFi OS presente | INCONCLUSIVE (risposta inaspettata) |
Il rilevatore invia anche una richiesta di baseline allo stesso endpoint senza il bypass e
richiede che restituisca 401 prima di dichiarare un host vulnerabile. Il controllo testa il
comportamento vulnerabile direttamente — un verdetto VULNERABLE significa che il bypass ha
effettivamente raggiunto il sink.
Ogni volta che il probe non conferma positivamente VULNERABLE o PATCHED, il rilevatore recupera
la pagina principale e cerca l'impronta del portale di gestione UniFi OS (
<title>UniFi OS</title>, window.UNIFI_OS_MANIFEST, id="portal-root",
id="site-manager_portal-container"). Questo vale anche per un 401: un host UniFi OS che
applica l'autenticazione sul bypass è INCONCLUSIVE, ma un proxy non UniFi che semplicemente
restituisce 401 per tutto non porta impronta e viene segnalato UNAFFECTED. Quindi ogni
risultato "non vulnerabile" è basato sull'impronta: marcatori presenti → INCONCLUSIVE; marcatori
assenti → UNAFFECTED (la CVE non si applica).
# singolo host (porta predefinita 11443)
./cve_2026_34908_check.py 192.168.1.10
# forma esplicita porta / URL
./cve_2026_34908_check.py host-a:11443 https://host-b
# scansione di un elenco, un target per riga (commenti '#' consentiti), output compatto
./cve_2026_34908_check.py -f targets.txt --brief
# output leggibile da macchina per pipeline
./cve_2026_34908_check.py -f targets.txt --json > results.json
| Flag | Descrizione |
|---|---|
targets | Uno o più host, host:port o https://host:port |
-f, --targets-file FILE | Legge i target da un file (uno per riga; commenti #) |
--brief | Singola riga allineata per target — ideale per scansionare molti host |
--json | Emette risultati JSON strutturati |
--timeout SECS | Timeout per richiesta (predefinito: 10) |
--no-color | Disabilita output colorato (rispetta anche NO_COLOR e non-TTY) |
Console vulnerabile (verbose, predefinito). La seconda riga è il dettaglio del verdetto;
il marcatore [!] e VULNERABLE vengono renderizzati in rosso su un TTY:
$ ./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
Console patchata (5.0.8+):
$ ./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
Un host UniFi OS confermato che il probe non è riuscito a classificare → INCONCLUSIVE. La riga di dettaglio
distingue i due casi (autenticazione applicata vs. risposta inaspettata):
$ ./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
Un host che non è affatto UniFi OS → UNAFFECTED (l'advisory non si applica):
$ ./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
Scansione di un elenco, una riga allineata per host (--brief). Il codice di uscita è 1 se qualsiasi host
è VULNERABLE, altrimenti 0 — utile negli script:
$ ./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
Output leggibile da macchina per pipeline (--json). Ogni risultato porta il
verdict più il bypass.state/bypass.detail sottostante; i risultati ERROR portano un
campo error invece di bypass:
$ ./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"
}
]