Laboratorio Docker autocontenido que demuestra la vulnerabilidad de traversal de directorios CVE-2007-4559 (TarSlip) a través del módulo tarfile de Python. Incluye APIs vulnerables y corregidas, generación de payloads y un script de demostración interactivo para educación en seguridad.
Solo con fines educativos. Este laboratorio explota intencionalmente vulnerabilidades del sistema de archivos dentro de contenedores Docker aislados. No lo ejecute en ningún sistema con datos sensibles o en entornos de producción.
Un laboratorio Docker autocontenido que demuestra la CVE-2007-4559 — la infame vulnerabilidad "TarSlip" en el módulo tarfile de Python — a través de una cadena de ataque concreta de principio a fin:
extractall() escribe ciegamente una entrada tar llamada ../../../etc/passwd fuera del directorio de extracción, sobrescribiendo el archivo real del sistema./admin protegido.tarfile.extractall() de Python reproduce fielmente cada entrada en un archivo tar, incluyendo aquellas cuyos nombres contienen secuencias de recorrido de directorio (../). Nunca fue diseñado para ser un límite de seguridad.
nombre de entrada tar : ../../../etc/passwd
directorio de extracción : /shared/uploads/a1b2c3d4/
ruta resuelta : /shared/uploads/a1b2c3d4/../../../etc/passwd
= /etc/passwd ← archivo del sistema sobrescrito
| Línea de tiempo | |
|---|---|
| 2007 | Error reportado al equipo de seguridad de Python |
| 2007 – 2022 | Marcado como "no es un problema de seguridad" — tarfile "funciona como se espera" |
| 2022 | Investigadores de Trellix escanean GitHub y encuentran más de 350,000 repos que llaman a extractall() con entradas no confiables |
| 2022 | Divulgación pública. La CVE-2007-4559 resurge. Caos en toda la industria. |
| 2023 | PEP 706 incluye filter='data' en Python 3.12 — la corrección es un solo argumento |
La demostración se ejecuta en tres tiempos, cada uno requiere una pulsación de tecla para avanzar.
GET /admin → 401. El endpoint admin existe y está protegido. El atacante no conoce la contraseña.innocent.tar.gz → los archivos caen dentro del directorio sandbox. Todo parece normal.Atacante crea tarslip_passwd.tar.gz
└─ entrada: "../../../etc/passwd"
contenido: admin:hacked:1001:... ← contraseña plantada
│
▼
POST /upload (carga de archivo multipart)
│
▼
extractall("/shared/uploads/{uuid}/")
resuelve "../../../etc/passwd" → /etc/passwd ← CVE-2007-4559
│
▼
GET /admin Authorization: Basic admin:hacked
│
▼
HTTP 200 — "¡Bienvenido, admin! Tienes acceso completo de administrador."
flag: CVE-2007-4559{tarslip_passwd_overwrite_to_admin_rce}
Un solo HTTP POST. Sin shell. Sin payload RCE. Solo un archivo tar.
El mismo tarball se sube a la API corregida, que pasa filter='data' a extractall(). Python lanza tarfile.OutsideDestinationError — el recorrido se bloquea, /etc/passwd no se toca, y /admin permanece bloqueado.
# Vulnerable — predeterminado antes de Python 3.14
tar.extractall(extraction_dir)
# Corregido — PEP 706 (Python 3.12+)
tar.extractall(extraction_dir, filter='data')
Un argumento. Quince años para implementarse.
Cuatro servicios en una red bridge Docker aislada (tarslip-net). Nada llega a internet.
┌─────────────────────────────────────────────────────────┐
│ tarslip-net (bridge) │
│ │
│ ┌─────────────────┐ ┌──────────────────────────┐ │
│ │ vulnerable-api │ │ file-server │ │
│ │ python:3.11.3 │ │ nginx:alpine │ │
│ │ puerto 8000 │ │ puerto 8080 (host) │ │
│ │ │ │ │ │
│ │ POST /upload │ │ Sirve /shared a través │ │
│ │ GET /admin │ │ de HTTP — navegar │ │
│ │ GET /health │ │ visualmente extracciones│ │
│ └────────┬────────┘ └────────────┬─────────────┘ │
│ │ volumen de almacenamiento compartido │
│ └────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────┐ │
│ │ attacker │ │
│ │ python:3.12 │ (sin puerto host — solo interno) │
│ │ │ │
│ │ craft_malicious.py — genera tarballs │
│ │ demo.py — impulsa la demostración │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────┘
| Servicio | Imagen | Rol | Puerto host |
|---|---|---|---|
vulnerable-api | python:3.11.3-slim | API Flask de carga + /admin protegido por autenticación /etc/passwd | 8000 |
fixed-api | python:3.12-slim | Mismo código + USE_SAFE_EXTRACTION=true | 8000 |
file-server | nginx:alpine | Listado de directorios de archivos extraídos | 8080 |
attacker | python:3.12-slim | Generador de payload + controlador de demostración | — |
Las API vulnerable y corregida usan código fuente idéntico. La única diferencia es la variable de entorno USE_SAFE_EXTRACTION=true en el contenedor corregido, que activa el único argumento filter='data'.
CVE-2007-4559-lab/
├── run_demo.sh ← empezar aquí
├── docker-compose.vulnerable.yml
├── docker-compose.fixed.yml
├── vulnerable-api/
│ ├── app.py # API Flask: /upload + /admin + /health
│ ├── Dockerfile # siembra admin:s3cr3t_Adm1nPass en /etc/passwd
│ └── requirements.txt
├── file-server/
│ ├── Dockerfile
│ └── nginx.conf
└── attacker/
├── craft_malicious.py # genera innocent.tar.gz + tarslip_passwd.tar.gz
├── demo.py # controlador CLI de cuatro modos (craft/baseline/exploit/verify)
├── Dockerfile
└── requirements.txt
docker compose version)bashgit clone https://github.com/your-username/CVE-2007-4559-lab.git
cd CVE-2007-4559-lab
bash run_demo.sh
El script es completamente interactivo. Imprime una narrativa antes de cada paso y espera Enter para avanzar. No se necesita conocimiento previo de Docker para seguirlo.
| Pausa | Narrativa mostrada | Acción al presionar Enter |
|---|---|---|
| 1 | Línea de tiempo de la CVE, qué son los tres tiempos | Construir stack vulnerable |
| 2 | Roles de los contenedores, contraseña admin sembrada | Generar payloads |
| 3 | Qué hay dentro de cada tarball | Tiempo 1 — base |
| 4 | Por qué /admin es 401, cómo se ve la extracción normal | Tiempo 2 — exploit |
| 5 | El cálculo exacto del recorrido, qué se sobrescribe | Cambiar a stack corregido |
| 6 | Qué hace filter='data' y por qué funciona | Tiempo 3 — verificar |
| 7 | Conclusiones clave + patrón ZipSlip más amplio | Desmontaje |
Si desea avanzar manualmente:
# Stack vulnerable
docker compose -f docker-compose.vulnerable.yml up --build -d
docker compose -f docker-compose.vulnerable.yml exec attacker python craft_malicious.py
docker compose -f docker-compose.vulnerable.yml exec attacker python demo.py baseline
docker compose -f docker-compose.vulnerable.yml exec attacker python demo.py exploit