
سلسلة RCE مستقلة عن ASLR لـ nginx PoC تجمع بين تسريب القراءة الزائدة من كومة PoolSlip (CVE-2026-9256) مع تجاوز rift (CVE-2026-42945) للوصول إلى system() على nginx الأصلي.
أول سلسلة عامة تجمع بين ثغرتين مُعلنتين حديثًا في محرك إعادة الكتابة (rewrite engine) في nginx لتحويلهما إلى تنفيذ system() عن بُعد مستقل عن ASLR على صورة Docker الرسمية nginx:1.30.0 الأصلية
(Debian 13، glibc 2.41) — بدون عناوين مُثبّتة مسبقًا، وبدون إعادة تشغيل nginx، ونسبة نجاح ~90% لكل عامل (worker) جديد.
$args → تسريب libc + heap الحيّين.is_args (بايتات آمنة لعناوين URL فقط) → كتابة جزئية بمقدار بايتين
على مؤشر تنظيف limit_conn → يقوم ngx_destroy_pool باتباعه → system(cmd).كلاهما ناتج عن نفس عدم التطابق ثنائي المرور (two-pass mismatch) في is_args مُوجَّه نحو مصرفين (sinks) مختلفين. الشرح التقني الكامل: Writeup
الإفصاح والسلامة
- كلا الثغرتين مُرقَّعتان بالفعل وتحت استغلال نشط في البرية. قم بالترقية إلى nginx 1.30.2 (stable) أو 1.31.1 (mainline) — لاحظ أن
1.30.1يُصلح rift لكنه لا يزال عرضة لـ PoolSlip، لذا فإن1.30.2فقط يُغلق كليهما. NGINX Plus: R36 P5 / R32 P7 / R37.0.1.1.- هذا منشور للتعليم والاختبار المُصرَّح به فقط. يعمل ضد مختبر Docker المحلي المُضمَّن — لا توجّهه نحو بنية تحتية لا تملكها أو لا تملك إذنًا كتابيًا لاختبارها.
| CVE | الاسم المستعار | الإصدارات المصابة | الإصدار المُصلَح (nginx OSS) |
|---|---|---|---|
| CVE-2026-42945 | rift | 0.6.27 – 1.30.0 | 1.30.1 / 1.31.0 |
| CVE-2026-9256 | PoolSlip | 0.1.17 – 1.30.1 + 1.31.0 | 1.30.2 / 1.31.1 |
| الملف | الغرض |
|---|---|
nginx.conf | إعداد بوابة API واقعي (الهدف — limit_conn، إعادة كتابة ترحيل من v1 إلى v2، وكيل رفع (upload proxy)، صفحة بحث متدهورة). بدون ضبط المُخصِّص (allocator tuning). |
run.sh | يُشغّل المختبر المحلي: صورة nginx:1.30.0 الأصلية تخدم nginx.conf، بالإضافة إلى خادم أعلى (upstream) بطيء، على المضيف :19322. |
exp_official.py | السلسلة الكاملة: تسريب PoolSlip → اشتقاق libc/heap → رشّ (spray) → تهيئة (groom) → كتابة جزئية عبر rift → system(). |
exp_official.py)، curl، Linux x86-64.run.sh أداة perl (الموجودة أصلًا في صورة nginx الأصلية) للخادم الأعلى البطيء، لذا لا توجد حزمة إضافية، ولا ملف slow_backend، ولا حاجة للإنترنت. يبقى ملف nginx التنفيذي نفسه أصليًا.WRITEUP.md، استخدم docker exec ثم apt-get install gdb file binutils، ثم حمّل gef.git clone https://github.com/y198nt/Nginx-chain-Rift-Poolslip
cd Nginx-chain-Rift-Poolslip
bash run.sh # stock nginx:1.30.0 on http://127.0.0.1:19322
python3 exp_official.py --cmd 'id > /tmp/rce_proof' # leak → overwrite → system()
docker exec nginx-rift-official cat /tmp/rce_proof # → uid=101(nginx) ...
exp_official.py هو محاولة واحدة (~90% لكل عامل جديد): يُسرّب مرة واحدة ويُطلق مرة واحدة. إذا كان
ملف الإثبات فارغًا، فقد هبطت التهيئة في خانة خاطئة — فقط أعد تشغيله.
docker rm -f nginx-rift-official # tear the lab down
rewrite ^/search/((.*))$ /lookup?$1$2 مرور النسخ يضبط
r->args.len متجاوزًا المخزن المؤقت؛ فتعكس صفحة /search المتدهورة قيمة $args إلى الكومة المجاورة.
بعد فترة تسخين قصيرة، يستقر مؤشر libpcre-cluster (ثابت 0xb0ad08 أسفل libc) ومؤشر كومة
عند إزاحات مستقرة → libc_base وheap_base، دون أي شيء مُثبّت مسبقًا.limit_conn صالح بالفعل — فتمرّ البايتات العليا العشوائية بفعل ASLR دون مساس →
مستقل عن ASLR.ngx_pool_cleanup_t{handler=&system, data=cmd, next=0} المرشوش؛ فإغلاق الضحية يُفعّل مرور التنظيف في ngx_destroy_pool →
system(cmd).راجع Writeup للاشتقاق الكامل، وجلسات gdb، والطرق المسدودة والتحولات.
rewrite تحتوي بديلها على ? و تشير إلى التقاط PCRE
($1/$2)، أو نمط التقاط متداخل مثل ^/x/((.*))$ → /y?$1$2. دقّق في الإعدادات بحثًا عن
هذه وأعد صياغة rewrite/set المُخالِف حتى الترقيع.$args (أو أي متغير مشتق من الطلب يُعاد إرساله) هو ما يحوّل القراءة الزائدة
إلى تسريب معلومات — لا تعكس وسائط الطلب حرفيًا.%2B/+ في مسار URI (GET /search/+++…)، واستجابات 503 من صفحة متدهورة
تعكس $args طويلًا، وعواصف SIGSEGV/إعادة إنشاء العمال.