
ngxray — ماسح أمان إعدادات nginx
ماسح ضوئي ثابت لثغرات إعدادات nginx. يحلل الإعدادات باستخدام المحلل الخاص بـ nginx نفسه ويطابقها مع قواعد JSON تعريفية لاكتشاف CVEs معروفة في أنماط توجيهات محرك إعادة الكتابة/السكريبت.
cd ngxray
git submodule update --init
make
python3 scan.py /etc/nginx/nginx.conf
تُعرَّف القواعد كملفات JSON في rules/. يأتي الماسح الضوئي مع:
| القاعدة | CVE | النمط |
|---|
rewrite-is-args | CVE-2026-42945 | rewrite … /path?args + set $var $1 |
rewrite-is-args-if | CVE-2026-42945 | rewrite … /path?args + if (… $1 …) |
nested-capture-redirect | CVE-2026-9256 | rewrite ^/((…))$ … redirect مع تداخل $N |
is_args القديمةالإصدارات المتأثرة: NGINX 0.6.27 – 1.30.0, NGINX Plus R32 – R36. الإصدارات المُصلَحة: 1.31.0 / 1.30.1.
يؤدي استبدال rewrite يحتوي على ? إلى تعيين e->is_args=1 في محرك السكريبت. تستمر هذه العلامة في التوجيهات اللاحقة set أو if، مما يتسبب في قيام تمرير النسخ بتشفير بيانات الالتقاط URI (توسيع 3x) في مخزن بحجم البايتات الخام.
location ~ ^/api/(.*)$ {
rewrite ^/api/(.*)$ /internal?migrated=true;
set $original_endpoint $1; # $1 evaluated with stale is_args=1
}
الإصدارات المتأثرة: حتى NGINX 1.31.0. الإصدارات المُصلَحة: 1.31.1 / 1.30.2.
تتسبب مجموعات الالتقاط المتداخلة في نقص حساب ميزانية الهروب في المسار السريع — يتم ترميز نفس بايتات URI مرة واحدة لكل مرجع $N متداخل.
rewrite ^/((.*))$ http://backend/$1$2 redirect;
# Single file
python3 scan.py /etc/nginx/nginx.conf
# Directory (recursive, all *.conf files)
python3 scan.py /etc/nginx/
# Filenames from stdin
find /etc/nginx -name '*.conf' | python3 scan.py -
# Pre-parsed JSONL (faster for large batches)
find /etc/nginx -name '*.conf' | ./build/nginx_conf_parse - | python3 scan.py --jsonl -
# JSON output
python3 scan.py --json /etc/nginx/nginx.conf
# Custom rules directory
python3 scan.py --rules ./my-rules/ /etc/nginx/nginx.conf
يمكن لحزمة corpus_tools/ جمع مواد إعدادات nginx العامة من GitHub Code Search واستخراج مقاطع nginx من تنسيقات الغلاف.
python3 corpus_tools/collect_github_nginx_corpus.py \
--output-dir corpus_out/nginx-rift \
--query-file corpus_tools/queries/nginx-rift.txt
python3 corpus_tools/extract_nginx_configs.py \
--input-dir corpus_out/nginx-rift/raw \
--output-dir corpus_out/nginx-rift/extracted
python3 scan.py corpus_out/nginx-rift/extracted
انظر corpus_tools/README.md و corpus_tools/docs/ للحصول على سير العمل الكامل وطريقة الجمع الموثقة المرتكزة على Rift.
ضع ملف JSON في rules/. يقوم المحرك بتحميل جميع ملفات *.json من مجلد القواعد عند بدء التشغيل.
{
"id": "rule-name",
"cve": "CVE-YYYY-NNNNN",
"severity": "CRITICAL",
"message": "short description shown in output",
"affected": "nginx version range",
"fixed": "fixed versions",
"ref": "https://link-to-advisory",
"match": [ ... ],
"tests": {
"vulnerable": ["server { ... }"],
"safe": ["server { ... }"]
}
}
يمكن أن تتضمن كل قاعدة tests مع مقاطع إعدادات vulnerable (يجب أن تُفعَّل) وsafe (يجب ألا تُفعَّل). شغّل python3 scan.py --test للتحقق من صحة جميع القواعد.
match هو قائمة بشروط التوجيهات. إذا كان هناك شرط واحد، يبحث المحرك في شجرة الإعدادات بأكملها عن أي توجيه مطابق. إذا كان هناك عدة شروط، يجب أن تتطابق جميع الشروط مع توجيهات شقيقة في نفس الكتلة.
كل شرط:
{
"directive": "rewrite",
"args": {
"0": { "contains": "?" },
"1": { "regex": "\\$[1-9]" }
},
"any_arg": { "regex": "\\$[1-9]" },
"or": [
{ "args": { "1": { "contains": "?" } } },
{ "any_arg_from": 2, "any_of": ["redirect", "permanent"] }
]
}
args — مُفهرسة حسب الموضع (كسلسلة نصية). كل قيمة عبارة عن كائن شرط.max_args — ارفض إذا كان التوجيه يحتوي على أكثر من N وسيط.overlapping_refs — تحقق من أن مراجع $N في args[1] تقابل مجموعات الالتقاط في args[0] التي تحتوي بعضها داخل بعض فعليًا.any_arg — يجب أن يطابق الشرط وسيطًا واحدًا على الأقل في أي موضع.or — يجب أن يطابق فرع واحد على الأقل. يمكن للفروع فحص args أو any_arg_from + any_of.| المفتاح | النوع | يطابق عندما |
|---|---|---|
contains | string | يحتوي الوسيط على السلسلة الفرعية |
regex | string | يطابق الوسيط التعبير النمطي |
not_regex | string | لا يطابق الوسيط التعبير النمطي |
any_of | [string] | يساوي الوسيط إحدى القيم |
extract | string | تعبير نمطي بمجموعة التقاط — يستخرج جميع المطابقات للعد |
min_unique | int | (يُستخدم مع extract) تم العثور على N مطابقات متميزة على الأقل |
extract + min_unique هي الآلية العامة لعد الأنماط في وسيط. على سبيل المثال، لطلب ما لا يقل عن مرجعَي التقاط متميزين ($1–$9):
{ "extract": "\\$([1-9])", "min_unique": 2 }
{
"id": "proxy-buffer-overflow",
"cve": "CVE-2099-99999",
"severity": "CRITICAL",
"message": "proxy_pass with oversized buffer — heap overflow",
"ref": "https://example.com/advisory",
"match": [
{
"directive": "proxy_pass",
"args": { "0": { "regex": "https?://" } }
},
{
"directive": "proxy_buffer_size",
"args": { "0": { "regex": "^(6[5-9]|[7-9]\\d|\\d{3,})k$" } }
}
],
"tests": {
"vulnerable": [
"server { location / { proxy_pass http://backend; proxy_buffer_size 128k; } }"
],
"safe": [
"server { location / { proxy_pass http://backend; proxy_buffer_size 8k; } }"
]
}
}
يحتوي مجلد parser/ على برنامج C مستقل يستخدم المحلل الخاص بـ nginx نفسه (ngx_conf_read_token وngx_conf_parse من src/core/ngx_conf_file.c) لتحليل ملفات الإعدادات إلى AST بصيغة JSON. هذا هو نفس المحلل الذي يستخدمه nginx عند بدء التشغيل — دون إعادة تنفيذ.
التعديل الرئيسي: يتم استبدال ngx_conf_handler() (الذي يوزع التوجيهات على الوحدات المضمنة) بـ conf_handler()، الذي يسجل كل توجيه في شجرة بغض النظر عن الاسم. يتيح ذلك للمحلل معالجة أي إعداد صالح دون نظام الوحدات. يُطبَّق التعديل كتصحيح وقت البناء — لا تُنسخ ملفات مصدر nginx إلى هذا المستودع.
يتم تجميع ملفات مصدر nginx المتبقية (مخصص الذاكرة pool، دوال السلاسل، التسجيل، إلخ) مباشرة من الوحدة الفرعية.
يقوم scan.py بتحميل قواعد JSON من rules/، ويحلل الإعدادات عبر nginx_conf_parse، ويقيّم كل قاعدة مقابل AST. جميع تفاصيل الاكتشاف موجودة في ملفات قواعد JSON.
يتطلب مترجم C وPython 3. يتم تضمين مصدر nginx كوحدة فرعية git.
git submodule update --init
make
make test
تتم مزامنة هذا المستودع للقراءة فقط إلى github.com/califio/ngxray عبر Copybara. انظر COPYBARA.md.