
خيار IPv6 ND بطول صفر. فحص واحد مفقود. البرنامج الخفي يسير إلى الخلف ويعيش في الحلقة. تم الإبلاغ إلى OpenBSD، تم الإصلاح، تم تعيين CVE.
| CVE | CVE-2026-41285 |
| فئة الخطأ | حلقة لا نهائية عبر تجاوز عدد صحيح سالب |
| السبب الجذري | محلل خيارات ND ينفذ nd_opt_len * 8 - 2 دون التحقق من len==0 |
| المكون | sbin/slaacd/engine.c, usr.sbin/rad/engine.c |
| التأثير | تعطيل دائم لـ SLAAC IPv6 (slaacd) أو خدمة RA (rad) |
| المطلوب | أي جهاز على نفس قطاع الشبكة L2 |
| تم اختباره | OpenBSD 7.8 GENERIC amd64 |
تنص الفقرة §4.6 من RFC 4861 على أن خيارات ND التي طولها صفر غير صالحة ويجب التخلص منها بصمت. يقوم النواة الخاصة بها nd6_options() في sys/netinet6/nd6.c بذلك بشكل صحيح. لكن حزمة ICMPv6 الخام لا تزال تُرسل إلى مقابس المستخدم قبل أن يصبح هذا الفحص مهمًا.
يقوم كل من slaacd و rad بتحليل خيارات ND بأنفسهم. تبدو الحلقة كما يلي:
while (len > 0) {
// ...
optlen = nd_opt->nd_opt_len * 8 - 2; // nd_opt_len is uint8_t
if (optlen > len)
break;
len += 2;
// advance pointer by optlen... which is (uint32_t)-2 promoted from int
}
عندما يكون nd_opt_len == 0: التعبير 0 * 8 - 2 يُرقّى إلى int ← -2.
الشرط الحارس (-2 > len) يكون دائمًا خطأ (مقارنة موقعة، len موجب).
ثم len += 2 والمؤشر يعود للخلف بمقدار 2 بايت. الحلقة لا تتقدم أبدًا.
تستخدم وحدة المعالجة المركزية بنسبة 100%. إلى الأبد.
تحققت النواة من نسختها الخاصة. تلقى الخفي في مساحة المستخدم الأصلي الخام. لم يخبر أحد slaacd.
rcctl restart slaacd أو إعادة تشغيل النظامpython3 poc/kill_slaacd.py <interface>
يتطلب Scapy. يرسل إعلان راوتر واحد مع خيار ND واحد حيث nd_opt_len = 0. هذا كل شيء. نوع الخيار لا يهم (يستخدم PoC النوع 200 / غير معروف).
للإثبات الكامل من البداية إلى النهاية (يظهر SLAAC يعمل ← الهجوم ← SLAAC ميت):
python3 poc/prove_dos.py
── Before exploit ──
SLAAC addresses: 2001:db8:1:0:df6f:edeb:6e3a:2640, ...
Engine CPU: 0.0%
── After one packet ──
Engine CPU: 23.1% → 43.3% (climbing)
New RA with 2001:db8:2::/64 sent → no address configured
slaacd is dead. IPv6 autoconf: DEAD.
نفس سطح الهجوم مثل CVE-2022-27881 و CVE-2022-27882 (حلقات لا نهائية سابقة في slaacd في engine.c، أيضًا تحليل خيارات ND). هذه حالة جديدة من نفس فئة الأخطاء - لم تغطي التصحيحات السابقة جميع حلقات التحليل.
تحقق من nd_opt_len == 0 قبل إجراء العمليات الحسابية عليه. اخرج من الحلقة. هذا ما تفعله النواة بالفعل في nd6_options():
if (nd_opt->nd_opt_len == 0)
break; // or: goto bad;
التصحيح المقترح لـ parse_ra() في slaacd، و debug_log_ra()، ومحلل RS في rad - جميع الحلقات الثلاث تحتاج إلى نفس الحارس المكون من سطر واحد.
هذا هجوم تعطيل على الشبكة المحلية. إذا كنت على نفس قطاع L2 الذي يعمل عليه جهاز OpenBSD مع تشغيل slaacd، فإن حزمة واحدة تجمد IPv6 الخاص به. لا ترسلها إلى شبكات لا تملكها. إذا كنت تدير OpenBSD، تحقق من وجود تصحيح أو أضف الحارس len==0 بنفسك.
Daniel Wade - GitHub · Twitter/X · Bluesky · nadsec.online