
Exploit POC per CVE-2026-25895 FUXA Path Traversal non autenticato -> Scrittura arbitraria di file -> RCE
Scrittura arbitraria di file non autenticata, pre-autenticazione, contro FUXA, una piattaforma SCADA/HMI basata su Node.js. Si collega all'esecuzione di codice remoto tramite diverse primitive post-scrittura distinte. Funziona anche quando secureEnabled = true (autenticazione attiva) perché l'endpoint vulnerabile non ha alcun middleware associato.
Disponibile anche su ExploitDB: https://www.exploit-db.com/exploits/52568
| Campo | Valore |
|---|---|
| ID CVE | CVE-2026-25895 |
| Versione affetta | FUXA <= 1.2.9 |
| Versione corretta | FUXA 1.2.10 |
| Fornitore | frangoteam / FUXA |
| Vettore d'attacco | Network (HTTP/HTTPS) |
| Autenticazione | Nessuna richiesta |
| Impatto | Scrittura arbitraria di file, esecuzione di codice remoto |
| CVSS v3.1 | 9.8 (Critico) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Ricercatore | Anthony Cihan (Hann1bl3L3ct3r) |
L'endpoint POST /api/upload di FUXA (server/api/projects/index.js:193) è registrato senza middleware, bypassando sia il controllo JWT / API-key di secureFnc sia il controllo dei permessi di amministrazione applicato a ogni altro endpoint di gestione dei progetti. All'interno del gestore, il campo destination del corpo JSON viene concatenato in un percorso del filesystem con un solo underscore iniziale e senza alcuna normalizzazione o verifica di contenimento:
let destinationDir = path.resolve(runtime.settings.appDir, `_${destination}`);
filePath = path.join(destinationDir, fullPath || fileName);
fs.writeFileSync(filePath, basedata, encoding);
Un valore di destination della forma a/../../../../../etc (dove a assorbe il prefisso di underscore iniziale) fa sì che path.resolve di Node esca da appDir verso qualsiasi posizione scrivibile dal processo FUXA. Poiché fs.writeFileSync è preceduto da un fs.mkdirSync(dir, { recursive: true }) condizionale, l'attaccante può anche creare le directory padre secondo necessità.
Il risultato è una primitiva di scrittura arbitraria di file non autenticata raggiungibile sulla porta HTTP predefinita (1881), sfruttabile in fase pre-autenticazione indipendentemente dal fatto che l'amministratore FUXA abbia abilitato l'accesso.
Un attaccante remoto non autenticato può:
settings.js per ottenere l'esecuzione di codice al successivo riavvio di FUXA/etc/cron.d/<file>) per l'esecuzione di codice entro 60 secondi quando FUXA viene eseguito come root (impostazione predefinita nell'immagine Docker del fornitore)/root/.ssh/authorized_keys o in ~/.ssh/authorized_keys di qualsiasi utenteQuesta è una vulnerabilità di gravità critica pre-autenticazione su una piattaforma ICS/SCADA utilizzata per operare processi industriali.
| Versione | Stato |
|---|---|
<= 1.2.9 | Vulnerabile |
1.2.10+ | Corretta |
Confermato sfruttabile su un'installazione pulita di FUXA 1.2.9 su Ubuntu Server.
Script Python 3 in un unico file, una dipendenza di terze parti (requests).
pip install requests
python3 fuxapwn.py --help
Identifica l'utente OS in esecuzione, l'esposizione di Node-RED e qualsiasi altro account sul sistema senza scrivere nulla di inusuale:
python3 fuxapwn.py -u http://target:1881 --mode recon \
--probe-root --probe-home
Scrive un indicatore neutrale /tmp/healthcheck (nessun IOC specifico della CVE nel nome file o contenuto):
python3 fuxapwn.py -u http://target:1881 --mode canary
Se FUXA viene eseguito come root, inserisce un file cron che si attiva entro 60 secondi senza necessità di riavvio di FUXA:
python3 fuxapwn.py -u http://target:1881 --mode cron \
--cron-cmd 'id > /tmp/fx.txt 2>&1'
settings.jsInstalla un listener webshell HTTP all'interno del processo Node di FUXA (si attiva al successivo avvio a freddo, poiché require() memorizza nella cache i moduli), quindi entra in una REPL interattiva dopo il riavvio di FUXA:
# Prepara il payload — sostituisce settings.js ma preserva la configurazione reale
# del target (uiPort, allowedOrigins, secureEnabled, ecc.) in modo che
# l'applicazione continui a funzionare normalmente.
python3 fuxapwn.py -u http://target:1881 --mode webshell \
--appdata /opt/FUXA/server/_appdata --ws-port 31337
# Dopo il riavvio di FUXA, connettersi alla webshell installata.
python3 fuxapwn.py -u http://target:1881 --mode webshell-exec \
--ws-host target --ws-port 31337 \
--ws-path /_abc123 --ws-token <printed-above> --interact
Riferimento completo per modalità e flag: python3 fuxapwn.py --help.
GET /api/settings (server/api/index.js:103) è registrato senza middleware e restituisce la configurazione runtime in tempo reale, leggermente oscurata (il server elimina secretCode e smtp.password prima dell'invio). I campi rimanenti rivelano percorsi assoluti (appDir, workDir, userSettingsFile, logsDir, uploadFileDir) che tipicamente identificano l'utente di servizio, oltre a nodeRedEnabled, che è un indicatore diretto di un percorso RCE secondario non autenticato (vedi sotto).
Quando FUXA viene avviato tramite npm start sotto un account utente locale e i percorsi di installazione non codificano l'utente (ad es. l'installazione si trova sotto /opt, /tmp o un /app generico), e /root/ non è scrivibile, il PoC ricade sull'iterazione di /home/<candidato>/ con scritture di zero byte.
Il gestore upload di FUXA chiama condizionalmente fs.mkdirSync(parent, { recursive: true }) prima di fs.writeFileSync, creando un'ambiguità EACCES: un /home/<utente>/ inesistente fallisce con EACCES sul tentativo di mkdir (il processo non può creare directory sotto /home/ di proprietà di root), mentre un /home/<altro>/ esistente con modalità 0700 fallisce con EACCES sulla scrittura stessa. Stesso errno, significato diverso. Il PoC disambigua analizzando il token di syscall dal err.message inoltrato dal server (formato libuv "<CODE>: <reason>, <syscall> '<path>'") e segnala solo EACCES su open (o qualsiasi syscall non mkdir) come "altro utente esistente". Ciò elimina la falsa lista di movimento laterale che una sonda ingenua basata solo su errno produrrebbe.
Se nodeRedEnabled = true nelle impostazioni trapelate, gli endpoint admin di Node-RED incorporato in FUXA (/nodered/flows/deploy, ecc.) sono raggiungibili senza autenticazione a causa di un controllo whitelist dell'header Referer (node-red/index.js:134-136) che accetta qualsiasi richiesta il cui Referer contenga /editor, /viewer o /lab. Ciò produce un RCE non autenticato immediato tramite un nodo funzione senza richiedere un riavvio. La modalità recon segnala questa condizione; gli operatori dovrebbero preferirla quando disponibile.
settings.js preservano la configurazione del targetQuando genera una sostituzione di settings.js (modalità settings-rce e webshell), il PoC prima recupera la configurazione live tramite /api/settings e la serializza in JSON come corpo di module.exports (JSON è un sottoinsieme letterale valido di oggetti JavaScript). Ciò preserva uiPort, allowedOrigins, secureEnabled e altre impostazioni runtime del target in modo che l'applicazione continui a funzionare normalmente dopo la sostituzione. Il PoC avverte esplicitamente quando il target ha secureEnabled = true o un blocco smtp, poiché l'oscuramento lato server rimuove secretCode (il fallback JWT scatterà) e smtp.password (la posta elettronica si romperà fino al ripristino manuale).
POST /api/upload da fonti non autenticate, in particolare con valori di destination contenenti .. o percorsi assoluti del filesystem nella rispostaGET /api/settings da fonti non autenticate (normalmente utilizzato solo dall'UI autenticata)/tmp/healthcheck*, /tmp/.fuxa-probe-* o /home/*/.fuxa-probe-* (nomi file predefiniti per canary e sonde — il PoC permette di sovrascriverli per evitare IOC evidenti, quindi la loro assenza non esclude lo sfruttamento)settings.js, /etc/cron.d/* o ~/.ssh/authorized_keys che non corrispondono a un'azione di amministrazione/api/upload è protetto dalla catena middleware standard nella release corretta./api/upload e /api/settings./root/, nessuna scrittura su /etc/cron.d/, nessuna persistenza a livello di sistema tramite cron).nodeRedEnabled = false) se non è attivamente utilizzato.1.2.10server/api/projects/index.js:193 in FUXA 1.2.9Ricerca, PoC e articolo a cura di Anthony Cihan (Hann1bl3L3ct3r), Responsabile della Sicurezza Offensiva.
Questo repository contiene codice exploit funzionante per una vulnerabilità critica in un prodotto ICS/SCADA. È pubblicato secondo i principi del responsible disclosure, dopo la correzione da parte del fornitore, a beneficio dei difensori (autori di rilevamento, responder agli incidenti) e dei tester di sicurezza autorizzati.
Utilizzare solo contro sistemi di propria proprietà o per i quali si dispone di autorizzazione esplicita e scritta per testarli. L'uso non autorizzato di questo codice contro sistemi di terze parti è illegale nella maggior parte delle giurisdizioni e sarà trattato come tale dall'autore. L'autore non si assume alcuna responsabilità per un uso improprio.
Se sei un operatore FUXA e desideri assistenza per convalidare il livello di patch rispetto a questo PoC in condizioni controllate, contatta l'autore.
Rilasciato per fini autorizzati di test di sicurezza e ricerca difensiva. Vedi LICENSE per i termini completi.
| Modalità | Scopo |
|---|
recon | Info leak non autenticato tramite GET /api/settings; deduce l'utente OS dai percorsi assoluti; riporta lo stato di Node-RED; sonde attive opzionali --probe-root e --probe-home |
canary | Prova della primitiva di scrittura file con un percorso predefinito neutrale |
settings-rce | Sostituisce settings.js con un payload che esegue un comando configurabile al successivo avvio a freddo di FUXA |
ssh-key | Scrive una chiave pubblica in authorized_keys di un utente target |
drop | Rilascio arbitrario di file in qualsiasi percorso assoluto |
cron | Rilascia /etc/cron.d/<nome> per RCE entro 60 secondi senza attendere un riavvio di FUXA (richiede FUXA eseguito come root) |
webshell | Installa un listener webshell HTTP all'interno del processo Node di FUXA tramite sostituzione di settings.js (si attiva al successivo avvio a freddo) |
webshell-exec | Client per una webshell già installata; comando singolo o REPL --interact |