مستودع أبحاث كامل لثغرة CVE-2026-42945 مع تحليل تجاوز سعة المخزن المؤقت في الكومة (heap buffer overflow)، واستغلال RCE (heap spray + Feng Shui)، وسكربتات كشف، وإرشادات تصحيح لثغرة وحدة إعادة الكتابة (rewrite module) في NGINX.
تجاوز سعة المخزن المؤقت في الكومة (Heap Buffer Overflow) في وحدة ngx_http_rewrite_module الخاصة بـ NGINX
| المقياس | القيمة |
|---|---|
| CVSS v4.0 | 9.2 (حرج) |
| CVSS v3.1 | 8.1 (عالي) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CWE | 122 — تجاوز سعة المخزن المؤقت في الكومة |
| التاريخ المقدَّم | يونيو 2008 — v0.6.27 |
| الاكتشاف | أبريل 2026 — أبحاث DepthFirst |
| الإصلاح | 13 مايو 2026 — v1.30.1, v1.31.0 |
| نشر CVE | 21 مايو 2026 |
| العمر | ~18 عامًا (غير مكتشف) |
| التزام الإصلاح | 524977e7c534e87e5b55739fa74601c9f1102686 |
يمكن لمهاجم عن بُعد وغير مصادَق أن يسبب تجاوز سعة مخزن مؤقت حتميًا في الكومة في عمليات عمل NGINX عن طريق إرسال طلب HTTP مصمم بعناية إلى خادم يستخدم نمط تكوين محدد يجمع بين rewrite وset/if/rewrite. يؤدي هذا التجاوز إلى إفساد بيانات تعريف الكومة (مؤشرات ngx_pool_cleanup_t)، مما يتيح تنفيذ التعليمات البرمجية عن بُعد (RCE) عبر تقنيات حقن الكومة (heap spray) وتعديل تخطيط الكومة (Feng Shui).
server { listen 19321;
location ~ ^/api/(.*)$ {
rewrite ^/api/(.*)$ /internal?migrated=true;
set $original_endpoint $1;
}
}
**Key requirements:**
- توجيه `rewrite` يحتوي بديله على `?` (فاصل سلسلة الاستعلام)
- توجيه `set` أو `if` أو `rewrite` لاحق يشير إلى **التقاط PCRE غير مُسمّى** (`$1`، `$2`، إلخ.)
- علامة `?` في بديل `rewrite` تؤدي إلى تشغيل `ngx_http_script_start_args_code` الذي يضبط `e->is_args = 1`
### ما يمكن أن يحققه المهاجم
| Capability | Description |
|-----------|-------------|
| **رفض الخدمة** | تعطل عمليات العامل بشكل حتمي، مما يسبب حلقات إعادة تشغيل (تعمل بغض النظر عن ASLR) |
| **تنفيذ التعليمات البرمجية عن بُعد** | مع تعطيل ASLR (أو تجاوزه عبر الكتابة الجزئية الفوقية)، يمكن تحقيق RCE كاملة كمستخدم nginx |
| **سرقة البيانات** | عبر بدائيات قراءة الذاكرة، يمكن استخراج بيانات حساسة من كومة العامل |
| **الثبات** | زرع أبواب خلفية عبر تنفيذ التعليمات البرمجية في ذاكرة عملية العامل |
---
## 2. تحليل السبب الجذري
### محرك البرمجة النصية ثنائي المرور
تستخدم وحدة `ngx_http_rewrite_module` في NGINX **محرك برمجة نصية ثنائي المرور** في `src/http/ngx_http_script.c`:
1. **مرحلة الطول** (`ngx_http_script_run`): يتكرر عبر جميع أكواد البرمجة النصية لحساب الحجم الإجمالي المطلوب للمخزن المؤقت. يكتب الأطوال إلى `le.ip` و `le.pos`.
2. **مرحلة النسخ** (`ngx_http_script_copy_len`/`_code`): يتكرر مرة أخرى، ويكتب البايتات الفعلية في المخزن المؤقت المخصص مسبقًا عند `e->ip` و `e->pos`.
كل كود برمجي له معالِجان: واحد لكل مرحلة. على سبيل المثال:
- `ngx_http_script_copy_len` → `ngx_http_script_copy_code`
- `ngx_http_script_start_args_len` → `ngx_http_script_start_args_code`
### علامة `is_args`
تتحكم العلامة `e->is_args` في **بنية المحرك** (`ngx_http_script_engine_t`) في كيفية تعامل مرحلة النسخ مع أحرف معينة:```c
typedef struct {
u_char *ip;
u_char *pos;
ngx_http_variable_value_t *sp;
ngx_str_t buf;
int flushed;
unsigned is_args:1; // <-- THE BUG
unsigned ncaptures:1;
ngx_uint_t captures_size;
// ...
} ngx_http_script_engine_t;
عندما تكون e->is_args = 1، يستدعي كود النسخ لمراجع الالتقاط $N الدالة ngx_escape_uri() مع NGX_ESCAPE_ARGS، والتي تقوم بتوسيع:
+ → %2B (1 بايت → 3 بايتات، +200%)% → %25 (1 بايت → 3 بايتات، +200%)& → %26 (1 بايت → 3 بايتات، +200%)مسار التنفيذ للنمط القابل للاستغلال:``` rewrite ^/api/(.*)$ /internal?migrated=true;
1. أثناء **تقييم إعادة الكتابة**، يصادف المحرك `?` في سلسلة الاستبدال، مما يؤدي إلى تشغيل `ngx_http_script_start_args_code`، وضبط `e->is_args = 1`.
2. تقوم إعادة الكتابة بتعديل URI الخاص بالطلب ثم تتابع إلى التوجيه التالي.
3. **لا يتم مسح `e->is_args` أبدًا**.
ثم:```
set $original_endpoint $1;
le) لتمرير الطول: ```c
ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
هذا يصفّر le.is_args = 0 بشكل صحيح، لذا فإن تمريرة الطول تُرجع طول الالتقاط الخام غير المُرمَّز.
e، الذي ما يزال يحتوي على e->is_args = 1 من الخطوة 1. تطبّق تمريرة النسخ ترميز URI، مما يوسّع كل حرف قابل للترميز من 1 بايت إلى 3 بايتات داخل مخزن مؤقت تم تحديد حجمه ليتسع للطول الخام — تجاوز سعة الكومة.Pass 1 (Length — sub-engine le): le.is_args = 0 capture $1 = "A+++++B" → length = 7
Buffer allocated: 7 bytes
Pass 2 (Copy — main engine e): e.is_args = 1 ← LEAKED from rewrite capture $1 = "A+++++B" ngx_escape_uri("A+++++B", NGX_ESCAPE_ARGS): A → A (1 byte) + → %2B (3 bytes) ← EXPANSION + → %2B (3 bytes) + → %2B (3 bytes) + → %2B (3 bytes) + → %2B (3 bytes) B → B (1 byte) total written: 17 bytes buffer size: 7 bytes OVERFLOW: 10 bytes
نسبة التوسع هي `7 + (n_escapable * 2)` حيث `n_escapable` هو عدد `+` و`%` و`&` في الالتقاط.
---
## 3. آليات الاستغلال
### نظرة عامة
| الخطوة | التقنية | الوصف |
|------|-----------|-------------|
| 1 | تجاوز السعة | أرسل URI مصنوعًا بحشو `+` لتجاوز سعة مخزن الكومة المؤقت |
| 2 | رش الكومة | أرسل طلبات POST بأجسام كبيرة إلى `/spray` لملء الكومة ببيانات مُتحكم بها |
| 3 | فنغ شوي | رتّب التخصيصات بحيث يكون هدف التجاوز (`ngx_pool_cleanup_t`) مجاورًا |
| 4 | إفساد المعالج | يستبدل التجاوز `ngx_pool_cleanup_t.handler` بعنوان `system()` |
| 5 | تفعيل التنظيف | انتظر تدمير التجمع → ينفّذ `system(cmd)` أمر المهاجم |
| 6 | الصدفة العكسية | سلسل إلى حمولة صدفة عكسية للوصول التفاعلي |
### فنغ شوي عبر الطلبات
**يفشل فنغ شوي أحادي الطلب** لأن التجاوز يُفسد البيانات الوصفية للتجمع (`->d.next`, `->d.failed`) قبل الوصول إلى مؤشر `cleanup`. عندما يُدمَّر التجمع في نهاية الطلب، تتسبب البيانات الوصفية المُفسدة في **تعطل قبل استدعاء `system()`**.
بدلاً من ذلك، يستخدم الاستغلال **فنغ شوي عبر الطلبات**:
1. **الطلب 1 (رش)**: أرسل جسمًا كبيرًا عبر POST إلى `/spray`. يُبقي الخادم الخلفي (`server.py`) الاستجابة مع ترويسة `X-Delay`، مما يُبقي الاتصال مفتوحًا ويُحافظ على تخصيص الكومة. يملأ الرشّ الكومة بكتل `ngx_pool_cleanup_t` مزيفة.
2. **الطلب 2 (تجاوز)**: أرسل URI الخاص بالتجاوز. يُفسد التجاوز مؤشر `cleanup` فقط (وليس البيانات الوصفية للتجمع)، موجّهًا إياه إلى الكتلة المزيفة المُرَشّة.
3. **تدمير التجمع**: عندما تكتمل استجابة الرشّ (تنتهي المهلة)، تنتقل سلسلة التنظيف الخاصة بالتجمع إلى الكتلة المزيفة وتستدعي `system(cmd)`.
### متطلبات العناوين
| الرمز | القيمة (Docker، ASLR معطّل) | الوصف |
|--------|--------------------------|-------------|
| `HEAP_BASE` | `0x555555659000` | قاعدة كومة nginx |
| `system@libc` | `0x7ffff6f6e420` | `system()` في glibc |
| `NGX_CYCLES_POOL` | `0x5555556a4040` | مؤشر إلى تجمع cycles |
| عنوان التنظيف المزيف | `0x5555556a4030` | عنوان هدف الرش |