
إثبات مفهوم ومختبر يوضحان هجوم حجب الخدمة على مصافحة QUIC المتوافقة مع التفاوض على الإصدار عبر تسميم المفتاح في MsQuic v2.6.1، مع شاهد حقن UDP وتحليل الإصلاح.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · msquic-compat-vn-key-poison
MsQuic v2.6.1 (a01333cf) - Microsoft
من المفترض أن ينتظر التفاوض المتوافق على الإصدار حتى يتمكن العميل فعليًا من معالجة حزمة بالإصدار الجديد. تُثبّت هذه الشجرة الإصدار وتعيد إنشاء مفاتيح Initial في اللحظة التي يبدو فيها الترويسة الطويلة متوافقة، ثم تستمر. يعيد "التراجع" عند فشل فك التشفير رقم الإصدار ويترك المفاتيح على v2. أما حزمة من نوع Handshake أو حزمة مبتورة فلا تصل حتى إلى ذلك التراجع.
حزمة UDP واحدة من الرباعية (4-tuple) الخاصة بالخادم توقف مصافحة العميل الافتراضي. تبقى العملية قائمة. لا يوجد RCE.
| ID | لا يوجد CVE بعد |
| CWE | CWE-345، CWE-347، CWE-670 |
| CVSS | مرتفع: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
| المنتج | MsQuic |
| المتأثر | v2.6.1 (a01333cf)؛ لا يزال على main عند فحصي |
| المصادقة | UDP بدون مصادقة، يجب أن يكون المصدر هو الرباعية (4-tuple) الخاصة بالخادم (على المسار، أو انتحال IP للخادم مع منفذ العميل) |
| الترخيص | GNU Affero GPL v3.0 |
| المختبر | 127.0.0.1 فقط |
أثناء مصافحة QUIC v1، أرسل حزمة واحدة بترويسة طويلة مصدرها عنوان الخادم ومنفذه. الإصدار هو QUIC v2 (RFC 9369، على السلك 6b 33 43 cf). قيمة DestCidLength هي 0. يستخدم العملاء الافتراضيون مقبس UDP متصلًا حصريًا، لذا يقبلون DCID بطول صفر. تعامل المكتبة ذلك كتفاوض متوافق على الإصدار قبل AEAD، وتشتق مفاتيح Initial جديدة، ثم إما لا تصل أبدًا إلى تراجع فشل فك التشفير (نوع Handshake مؤجَّل؛ Initial المبتور يعيد FALSE أولًا) أو تتراجع عن Stats.QuicVersion وتترك المفاتيح على v2.
يستمر الخادم الحقيقي في التحدث بـ v1. يعيد العميل الإرسال بأملاح v2. لا تصل تلك المحاولة أبدًا إلى Connected. لا تتأثر الاتصالات الأخرى في العملية نفسها. هذا حجب خدمة (DoS) للمصافحة، وليس RCE.
يحتاج الهجوم من خارج المسار إلى انتحال IP للخادم مع منفذ UDP العابر للعميل. الهجوم على المسار كافٍ. يعني الربط الحصري أن المهاجم لا يحتاج إلى CID.
قرأت أولًا موجة GHSA لعام 2026 على microsoft/msquic: اعتراض اسم المضيف (GHSA-w5f4-fx9m-m4q7، هذا الوسم هو الـ cherry-pick)، وRCE عبر path-UAF (GHSA-92f5-vc22-8j33)، وACK underflow (GHSA-gvvw-8j96-8g5r). تلك المنافذ مغلقة على v2.6.1.
ثم ترويسات الحزم، وCID، والتفاوض على الإصدار، وretry، وstateless reset، وCIBIR. التفاوض المتوافق على الإصدار وفق RFC 9368 هو المتبقي. يرى QuicConnRecvHeader عدم تطابق في الإصدار، ويسأل إن كان متوافقًا، ويكتب OriginalQuicVersion، ويضبط CompatibleVerNegotiationAttempted، ويعيّن Stats.QuicVersion، ويستدعي QuicCryptoOnVersionChange، ويستمر. يقول التعليق إن معاملات النقل ستتحقق من ChosenVersion لاحقًا. لا تصل تلك TPs أبدًا إذا لم تُفكّ المصافحة.
يبدو فشل فك التشفير كإصلاح حتى تقرأه:
Connection->Stats.QuicVersion = Connection->OriginalQuicVersion;
Connection->State.CompatibleVerNegotiationAttempted = FALSE;
لا يوجد استدعاء ثانٍ لـ QuicCryptoOnVersionChange. تبقى تسميات HKDF على v2. حزم نوع Handshake تؤجَّل (HANDSHAKE > ReadKey=INITIAL) ويعيد RecvHeader القيمة FALSE، لذا لا يُنفَّذ ذلك التراجع أبدًا.
لم أحتج إلى src/tools/attack. سبعة بايتات كافية.
المنعطفات الخاطئة المسجلة مسبقًا: الحقن عند منفذ استماع الخادم (العميل متصل حصريًا، فلن يرى ذلك)؛ تمرير Initial قبل السم قبل الحزمة على loopback (تنتهي المصافحة أولًا، فيصل السم متأخرًا)؛ استخدام getchar() كانتظار لخادم العينة (Docker ليس لوحة مفاتيح)؛ استنساخ سطحي مع وحدة فرعية openssl فارغة (يجب أن تجلب الصورة v2.6.1 وتهيّئ openssl)؛ أي ترويسة طويلة عشوائية كدليل (الإصدار غير المتوافق 11223344 يظل Connected - الجزء المثير هو v2 المتوافق). مسرحيات: حمولة CRYPTO، أو reverse shell، أو RCE. الشاهد هو نتيجة المصافحة.
cd lab
./run.sh
وكيل UDP 127.0.0.1:18150 هو العنوان الذي يتصل به العميل. خادم quicsample الحقيقي هو 127.0.0.1:18151. عند أول حزمة من العميل يرسل PoC حزمة Handshake v2 مبتورة f06b3343cf0000 من مقبس الوكيل، ثم يمرر. اربطه بـ loopback.
control_connected=1
inject_connected=0
negative_connected=1
inject_hex=f06b3343cf0000
SUCCESS MSQUIC-COMPAT-VN-KEY-POISON-WITNESS
CONTROL هو تمرير فقط ويجب أن يكون Connected. NEGATIVE يحقن f0112233440000 أولًا ويجب أن يظل Connected. INJECT هو المرحلة الوحيدة التي تموت.
لا تستدعِ QuicCryptoOnVersionChange حتى تُفكّ الحزمة المُسبِّبة. عند كل فشل بعد التثبيت (RecvHeader FALSE و فشل فك التشفير) استعد الإصدار و أعد اشتقاق المفاتيح إلى OriginalQuicVersion. تجاهل Handshake / 0-RTT كمحفزات للتفاوض المتوافق على الإصدار. يجب أن يبدّل فقط Initial قابل لفك التشفير بالإصدار الجديد.
connection.c · crypto.c · binding.c · version_neg.c