
QUIC संगत संस्करण-वार्ता key-poison handshake DoS का प्रमाण-अवधारणा और लैब, 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
संगत संस्करण वार्ता (compatible version negotiation) को तब तक प्रतीक्षा करनी चाहिए जब तक क्लाइंट वास्तव में नए संस्करण के पैकेट को प्रोसेस न कर सके। यह ट्री उस क्षण संस्करण कमिट कर देता है और Initial keys फिर से बना देता है जब कोई long header संगत दिखता है, फिर जारी रहता है। Decrypt-fail "undo" संस्करण संख्या को बहाल करता है और keys को v2 पर छोड़ देता है। Handshake-प्रकार या truncated पैकेट उस undo तक कभी पहुँचता ही नहीं।
सर्वर 4-tuple से एक UDP datagram डिफ़ॉल्ट क्लाइंट handshake को रोक देता है। प्रोसेस चालू रहता है। कोई RCE नहीं।
| ID | अभी तक कोई CVE नहीं |
| CWE | CWE-345, CWE-347, CWE-670 |
| CVSS | High: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
| Product | MsQuic |
| Affected | v2.6.1 (a01333cf); जब मैंने जाँचा तब भी main पर मौजूद |
| Auth | unauthenticated UDP, स्रोत सर्वर 4-tuple होना चाहिए (on-path, या सर्वर का IP spoof प्लस क्लाइंट पोर्ट) |
| License | GNU Affero GPL v3.0 |
| Lab | केवल 127.0.0.1 |
QUIC v1 handshake के दौरान, सर्वर एड्रेस और पोर्ट से सोर्स किया गया एक long-header datagram भेजें। संस्करण QUIC v2 है (RFC 9369, wire 6b 33 43 cf)। DestCidLength 0 है। डिफ़ॉल्ट क्लाइंट exclusive connected UDP socket का उपयोग करते हैं, इसलिए वे शून्य-लंबाई DCID स्वीकार करते हैं। लाइब्रेरी उसे AEAD से पहले संगत संस्करण वार्ता मान लेती है, नई Initial keys व्युत्पन्न करती है, फिर या तो decrypt-fail revert तक कभी नहीं पहुँचती (Handshake प्रकार स्थगित है; truncated Initial पहले FALSE लौटाता है) या Stats.QuicVersion को बहाल करती है और keys को v2 पर छोड़ देती है।
वास्तविक सर्वर v1 बोलता रहता है। क्लाइंट v2 salts के साथ पुनःप्रेषण करता है। वह प्रयास कभी Connected तक नहीं पहुँचता। उसी प्रोसेस पर अन्य कनेक्शन अछूते रहते हैं। यह handshake DoS है, RCE नहीं।
Off-path के लिए सर्वर का IP spoof प्लस क्लाइंट का ephemeral UDP पोर्ट चाहिए। On-path पर्याप्त है। Exclusive binding का मतलब है कि हमलावर को CID की आवश्यकता नहीं है।
मैंने पहले microsoft/msquic पर 2026 GHSA लहर पढ़ी: hostname MITM (GHSA-w5f4-fx9m-m4q7, यह टैग cherry-pick है), path-UAF RCE (GHSA-92f5-vc22-8j33), ACK underflow (GHSA-gvvw-8j96-8g5r)। वे sinks v2.6.1 पर बंद हैं।
फिर packet headers, CID, version negotiation, retry, stateless reset, CIBIR। RFC 9368 compatible VN बचा हुआ है। QuicConnRecvHeader संस्करण बेमेल देखता है, पूछता है कि क्या यह संगत है, OriginalQuicVersion लिखता है, CompatibleVerNegotiationAttempted सेट करता है, Stats.QuicVersion असाइन करता है, QuicCryptoOnVersionChange कॉल करता है, और जारी रहता है। टिप्पणी कहती है कि transport parameters बाद में ChosenVersion जाँचेंगे। यदि handshake कभी decrypt नहीं होता तो वे TPs कभी नहीं आते।
Decrypt-fail एक फिक्स जैसा दिखता है जब तक आप इसे नहीं पढ़ते:
Connection->Stats.QuicVersion = Connection->OriginalQuicVersion;
Connection->State.CompatibleVerNegotiationAttempted = FALSE;
कोई दूसरा QuicCryptoOnVersionChange नहीं। HKDF labels v2 पर ही रहते हैं। Handshake-प्रकार के पैकेट स्थगित होते हैं (HANDSHAKE > ReadKey=INITIAL) और RecvHeader FALSE लौटाता है, इसलिए वह undo कभी नहीं चलता।
मुझे src/tools/attack की आवश्यकता नहीं थी। सात बाइट्स पर्याप्त हैं।
पहले से दर्ज गलत रास्ते: सर्वर के listen पोर्ट पर इंजेक्ट करना (क्लाइंट exclusive-connected है, वह उसे नहीं देखेगा); loopback पर poison से पहले Initial अग्रेषित करना (handshake पहले पूरा हो जाता है, poison देर से आता है); सैंपल सर्वर के प्रतीक्षा के रूप में getchar() (Docker कोई कीबोर्ड नहीं है); खाली openssl submodule के साथ shallow clone (इमेज को v2.6.1 fetch करना और openssl init करना पड़ता है); प्रमाण के रूप में कोई भी बकवास long header (गैर-संगत 11223344 अभी भी Connected - दिलचस्प हिस्सा संगत v2 है)। नाटक: एक CRYPTO payload, एक reverse shell, RCE। गवाह handshake परिणाम है।
cd lab
./run.sh
UDP proxy 127.0.0.1:18150 वह एड्रेस है जिससे क्लाइंट कनेक्ट करता है। वास्तविक quicsample सर्वर 127.0.0.1:18151 है। पहले क्लाइंट datagram पर PoC proxy socket से truncated v2 Handshake f06b3343cf0000 भेजता है, फिर अग्रेषित करता है। इसे loopback पर बाइंड करें।
control_connected=1
inject_connected=0
negative_connected=1
inject_hex=f06b3343cf0000
SUCCESS MSQUIC-COMPAT-VN-KEY-POISON-WITNESS
CONTROL केवल forward-only है और उसे Connected होना चाहिए। NEGATIVE पहले f0112233440000 इंजेक्ट करता है और फिर भी Connected होना चाहिए। INJECT ही एकमात्र चरण है जो मरता है।
जब तक ट्रिगर करने वाला पैकेट decrypt न हो, QuicCryptoOnVersionChange को कॉल न करें। कमिट के बाद प्रत्येक विफलता पर (RecvHeader FALSE और decrypt-fail) संस्करण बहाल करें और keys को OriginalQuicVersion पर वापस व्युत्पन्न करें। Handshake / 0-RTT को compatible-VN ट्रिगर के रूप में अनदेखा करें। केवल नए संस्करण का decryptable Initial ही स्विच करना चाहिए।
connection.c · crypto.c · binding.c · version_neg.c