
مختبر Docker مستقل يوضح ثغرة CVE-2007-4559 (TarSlip) الخاصة بتجاوز المسار عبر وحدة tarfile في بايثون. يتضمن واجهات برمجة تطبيقات ضعيفة ومُصلحة، وتوليد الحمولات، وسكربت عرض تفاعلي لأغراض التعليم الأمني.
للاستخدام التعليمي فقط. يستغل هذا المختبر عن قصد ثغرات نظام الملفات داخل حاويات Docker معزولة. لا تشغّله على أي نظام يحتوي بيانات حساسة أو في بيئات الإنتاج.
مختبر Docker مستقل بذاته يوضح CVE-2007-4559 — ثغرة «TarSlip» سيئة السمعة في وحدة tarfile الخاصة بلغة Python — عبر سلسلة هجوم ملموسة من البداية إلى النهاية:
extractall() بشكل أعمى إدخال tar باسم ../../../etc/passwd خارج مجلد الاستخراج، فتحل محل ملف النظام الحقيقي./admin المحمية.تعيد tarfile.extractall() في Python إنتاج كل إدخال في أرشيف tar بأمانة، بما في ذلك الإدخالات التي تحتوي أسماؤها على تسلسلات اجتياز المسار ../. لم تُصمم أبدًا لتكون حاجزًا أمنيًا.
tar entry name : ../../../etc/passwd
extraction dir : /shared/uploads/a1b2c3d4/
resolved path : /shared/uploads/a1b2c3d4/../../../etc/passwd
= /etc/passwd ← system file overwritten
| الجدول الزمني | |
|---|---|
| 2007 | الإبلاغ عن الخلل لفريق أمان Python |
| 2007 – 2022 | تصنيفه «ليست مشكلة أمنية» — tarfile «تعمل كما هو مقصود» |
| 2022 | باحثو Trellix يفحصون GitHub ويعثرون على أكثر من 350,000 مستودع يستدعي extractall() على مدخلات غير موثوقة |
| 2022 | كشف علني. عادت CVE-2007-4559 إلى الواجهة. حالة هرج على مستوى الصناعة. |
| 2023 | PEP 706 يصدر filter='data' في Python 3.12 — الإصلاح هو وسيط واحد |
يدور العرض في ثلاث مراحل، تتطلب كل مرحلة ضغطة مفتاح للمتابعة.
GET /admin → 401. نقطة نهاية المسؤول موجودة ومحمية. لا يعرف المهاجم كلمة المرور.innocent.tar.gz → الملفات تصل إلى داخل مجلد العزل. كل شيء يبدو طبيعيًا.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}
طلب HTTP POST واحد. لا قشرة. لا حمولة RCE. مجرد ملف tar.
يُرفع الأرشيف نفسه إلى الواجهة البرمجية المصلّحة، التي تمرر filter='data' إلى extractall(). تثير Python استثناء tarfile.OutsideDestinationError — يُحجَب الاجتياز، ويبقى /etc/passwd دون مساس، ويظل /admin مقفلًا.
# Vulnerable — default before Python 3.14
tar.extractall(extraction_dir)
# Fixed — PEP 706 (Python 3.12+)
tar.extractall(extraction_dir, filter='data')
وسيط واحد. خمسة عشر عامًا حتى صدر.
أربع خدمات على شبكة جسر Docker معزولة (tarslip-net). لا يصل أي شيء إلى الإنترنت.
┌─────────────────────────────────────────────────────────┐
│ 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 │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────┘
| الخدمة | الصورة | الدور | منفذ المضيف |
|---|---|---|---|
vulnerable-api | python:3.11.3-slim | واجهة رفع Flask + /admin محمية بمصادقة /etc/passwd | 8000 |
fixed-api | python:3.12-slim | الكود نفسه + USE_SAFE_EXTRACTION=true | 8000 |
file-server | nginx:alpine | عرض قائمة مجلدات الملفات المستخرجة | 8080 |
attacker | python:3.12-slim | مولّد الحمولات + مُشغّل العرض | — |
تستخدم الواجهتان البرمجيتان الضعيفة والمصلّحة كودًا مصدريًا متطابقًا. الفرق الوحيد هو متغير البيئة USE_SAFE_EXTRACTION=true على الحاوية المصلّحة، وهو ما يقلب وسيط 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
السكربت تفاعلي بالكامل. يعرض سردًا قبل كل خطوة وينتظر الضغط على Enter للمتابعة. لا حاجة إلى معرفة سابقة بـ Docker لمتابعة العرض.
| نقطة التوقف | السرد المعروض | الإجراء عند الضغط على Enter |
|---|---|---|
| 1 | الجدول الزمني لـ CVE، وماهية المراحل الثلاث | بناء البيئة الضعيفة |
| 2 | أدوار الحاويات، كلمة مرور المسؤول المزروعة | توليد الحمولات |
| 3 | ما بداخل كل أرشيف tar | المرحلة 1 — خط الأساس |
| 4 | لماذا يُرجع /admin 401، وكيف يبدو الاستخراج الطبيعي | المرحلة 2 — الاستغلال |
| 5 | رياضيات الاجتياز بالضبط، وما الذي يُستبدل | التبديل إلى البيئة المصلّحة |
| 6 | ماذا يفعل filter='data' ولماذا يعمل | المرحلة 3 — التحقق |
| 7 | الخلاصات الرئيسية + نمط ZipSlip الأوسع | إيقاف البيئة |
إذا أردت التنقل خطوة بخطوة يدويًا:
# Vulnerable 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
# Fixed 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
# Teardown
docker compose -f docker-compose.fixed.yml down
بينما تعمل البيئة الضعيفة، افتح http://localhost:8080/uploads/ لتصفح مجلدات الجلسات المستخرجة في متصفحك.
/adminتزرع الواجهة البرمجية كلمة مرور سرية للمسؤول داخل /etc/passwd وقت بناء الصورة:
admin:s3cr3t_Adm1nPass:1001:1001:Administrator:/home/admin:/bin/bash