
针对 CVE-2026-25895 FUXA 未认证路径遍历 -> 任意文件写入 -> 远程代码执行 (RCE) 的 POC 利用
未认证、预认证的任意文件写入漏洞,影响基于 Node.js 的 SCADA/HMI 平台 FUXA。通过多种不同的写入后原语实现远程代码执行。即使启用了 secureEnabled = true(开启认证)也能利用,因为存在漏洞的端点没有挂载中间件。
也可在 ExploitDB 上获取:https://www.exploit-db.com/exploits/52568
| 字段 | 值 |
|---|---|
| CVE ID | CVE-2026-25895 |
| 受影响版本 | FUXA <= 1.2.9 |
| 修复版本 | FUXA 1.2.10 |
| 厂商 | frangoteam / FUXA |
| 攻击向量 | 网络(HTTP/HTTPS) |
| 认证要求 | 无 |
| 影响 | 任意文件写入、远程代码执行 |
| CVSS v3.1 | 9.8(严重)— AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| 研究人员 | Anthony Cihan (Hann1bl3L3ct3r) |
FUXA 的 POST /api/upload 端点(server/api/projects/index.js:193)未注册中间件,绕过了应用于其他所有项目管理端点的 secureFnc JWT/API 密钥检查和管理员权限门。在处理程序中,JSON 主体字段 destination 被拼接成文件系统路径,仅添加了一个下划线前缀,没有进行规范化或包含性检查:
let destinationDir = path.resolve(runtime.settings.appDir, `_${destination}`);
filePath = path.join(destinationDir, fullPath || fileName);
fs.writeFileSync(filePath, basedata, encoding);
destination 值为 a/../../../../../etc(其中 a 吸收了前导下划线前缀)的形式使得 Node 的 path.resolve 能够脱离 appDir 到达 FUXA 进程可写的任何位置。由于 fs.writeFileSync 之前有带条件 fs.mkdirSync(dir, { recursive: true }) 调用,攻击者还可以根据需要创建父目录。
结果是一个未认证的任意文件写入原语,可通过默认 HTTP 端口(1881)访问,无论 FUXA 管理员是否已启用登录,都可预认证利用。
未认证的远程攻击者可以:
settings.js 以在 FUXA 下次重启时实现代码执行/etc/cron.d/<file>),当 FUXA 以 root 身份运行时(厂商 Docker 镜像中的默认设置),在 60 秒内实现代码执行/root/.ssh/authorized_keys 或任何用户的 ~/.ssh/authorized_keys这是一个针对用于操作工业流程的 ICS/SCADA 平台的预认证严重级别漏洞。
| 版本 | 状态 |
|---|---|
<= 1.2.9 | 存在漏洞 |
1.2.10+ | 已修复 |
已确认在 Ubuntu Server 上全新安装的 FUXA 1.2.9 上可被利用。
单文件 Python 3 脚本,一个第三方依赖(requests)。
pip install requests
python3 fuxapwn.py --help
识别运行中的操作系统用户、Node-RED 暴露情况以及主机上的其他账户,无需写入任何异常内容:
python3 fuxapwn.py -u http://target:1881 --mode recon \
--probe-root --probe-home
写入一个中性的 /tmp/healthcheck 标记(文件名和内容中无 CVE 特定的 IOC):
python3 fuxapwn.py -u http://target:1881 --mode canary
如果 FUXA 以 root 身份运行,投放一个在 60 秒内触发且无需重启 FUXA 的 cron 文件:
python3 fuxapwn.py -u http://target:1881 --mode cron \
--cron-cmd 'id > /tmp/fx.txt 2>&1'
settings.js 替换实现持久 webshell在 FUXA Node 进程中安装一个 HTTP webshell 监听器(在下次冷启动时激活,因为 require() 会缓存模块),然后在 FUXA 重启后进入交互式 REPL:
# 部署 payload — 替换 settings.js 但保留目标的实际配置(uiPort, allowedOrigins, secureEnabled 等),使应用程序继续正常提供服务。
python3 fuxapwn.py -u http://target:1881 --mode webshell \
--appdata /opt/FUXA/server/_appdata --ws-port 31337
# 一旦 FUXA 重启,连接到已安装的 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
完整按模式标志参考:python3 fuxapwn.py --help。
GET /api/settings(server/api/index.js:103)未注册中间件,返回实时运行配置,仅做轻微脱敏(服务器在发送前删除 secretCode 和 smtp.password)。其余字段泄露绝对路径(appDir, workDir, userSettingsFile, logsDir, uploadFileDir),通常能识别服务用户,另外还有 nodeRedEnabled,这是另一个未认证 RCE 路径的直接指示(见下文)。
当 FUXA 通过 npm start 在本地用户账户下启动,并且安装路径不包含用户信息(例如安装在 /opt、/tmp 或通用 /app 下),且 /root/ 不可写时,POC 会回退到遍历 /home/<candidate>/ 并通过零字节写入进行探测。
FUXA 的上传处理程序在 fs.writeFileSync 之前有条件地调用 fs.mkdirSync(parent, { recursive: true }),这会产生 EACCES 歧义:不存在的 /home/<user>/ 会在 mkdir 尝试时失败并返回 EACCES(进程无法在 root 拥有的 /home/ 下创建目录),而现有的 /home/<other>/ 如果权限为 0700,则会在写入本身时失败并返回 EACCES。相同的 errno,不同的含义。POC 通过解析服务器转发错误信息中的系统调用标记(libuv 格式 "<CODE>: <reason>, <syscall> '<path>'")来消除歧义,仅将 EACCES 发生在 open 时(或任何非 mkdir 的系统调用)报告为“其他用户存在”。这消除了 naive errno 探测可能产生的误报横向移动列表。
如果泄露设置中 nodeRedEnabled = true,则 FUXA 嵌入式 Node-RED 的管理端点(/nodered/flows/deploy 等)可被未认证访问,因为 Referer 头白名单检查(node-red/index.js:134-136)会接受任何 Referer 包含 /editor、/viewer 或 /lab 的请求。这样就通过函数节点实现了即时未认证 RCE,无需重启。recon 模式会标记此条件;操作者应在可用时优先使用。
settings.js payload 保留目标配置生成 settings.js 替换文件时(模式 settings-rce 和 webshell),POC 首先通过 /api/settings 获取实时配置,并将其 JSON 序列化作为 module.exports 主体(JSON 是有效的 JavaScript 对象字面量子集)。这样保留了目标的 uiPort、allowedOrigins、secureEnabled 和其他运行时设置,使应用程序在替换后继续正常提供服务。当目标启用了 secureEnabled = true 或包含 smtp 块时,POC 会明确发出警告,因为服务器端脱敏会删除 secretCode(JWT 回退将启用)和 smtp.password(邮件将中断,直到手动恢复)。
POST /api/upload,特别是响应中包含 .. 或绝对文件系统路径的 destination 值GET /api/settings(通常仅由已认证的 UI 使用)/tmp/healthcheck*、/tmp/.fuxa-probe-* 或 /home/*/.fuxa-probe-* 的文件(默认的 canary 和探测标记文件名 — POC 允许覆盖这些以避免明显的 IOC,因此缺失并不排除被利用)settings.js、/etc/cron.d/* 或 ~/.ssh/authorized_keys 的修改时间戳与管理操作不对应/api/upload 端点受标准中间件链保护。/api/upload 和 /api/settings 的未认证请求。/root/、无法写入 /etc/cron.d/、无法通过 cron 实现主机级持久化)。nodeRedEnabled = false)。1.2.10server/api/projects/index.js:193 in FUXA 1.2.9研究、POC 及撰写:Anthony Cihan (Hann1bl3L3ct3r),Offensive Security 负责人。
本仓库包含针对 ICS/SCADA 产品中严重漏洞的功能性利用代码。根据负责任的披露原则,在厂商发布补丁后发布,旨在惠及防御者(检测编写人员、事件响应人员)和授权安全测试人员。
仅限针对您拥有或您获得明确书面授权进行测试的系统使用。 未经授权使用这些代码攻击第三方系统在大多数司法管辖区是非法的,并且作者将予以追究。作者不对滥用行为承担任何责任。
如果您是 FUXA 操作者,并希望在受控条件下通过此 PoC 验证您的补丁级别,请联系作者。
为授权安全测试和防御性研究目的发布。完整条款见 LICENSE。
| 模式 | 用途 |
|---|
recon | 通过 GET /api/settings 进行未认证信息泄露;从绝对路径推断运行中的 OS 用户;报告 Node-RED 状态;可选的 --probe-root 和 --probe-home 主动探测 |
canary | 使用中性默认路径证明文件写入原语 |
settings-rce | 用 payload 替换 settings.js,在下次 FUXA 冷启动时执行可配置的命令 |
ssh-key | 将公钥写入目标用户的 authorized_keys |
drop | 将任意文件投放至任何绝对路径 |
cron | 将文件投放至 /etc/cron.d/<name>,在 60 秒内实现 RCE,无需等待 FUXA 重启(要求 FUXA 以 root 身份运行) |
webshell | 通过替换 settings.js 在 FUXA Node 进程中安装 HTTP webshell 监听器(在下次冷启动时激活) |
webshell-exec | 已安装 webshell 的客户端;单条命令或 --interact 交互式 REPL |