
أداة تحقق أوتوماتيكية من العيوب لـ 6 ثغرات في dnsmasq (CVE-2026-2291, 4890, 4891, 4892, 4893, 5172)
أداة صندوق أسود آلية للتحقق من 6 ثغرات في dnsmasq (مايو 2026). ترسل حزم هجوم إلى جهاز قيد الاختبار (DUT) وتُبلغ عن نجاح/فشل — بدون الحاجة إلى الوصول إلى الكود المصدري.
# قم بتعيين DNS الخاص بـ DUT إلى عنوان IP للشبكة الواسعة (WAN) للابتوب عبر الواجهة الرسومية أولاً، ثم:
sudo python3 dnsmasq_cve_verify.py --laptop <YOUR_WAN_IP> --dut <DUT_LAN_IP> --dut-pass <SSH_PASS>
# مثال:
sudo python3 dnsmasq_cve_verify.py --laptop 10.0.0.211 --dut 192.168.1.1 --dut-pass '12345Asdf@'
| CVE | CVSS | النوع | ناقل الهجوم | الميزة المتأثرة |
|---|---|---|---|---|
| CVE-2026-2291 | 9.2 | تجاوز سعة المخزن المؤقت في الكومة (Heap overflow) | عن بُعد | extract_name() — نشط دائمًا |
| CVE-2026-5172 | 7.5 | قراءة خارج الحدود / تعطل | عن بُعد | extract_addresses() — نشط دائمًا |
| CVE-2026-4890 | 7.5 | حلقة لا نهائية (DoS) | عن بُعد | تحليل خريطة بت NSEC (--dnssec) |
| CVE-2026-4891 | 5.3 | قراءة خارج الحدود في الكومة | عن بُعد | التحقق من RRSIG (--dnssec) |
| CVE-2026-4892 | 8.4 | تجاوز سعة الكومة → صلاحية الجذر | محلي/مجاور | CLID الخاص بـ DHCPv6 (--dhcp-script + DHCPv6) |
| CVE-2026-4893 | 5.3 | تجاوز التحقق | عن بُعد | فحص مصدر ECS (--add-subnet) |
السبب الجذري: union bigname يُعلن char name[MAXDNAME] ولكن الأحرف المُستَثناة يمكنها توسيع الاسم إلى 2*MAXDNAME+1 بايت، مما يسبب تجاوزًا للكومة.
طريقة الاختبار: يُرسل استعلامات DNS تحتوي على أسماء نطاقات بأحرف ذات بت عالٍ (0x80+) والتي يتم هروبها داخليًا بصيغة \DDD (4 بايت لكل بايت مدخل). إذا تعطل dnsmasq أو توقف عن الاستجابة، فهو معرض للخطر.
السلوك المُصحَّح: يرفض الأسماء الكبيرة جدًا بأدب (FORMERR/REFUSED) أو يستخدم مخزنًا مؤقتًا موسعًا.
السبب الجذري: حقل rdlen المُزَيَّف يسمح لـ extract_name() بتقديم المؤشر إلى ما بعد نهاية السجل. يؤدي نقص البايتات المتبقية إلى قيمة ضخمة → قراءة خارج الحدود ضخمة → تعطل.
طريقة الاختبار: يُرسل استجابات DNS مع سجلات CNAME حيث rdlen أصغر من الاسم المشفر الفعلي. إذا تعطل dnsmasq، فهو معرض للخطر.
السلوك المُصحَّح: يتحقق من بقاء المؤشر داخل حدود rdlen المُعلنة بعد extract_name().
السبب الجذري: تحليل خريطة بت NSEC يتقدم بمقدار p[1] بدلاً من p[1]+2 (نقص حجم رأس النافذة). مع bitmap_length=0، لا يتقدم المؤشر أبدًا → حلقة لا نهائية.
طريقة الاختبار: يُرسل سجل NSEC مزيفًا مع window=0, bitmap_length=0. إذا توقف dnsmasq عن الاستجابة لجميع الاستعلامات (يعلق، لا يتعطل)، فهو معرض للخطر. يمكن استغلاله قبل التحقق من RRSIG.
السلوك المُصحَّح: يتقدم بمقدار p[1]+2 ويتجاوز الخرائط البتية ذات الطول الصفري.
السبب الجذري: rdlen في RRSIG لم يتم التحقق منه مقابل الحجم الأدنى (18 + اسم المُوَقِّع). طول التوقيع المحسوب يقل تحت الصفر → يُعامَل كقيمة ضخمة → قراءة خارج الحدود.
طريقة الاختبار: يُرسل سجلات RRSIG مع rdlen=10 (أقل بكثير من الحد الأدنى 31+ بايت). تعطل = معرض للخطر.
السلوك المُصحَّح: يتحقق من rdlen >= fixed_fields + signer_name_length قبل حساب طول التوقيع.
السبب الجذري: CLIDs لـ DHCPv6 (حتى 65535 بايت) يتم ترميزها بصيغة ست عشرية عبر sprintf("%.2x") في daemon->packet (5131 بايت). CLID بحجم 3000 بايت → سلسلة ست عشرية بحجم 6000 بايت → تجاوز. عملية المساعد تعمل بصلاحية الجذر.
طريقة الاختبار: يُرسل SOLICIT لـ DHCPv6 مع معرّف عميل بحجم 3000 بايت. يتطلب الاتصال عبر IPv6 وتكوين --dhcp-script. تعطل المساعد = معرض للخطر.
السلوك المُصحَّح: يقتطع أو يتحقق من طول CLID قبل الترميز الست عشري.
ملاحظة: بعض الإصدارات تُجمَّع مع -DNO_DHCP6 ولا تتأثر بهذه الثغرة.
السبب الجذري: process_reply() يمرر طول سجل OPT (~23 بايت) بدلاً من طول الحزمة الكامل إلى check_source(). جميع فحوصات الحدود تفشل → الدالة تعيد دائمًا 1 (صالح).
طريقة الاختبار: يُرسل استعلامات DNS مع خيار EDNS Client Subnet يحتوي على بادئات مصدر مزيفة. إذا قام dnsmasq بإرجاع ECS دون تحقق، فهو معرض للخطر.
السلوك المُصحَّح: يمرر طول الحزمة الكامل إلى check_source()، مما يتيح فحوصات حدود مناسبة وفقًا لـ RFC 7871 القسم 9.2.
الترقية إلى dnsmasq 2.92rel2 (مُوصى بها)
dnsmasq_cve_verify.py)أداة ضمان الجودة الأساسية. تُشغَّل على الابتوب المختبر، وترسل حزم هجوم إلى DUT، وتُبلغ عن نجاح/فشل واضح لكل ثغرة CVE. لا يلزم تعديل DUT أبعد من الوصول عبر SSH للقراءة فقط لفحص الحالة.
┌─────────────────────────────────────────────────────────────────────┐
│ الابتوب المختبر │
│ │
│ واجهة LAN واجهة WAN │
│ <LAPTOP_LAN_IP> <LAPTOP_WAN_IP> │
│ │ │ │
│ │ ┌────┴──────────────┐ │
│ │ │ خادم DNS ضار │ │
│ │ │ (منفذ 53) │ │
│ │ └────┬──────────────┘ │
│ │ │ │
└────────┼───────────────────────────────┼────────────────────────────┘
│ شبكة LAN فرعية │ شبكة WAN فرعية
│ │
┌────────┼───────────────────────────────┼────────────────────────────┐
│ │ │ │
│ LAN: <DUT_LAN_IP> WAN: <DUT_WAN_IP> │
│ (بوابة LAN) (رابط WAN) │
│ │
│ DUT (راوتر Linksys) │
│ dnsmasq (أي إصدار < 2.92rel2) │
│ │
│ resolv-file=/etc/resolv.conf │
│ → nameserver <LAPTOP_WAN_IP> ← يُضبط عبر الواجهة الرسومية، يُمرر إلينا│
│ │
└─────────────────────────────────────────────────────────────────────┘
تدفق البيانات:
1. ترسل الأداة استعلام DNS إلى عنوان IP لـ LAN الخاص بـ DUT (منفذ 53)
2. لا يستطيع dnsmasq الخاص بـ DUT الحل محليًا → يُمرر upstream إلى LAPTOP_WAN_IP
3. خادمنا الضار على واجهة WAN يرد بحمولة هجوم
4. يعالج dnsmasq الخاص بـ DUT الاستجابة الضارة → تعطل/تعليق/نجاة
5. تتحقق الأداة من حالة DUT عبر SSH (للقراءة فقط)
إعداد مثال (عنوان IP الخاص بك سيختلف):
| الدور | IP (مثال) |
|---|---|
| LAN الابتوب | 192.168.1.254 |
| WAN الابتوب | 10.0.0.211 |
| LAN DUT | 192.168.1.1 |
| WAN DUT | 10.0.0.214 |
الشرط الأساسي: يجب أن يكون عنوان IP لـ WAN للابتوب و WAN لـ DUT على نفس الشبكة الفرعية، حتى يتمكن DUT من الوصول إلى الابتوب كخادم DNS upstream.