
للاستخدام التعليمي فقط. وُجد هذا المختبر لإظهار ثغرة حقيقية في بيئة آمنة ومعزولة. لا تشغّل هذا أبدًا ضد أنظمة لا تملكها. لا تعِد استخدام كود الفلتر المكسور عمدًا في أي نظام إنتاجي.
مختبر Docker مكتفٍ بذاته يوضح CVE-2023-24329 — تباينًا في المحلِّل داخل urllib.parse.urlparse() في بايثون، يتيح تجاوز فلاتر مخطط URL واسم المضيف على بايثون < 3.11.4.
يُظهر المختبر واجهة برمجة تطبيقات تمنع صراحةً عناوين file:// وأسماء المضيفات الداخلية، وهي تُخدَع لقراءة /etc/passwd من حاويتها الخاصة وللوصول إلى خدمة داخلية خاصة — ثم يُثبت أن الاستغلال نفسه يفشل على بايثون المُصحَّح.
لا يتفق urlparse() في بايثون مع جالِبات HTTP/الملفات الأساسية حول كيفية التعامل مع عناوين URL التي تبدأ بمسافة بيضاء. على الإصدارات المتأثرة:
from urllib.parse import urlparse
urlparse(" file:///etc/passwd").scheme # → "" (empty — filter passes)
urlparse(" file:///etc/passwd").hostname # → None (empty — filter passes)
لكن urllib.request.urlopen(" file:///etc/passwd") يزيل المسافة ويجلب file:///etc/passwd على أي حال.
تلك الفجوة بين ما يراه المحلِّل وما يفعله الجالِب — هي الثغرة.
أصلح بايثون 3.11.4 هذا الأمر بإزالة المسافات البيضاء/أحرف التحكم البادئة قبل التحليل، فسدَّ الفجوة.
أربع خدمات على شبكة Docker bridge معزولة (cve-lab-net):
لا يملك internal-service أي تعيين لمنفذ مضيف — لا يمكن الوصول إليه إلا من داخل شبكة Docker، محاكيًا حدود ثقة حقيقية.
git clone <repo-url>
cd CVE-2023-24329-lab
docker compose -f docker-compose.vulnerable.yml up --build -d
docker compose -f docker-compose.vulnerable.yml exec attacker python exploit.py baseline

docker compose -f docker-compose.vulnerable.yml exec attacker python exploit.py exploit

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 exploit.py verify

docker compose -f docker-compose.fixed.yml down
CVE-2023-24329-lab/
├── docker-compose.vulnerable.yml # Python 3.11.3 (affected)
├── docker-compose.fixed.yml # Python 3.11.4 (patched)
├── vulnerable-api/
│ ├── app.py # Flask API with the naive filter
│ ├── requirements.txt
│ └── Dockerfile
├── internal-service/
│ ├── app.py # Fake internal metadata endpoint
│ ├── requirements.txt
│ └── Dockerfile
└── attacker/
├── exploit.py # Demo driver (baseline / exploit / verify)
├── requirements.txt
└── Dockerfile
تتشارك خدمتا API الضعيفة والمصحَّحة في نفس الكود المصدري — الفرق الوحيد هو إصدار بايثون في الصورة الأساسية. هذه هي خاصية الضبط العلمي الجوهرية في المختبر.
فلتر API الضعيف (مبسَّط):
parsed = urllib.parse.urlparse(url)
if parsed.scheme.lower() in {"file", "gopher", "ftp", "data"}:
return 403 # blocked
if parsed.hostname in {"localhost", "127.0.0.1", "internal-service"}:
return 403 # blocked
urllib.request.urlopen(url) # fetch the original, unmodified string
حمولة التجاوز هي مسافة بادئة واحدة:
file:///etc/passwd
^
space (0x20)
على بايثون ≤ 3.11.3، يرى urlparse مخططًا فارغًا ولا اسم مضيف → يمر الفلتر. يزيل urlopen المسافة → يجلب file:///etc/passwd.
على بايثون ≥ 3.11.4، يزيل urlparse المسافة أولًا → يرى scheme=file بشكل صحيح → يمنع الفلتر برمز 403.
عالجتها CPython في المسألة #102153 — يزيل الإصلاح أحرف التحكم C0 والمسافات من بداية عنوان URL قبل التحليل. بعد التصحيح، يتفق كلٌّ من المحلِّل والجالِب على هوية عنوان URL، فلا يمكن تجاوز الفلتر بهذه الطريقة.
النمط الدفاعي الصحيح بغض النظر عن إصدار بايثون:
# Parse → reconstruct from parts → pass the rebuilt URL downstream.
# Both the filter and the fetcher then operate on the same string.
parsed = urllib.parse.urlparse(url)
safe_url = parsed.geturl() # rebuilt from components
urllib.request.urlopen(safe_url)
${jndi:...} في log4j.internal-service إلى المضيف.vulnerable-api/app.py مكسور عمدًا لأغراض تعليمية — لا تنسخه في أي نظام حقيقي.MIT — مجاني للاستخدام والمشاركة والتعديل للأغراض التعليمية مع ذكر المصدر.
| المرحلة | ما تراه | ما تعلّمه |
|---|
| 1 — خط الأساس | file:///etc/passwd → 403 blocked scheme | يبدو الفلتر معقولًا |
| 2 — الاستغلال | نفس العنوان مع مسافة بادئة → 200 + محتويات /etc/passwd والسر الداخلي | مسافة واحدة تُسقط الفلتر بأكمله |
| 3 — التصحيح | نفس الحمولة ضد بايثون 3.11.4 → 403 blocked | urlparse المُصحَّح يزيل المسافات أولًا، ومن ثم يكتشفه الفلتر بشكل صحيح |
| الخدمة | إصدار بايثون | الدور | منفذ المضيف |
|---|
vulnerable-api | 3.11.3 | واجهة برمجة تطبيقات الهدف مع فلتر عناوين URL ساذج | 8000 |
fixed-api | 3.11.4 | نفس الكود، بمفسِّر مُصحَّح | 8000 |
internal-service | 3.12 | نقطة بيانات وصفية داخلية وهمية | لا يوجد |
attacker | 3.12 | مُشغِّل الاستغلال | لا يوجد |