
CVE-2026-42533: تجاوز في سعة الكومة وتسريب معلومات قبل التحقق من الهوية (pre-auth) في nginx ناتج عن إفساد التقاطات PCRE في محرك map/script، ويُستغل ضمن سلسلة هجوم لتنفيذ تعليمات برمجية عشوائية عن بُعد (RCE).
تجاوز سعة في الكومة وتسريب معلومات من الكومة قبل المصادقة في nginx، ناتج عن عدم حفظ/استعادة حالة التقاط PCRE بين تمريرتي محرك السكربت. متغير map يُقيَّم بتعبير نمطي بين مرجعَي التقاط يستبدل r->captures، لذا تختلف تمريرة LEN عن تمريرة VALUE في تحديد حجم الالتقاط. يُخصَّص المخزن المؤقت لالتقاط واحد ويُملأ بآخر. استبدال أكبر يعطي تجاوز سعة في الكومة بمحتوى وطول يتحكم بهما المهاجم. استبدال أصغر يعطي مخزنًا مؤقتًا كبيرًا جدًا يُعاد ذيله غير المهيّأ إلى العميل، مما يسرّب مؤشرات libc ومؤشرات الكومة.
تشكّل البدائيتان معًا سلسلة تؤدي إلى تنفيذ برمجي موثوق عن بُعد قبل المصادقة. التسريب يكسّر ASLR في طلب GET واحد، لذا لا يحتاج تجاوز السعة إلى تعطيل ASLR.
الشرح: https://cyberstan.co.uk/nginx-rce/ النشرة الأمنية: F5 K000162097 المُبلِّغ: Stan Shaw (cyberstan)
nginx 0.9.6 حتى 1.30.3 (المستقر) و1.31.2 (الرئيسي)، متأثرة منذ أن أضاف توجيه map دعم التعبيرات النمطية في عام 2011. يشمل ذلك وحدتي http وstream. حوالي 50 توجيهًا عبر 13 موقع استدعاء، بالإضافة إلى مسار ثانٍ عبر الالتقاطات المسمّاة (r->variables[]). تم الإصلاح في 1.30.4 و1.31.3.
CVE-2026-42533-PoC/
├── exploits/ exploit and validator scripts
│ ├── poc.py numbered-capture proxy_method chain (crash/leak/rce/rce-det)
│ ├── calibrate.py find PL_OFF / HEAP_PAGE_OFF for --rce-det on your build
│ ├── leak_multi.py info leak on the return and set sinks
│ ├── test_all_sites.py ASan validator, all 13 overflow sites
│ └── named_capture_poc.py named-capture r->variables[] variant
├── configs/ nginx configs the exploits run against
│ ├── nginx_poc.conf crash / leak / rce
│ └── nginx_det.conf deterministic rce-det
├── docs/
│ └── EXPLOITATION.md full exploitation writeup
└── README.md
نفّذ كل أمر أدناه من جذر المستودع، بحيث تُحَل مسارات exploits/ وconfigs/ وشجرة البناء ../nginx-1.30.1 كلها بشكل صحيح.
exploits/poc.py هو الاستغلال الرئيسي (التقاط مُرقّم، مصرف proxy_method). أوضاعه:
سكربتات مستقلة تغطي باقي سطح الثغرة:
الإعدادات موجودة في configs/: nginx_poc.conf (crash/leak/rce)، وnginx_det.conf (rce-det). الشرح الكامل في docs/EXPLOITATION.md.
Linux وgcc وpython3 وكود مصدر nginx 1.30.1. طُوّر واختُبر على Ubuntu 24.04.4 وglibc 2.39 وPCRE2 10.42 وpython 3.12 مع تفعيل ASLR بالكامل.
بناؤان: بناء نظيف للتسريب و RCE، بحيث تكون بقايا الكومة حقيقية. وبناء AddressSanitizer للانهيار ولمدققي المواقع، بحيث يُبلَّغ عن تجاوز السعة بحجم كتابة ومكدس استدعاء دقيقين.
tar xf nginx-1.30.1.tar.gz
cd nginx-1.30.1
# clean build -> objs.dbg/nginx (leak, rce)
./configure --with-pcre --with-http_ssl_module --with-debug --builddir=objs.dbg
make -j"$(nproc)"
# ASan build with all the modules the site validator needs -> objs/nginx
./configure --with-pcre --with-http_ssl_module --with-http_v2_module \
--with-stream --with-stream_ssl_preread_module --with-stream_ssl_module \
--with-debug \
--with-cc-opt='-g -O0 -fsanitize=address -fno-omit-frame-pointer' \
--with-ld-opt=-fsanitize=address --builddir=objs
make -j"$(nproc)"
exploits/poc.py يتواصل مع nginx يعمل على 127.0.0.1:8950. شغّله في طرفية باستخدام configs/nginx_poc.conf، ثم نفّذ الوضع الذي تريده في طرفية أخرى. السكربتات الثلاثة الأخرى تُشغّل وتوقف nginx الخاص بها، لذا تحتاج فقط إلى NGINX_BIN.
mkdir -p run/logs
../nginx-1.30.1/objs/nginx -p run -c "$PWD/configs/nginx_poc.conf" # ASan build, foreground
python3 exploits/poc.py --crash
المتوقع: تجاوز سعة كومة، WRITE of size 200 في ngx_http_script_copy_capture_code عند ngx_http_script.c:1404، يُستدعى من ngx_http_complex_value في ngx_http_proxy_create_request.
../nginx-1.30.1/objs.dbg/nginx -p run -c "$PWD/configs/nginx_poc.conf" # clean build
python3 exploits/poc.py --leak
المتوقع: جسم بحجم 8161 بايت مع كتابة 2 بايت والباقي بقايا كومة. مؤشر libc عند الإزاحة 0x08 ومؤشر كومة عند 0x10.
../nginx-1.30.1/objs.dbg/nginx -p run -c "$PWD/configs/nginx_poc.conf" # clean build
python3 exploits/poc.py --rce # writes /tmp/PWNED via system()
المتوقع: تسريب، حوالي 40 اتصال رش (spray)، مشغّل تجاوز سعة، ثم /tmp/PWNED يحتوي على مخرجات id. هذه محاولة واحدة تنجح حوالي ثلثي المرات على بناء التطوير؛ عند الفشل ينهار العامل وتعيد التشغيل. انظر قسم "الموثوقية" في docs/EXPLOITATION.md.
بإعداد مُتحكَّم فيه، تصبح الثغرة نفسها محاولة واحدة حتمية. يستعيد العنوان الأساسي المطلق للكومة من التسريب في سطر واحد (heap_base = (leaked_ptr & ~0xfff) - 0x22000)، ويضع عملية تنظيف وحيدة مزوّرة لتجمّع (pool) في اتصال محجوز على عنوان معروف، ثم يوجه عملية تنظيف تجمّع الضحية إليها بدلاً من جسم المشغّل العابر الذي يحرره nginx قبل الإغلاق.
mkdir -p run/logs
../nginx-1.30.1/objs.dbg/nginx -p run -c "$PWD/configs/nginx_det.conf" # controlled config
python3 exploits/poc.py --rce-det
nginx_det.conf هو إعداد مخبري (عامل واحد، مخازن مؤقتة ثابتة) يكون تخطيط الكومة فيه قابلًا لإعادة الإنتاج، وهذا ما يجعل الإزاحات في poc.py (PL_OFF، HEAP_PAGE_OFF) صحيحة. PL_OFF هي الإزاحة من أساس الكومة التي يقع عندها جسم تنظيف POST /b/ المحجوز؛ وتعتمد على تسلسل التخصيص الدقيق، لذا تتغير بين البناءات وإصدارات glibc والإعدادات. إذا أبلغ --rce-det عن Recalibrate، فاقرأ القيم الصحيحة من عامل حي باستخدام calibrate.py:
NGINX_BIN=../nginx-1.30.1/objs.dbg/nginx python3 exploits/calibrate.py
# prints e.g. set PL_OFF = 0x14426 , then edit it in exploits/poc.py
النتيجة غير الناجحة الوحيدة الأخرى هي سحب ASLR يضع 0x0a داخل عنوان التنظيف أو جسم تجاوز السعة، وهو ما لا يستطيع تعبير map النمطي حمله؛ الأداة تُبلّغ عن ذلك وتعيد المحاولة. النشر القياسي ليس بهذه الدرجة من القدرة على التنبؤ، لذا استخدم --rce هناك. انظر "بناء حتمي" في docs/EXPLOITATION.md.
NGINX_BIN=../nginx-1.30.1/objs/nginx python3 exploits/test_all_sites.py # ASan build
NGINX_BIN=../nginx-1.30.1/objs/nginx python3 exploits/test_all_sites.py 1 7 12 # a subset
NGINX_BIN=../nginx-1.30.1/objs/nginx python3 exploits/named_capture_poc.py # ASan build
NGINX_BIN=../nginx-1.30.1/objs.dbg/nginx python3 exploits/leak_multi.py # clean build
المتوقع: كلا return وset يسرّبان مؤشر libc ومؤشر كومة، وكلاهما مؤكد مقابل /proc/<pid>/maps للعامل.
أكثر شيئين يوقعان الناس في الخطأ: استخدام البناء الخاطئ (ASan مقابل النظيف)، وإزاحات --rce-det غير المطابقة لبيئتك. كلاهما مغطى أدناه.
--rce يستعيد أساس libc ومؤشر كومة في وقت التشغيل من التسريب، لذا لا يرمّز أي عنوان بشكل ثابت. لكنه يرمّز إزاحات خاصة بالبناء وlibc الذي طُوّر عليه:
LIBC_LEAK_OFFSET libc base to the leaked arena pointer
SYSTEM_OFFSET libc base to system()
BODY_DELTA_* leaked heap pointer to the overflow body buffer
POOL_OFF_FROM_BUF, D_LAST_OFF, D_END_OFF, LOG_OFF forged pool geometry
على توزيعة أو glibc أو بناء nginx مختلف، تحتاج هذه الإزاحات إلى إعادة معايرة. اقرأ إزاحة system() الحقيقية بالأمر readelf -sW /lib/x86_64-linux-gnu/libc.so.6 | grep '\bsystem\b'، واقرأ إزاحات التجمع من ngx_pool_t للهدف. وضعا --crash و --leak لا يحملان مثل هذه الإزاحات ويعملان على أي بناء من إصدار متأثر.
التسريب لا يحتاج إلى ضبط خاص. يعمل مع إعداد events {} قياسي، حيث يحدد worker_connections الافتراضي (512) حجم منطقة الذاكرة بحيث تقع قطعة الطلب المحررة في سلة (bin) من glibc لا تزال تحتفظ بمؤشرات منطقة الذاكرة والكومة التي يعيد استخدامها مخزن التسريب كبير الحجم. فقط worker_connections المنخفض بشكل غير عادي (أقل من ~256) يتجنبه؛ جميع قيم الإنتاج (512-1024) تسرّب.
ماسح إعدادات ثابت يكتشف النمط القابل للاستغلال دون استغلال أي شيء، موجود على: https://github.com/0xCyberstan/CVE-2026-42533-Config-Scanner.
يستهدف هذا الثغرة التي تم إصلاحها والإفصاح عنها علنًا. إنه موجود للمدافعين الذين يتحققون من التعرض ولإعادة إنتاج البحث. شغّله فقط ضد nginx الذي تملكه أو مخوَّل صراحةً لاختباره. قم بالترقية إلى 1.30.4 أو 1.31.3.
| الوضع | الغرض |
|---|
poc.py --crash | إطلاق تجاوز سعة الكومة؛ على بناء ASan يطبع حجم الكتابة ومكدس الاستدعاء في ngx_http_script_copy_capture_code. |
poc.py --leak | تسريب المعلومات: تفريغ مؤشرات libc والكومة من جسم الاستجابة كبير الحجم. |
poc.py --rce | تنفيذ برمجي كامل عن بُعد قبل المصادقة. محاولة واحدة عامة (configs/nginx_poc.conf)، تنجح بنسبة ~66% في كل محاولة، أعد التشغيل عند الفشل. |
poc.py --rce-det | تنفيذ برمجي كامل عن بُعد قبل المصادقة، حتمي ضد الإعداد المُتحكَّم فيه configs/nginx_det.conf. |
| السكربت | الغرض |
|---|
exploits/leak_multi.py | تسريب المعلومات عبر مُقيّمَين إضافيين (return، set)؛ كل مؤشر libc+heap مؤكد مقابل /proc/<pid>/maps. إعداد قياسي. |
exploits/test_all_sites.py | مدقّق AddressSanitizer يُطلق جميع مواقع الاستدعاء الـ13 لتجاوز السعة (http + stream). |
exploits/named_capture_poc.py | صيغة الالتقاط المسمّى (?P<name>...) عبر r->variables[] / copy_var_code، وهي سبب جذري ثانٍ. |
| العَرَض | السبب | ما يجب فعله |
|---|
--leak لا يُظهر مؤشرات، أو --rce / --rce-det لا تنجح أبدًا | أنت تستخدم بناء ASan؛ AddressSanitizer يسمم الذاكرة المحررة، لذا لا تحمل البقايا مؤشرات حقيقية | استخدم بناء objs.dbg النظيف لـ --leak و --rce و --rce-det و leak_multi.py. بناء ASan objs مخصص فقط لـ --crash و test_all_sites.py و named_capture_poc.py. |
--rce ينجح فقط في حوالي محاولتين من كل 3 | محاولة واحدة ضد إعداد عام؛ موقع جسم المشغّل المحرر يختلف | هذا متوقع. عند الفشل ينهار العامل ويعاد إنشاؤه، فقط أعد التشغيل. لمحاولة واحدة حتمية استخدم --rce-det. |
--rce-det يطبع No RCE. Recalibrate PL_OFF/HEAP_PAGE_OFF في كل مرة | PL_OFF خاص ببناءك وglibc وإعدادك وطول مسار البادئة -p لـ nginx. القيمة المرفقة مخصصة لبناء التطوير لهذا المستودع عند تشغيله بـ -p run. | نفّذ exploits/calibrate.py، والصق PL_OFF الذي يطبعه في exploits/poc.py، وابدأ nginx بنفس بادئة -p التي أعطيتها لـ calibrate.py (يستخدم README الأمر -p run). |
--rce-det يطبع أحيانًا 0x0a (regex-hostile) ... retry | سحب ASLR وضع بايت 0x0a (سطر جديد) في عنوان، وهو ما لا يمكن لتعبير map النمطي حمله | ليس فشلًا. أعد التشغيل؛ السحب التالي يتجنبه في أغلب الأحيان. |
nginx أو calibrate.py: bind() to 127.0.0.1:8950 failed (Address already in use) | عملية nginx سابقة لا تزال تحتجز المنفذ | نفّذ pkill -x nginx، انتظر ثانية، وأعد المحاولة. أبقِ nginx واحدًا فقط على المنفذ 8950. |
سكربت مستقل يطبع nginx not found | NGINX_BIN غير مضبوط أو يشير إلى البناء الخاطئ | اضبط NGINX_BIN (البناء النظيف لـ leak_multi.py، وبناء ASan للمدققات). |
No such file لإعداد أو سكربت أو ملف nginx الثنائي | أنت لست في جذر المستودع | انتقل إلى جذر المستودع أولاً؛ كل أمر يفترض ذلك (exploits/...، configs/...، ../nginx-1.30.1/...). |
| التسريب يعمل مع إعداد ولا يعمل مع آخر | انخفاض worker_connections عن ~256 يقلّص منطقة الذاكرة، لذا لا تحتفظ القطعة المحررة بمؤشرات | استخدم worker_connections عاديًا (512 إلى 1024). جميع قيم الإنتاج تسرّب. |