
Laboratoire Docker autonome démontrant CVE-2007-4559 (TarSlip) une traversée de répertoire via le module tarfile de Python. Inclut des API vulnérables et corrigées, la génération de payload, et un script de démonstration interactif pour l'éducation en sécurité.
Usage éducatif uniquement. Ce laboratoire exploite intentionnellement des vulnérabilités du système de fichiers dans des conteneurs Docker isolés. Ne pas exécuter sur un système contenant des données sensibles ou dans des environnements de production.
Un laboratoire Docker autonome qui démontre CVE-2007-4559 — la fameuse vulnérabilité « TarSlip » dans le module tarfile de Python — à travers une chaîne d'attaque concrète de bout en bout :
extractall() écrit aveuglément une entrée tar nommée ../../../etc/passwd en dehors du répertoire d'extraction, écrasant le véritable fichier système./admin protégé.La méthode tarfile.extractall() de Python reproduit fidèlement chaque entrée d'une archive tar, y compris celles dont les noms contiennent des séquences de traversée de chemin ../. Elle n'a jamais été conçue pour être une barrière de sécurité.
tar entry name : ../../../etc/passwd
extraction dir : /shared/uploads/a1b2c3d4/
resolved path : /shared/uploads/a1b2c3d4/../../../etc/passwd
= /etc/passwd ← system file overwritten
| Chronologie | |
|---|---|
| 2007 | Bug signalé à l'équipe de sécurité Python |
| 2007 – 2022 | Marqué comme « non critique pour la sécurité » — tarfile fonctionne comme prévu |
| 2022 | Les chercheurs de Trellix scannent GitHub et trouvent plus de 350 000 dépôts qui appellent extractall() sur des entrées non fiables |
| 2022 | Divulgation publique. CVE-2007-4559 refait surface. Agitation à l'échelle de l'industrie. |
| 2023 | La PEP 706 livre filter='data' dans Python 3.12 — le correctif est un seul argument |
La démo se déroule en trois temps, chacun nécessitant une pression de touche pour avancer.
GET /admin → 401. Le point de terminaison admin existe et est protégé. L'attaquant ne connaît pas le mot de passe.innocent.tar.gz → les fichiers atterrissent dans le répertoire sandbox. Tout semble normal.Attacker crafts tarslip_passwd.tar.gz
└─ entry: "../../../etc/passwd"
content: admin:hacked:1001:... ← planted password
│
▼
POST /upload (multipart file upload)
│
▼
extractall("/shared/uploads/{uuid}/")
resolves "../../../etc/passwd" → /etc/passwd ← CVE-2007-4559
│
▼
GET /admin Authorization: Basic admin:hacked
│
▼
HTTP 200 — "Welcome, admin! You have full admin access."
flag: CVE-2007-4559{tarslip_passwd_overwrite_to_admin_rce}
Un seul POST HTTP. Pas de shell. Pas de payload RCE. Juste un fichier tar.
La même archive tar est téléversée vers l'API corrigée, qui passe filter='data' à extractall(). Python lève une exception tarfile.OutsideDestinationError — la traversée est bloquée, /etc/passwd reste intact, et /admin reste verrouillé.
# Vulnerable — default before Python 3.14
tar.extractall(extraction_dir)
# Fixed — PEP 706 (Python 3.12+)
tar.extractall(extraction_dir, filter='data')
Un seul argument. Quinze ans pour arriver.
Quatre services sur un réseau pont Docker isolé (tarslip-net). Rien n'atteint internet.
┌─────────────────────────────────────────────────────────┐
│ tarslip-net (bridge) │
│ │
│ ┌─────────────────┐ ┌──────────────────────────┐ │
│ │ vulnerable-api │ │ file-server │ │
│ │ python:3.11.3 │ │ nginx:alpine │ │
│ │ port 8000 │ │ port 8080 (host) │ │
│ │ │ │ │ │
│ │ POST /upload │ │ Serves /shared over │ │
│ │ GET /admin │ │ HTTP — browse extracts │ │
│ │ GET /health │ │ visually │ │
│ └────────┬────────┘ └────────────┬─────────────┘ │
│ │ shared-storage volume │ │
│ └────────────────────────────┘ │
│ │
│ ┌─────────────────┐ │
│ │ attacker │ │
│ │ python:3.12 │ (no host port — internal only) │
│ │ │ │
│ │ craft_malicious.py — generates tarballs │
│ │ demo.py — drives the demo │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────┘
| Service | Image | Rôle | Port hôte |
|---|---|---|---|
vulnerable-api | python:3.11.3-slim | API de téléversement Flask + /admin protégé par authentification via /etc/passwd | 8000 |
fixed-api | python:3.12-slim | Même code + USE_SAFE_EXTRACTION=true | 8000 |
file-server | nginx:alpine | Liste des répertoires de fichiers extraits | 8080 |
attacker | python:3.12-slim | Générateur de payload + pilote de démo | — |
Les API vulnérable et corrigée utilisent un code source identique. La seule différence est la variable d'environnement USE_SAFE_EXTRACTION=true sur le conteneur corrigé, qui active le seul argument filter='data'.
CVE-2007-4559-lab/
├── run_demo.sh ← start here
├── docker-compose.vulnerable.yml
├── docker-compose.fixed.yml
├── vulnerable-api/
│ ├── app.py # Flask API: /upload + /admin + /health
│ ├── Dockerfile # seeds admin:s3cr3t_Adm1nPass into /etc/passwd
│ └── requirements.txt
├── file-server/
│ ├── Dockerfile
│ └── nginx.conf
└── attacker/
├── craft_malicious.py # generates innocent.tar.gz + tarslip_passwd.tar.gz
├── demo.py # four-mode CLI driver (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
Le script est entièrement interactif. Il affiche un récit avant chaque étape et attend Entrée pour continuer. Aucune connaissance préalable de Docker n'est nécessaire pour suivre.
| Pause | Récit affiché | Action sur Entrée |
|---|---|---|
| 1 | Chronologie de CVE, ce que sont les trois temps | Construire la pile vulnérable |
| 2 | Rôles des conteneurs, mot de passe admin initial | Générer les payloads |
| 3 | Ce qu'il y a dans chaque archive tar | Temps 1 — référence |
| 4 | Pourquoi /admin est 401, à quoi ressemble une extraction normale | Temps 2 — exploitation |
| 5 | Le calcul exact de la traversée, ce qui est écrasé | Passer à la pile corrigée |
| 6 | Ce que fait filter='data' et pourquoi ça fonctionne | Temps 3 — vérification |
| 7 | Points clés à retenir + schéma plus large de ZipSlip | Démantèlement |
Si vous voulez avancer manuellement :