
HTTP/2 से HTTP/1.1 रूपांतरण के माध्यम से HTTP अनुरोध तस्करी की कमजोरियों का पता लगाता है और उनका शोषण करता है, बैकएंड पार्सिंग विसंगतियों की पहचान करने के लिए स्वचालित हेडर तस्करी तकनीकों का उपयोग करता है।
यह उपकरण HTTP अनुरोध तस्करी का पता लगाने और उसका शोषण करने में मदद करता है जब इसे फ्रंटएंड सर्वर द्वारा HTTP/2 -> HTTP/1.1 रूपांतरण के माध्यम से प्राप्त किया जा सकता है।
योजना इस प्रकार है:
हमलावर ऐसा अनुरोध ढूंढना चाहता है जो बैकएंड सर्वर को दो अलग-अलग अनुरोधों के रूप में दिखाई दे।
यदि फ्रंटएंड<->बैकएंड HTTP/1.1 कनेक्शन कीप-अलाइव का उपयोग करता है, तो फ्रंटएंड उसी कनेक्शन पर अन्य उपयोगकर्ताओं के अनुरोध भेज सकता है। यदि हम एक आंशिक अनुरोध के साथ कनेक्शन को "जहर" देने में सक्षम हैं जो एक वैध अनुरोध के बाद आता है, तो हम दूसरे उपयोगकर्ता से अनुरोध प्राप्त कर सकते हैं।
अन्य संभावित परिदृश्यों में फ्रंटएंड सर्वर सुरक्षा और पुनर्लेखन को दरकिनार करना, कैश पॉइज़निंग या कैश धोखाधड़ी शामिल है।
HTTP अनुरोध तस्करी पर अधिक जानकारी के लिए कृपया Portswigger Web Security Academy देखें।
HTTP/2 में, सभी HTTP हेडर के नाम और मान बाइनरी होते हैं। इसका मतलब है कि तकनीकी रूप से उनमें अतिरिक्त स्थान या नई पंक्तियाँ भी हो सकती हैं।
RFC7540#10.3 में कहा गया है कि जो कार्यान्वयन HTTP/2 अनुरोधों को HTTP/1 में अनुवाद करते हैं, उन्हें वर्ण सेट पर सीमाओं का ध्यान रखना चाहिए जो ऐसे रूपांतरण से उत्पन्न होती हैं; अधिकांश कार्यान्वयन वास्तव में उन्हें अस्वीकार करते हैं। इसके बावजूद, हम ऐसे कार्यान्वयन खोजने की उम्मीद करते हैं जो ऐसे हेडर की अनुमति देते हैं। वे बैकएंड के लिए परिवर्तित HTTP/1.1 अनुरोध को दूषित कर देंगे।
एक और बात यह है कि HTTP अनुरोध तस्करी से संबंधित कुछ हालिया सुधार केवल HTTP/1.1 पार्सर्स में लागू किए गए हो सकते हैं।
सामान्य तौर पर, हम उम्मीद करते हैं कि HTTP/2 के कुछ कार्यान्वयन हैं जो HTTP/1.1 में HTTP अनुरोध तस्करी पर हाल के शोध से बहुत अवगत नहीं हैं और संबंधित शमन उपायों को शामिल नहीं करते हैं।
आश्चर्यजनक रूप से, हाँ!
मुझे Cloudflare के माध्यम से एक स्पेस कैरेक्टर वाले हेडर की तस्करी करने की संभावना मिली, जिससे Cloudflare<->क्लाइंट तस्करी का दरवाजा खुल गया (यदि Cloudflare क्लाइंट का सॉफ्टवेयर हेडर नामों को स्वीकार करता है और ट्रिम करता है)। यहाँ ब्लॉग पोस्ट है।
एक और बग बाउंटी रिपोर्ट भी है जो अभी तक सार्वजनिक नहीं है। यह इस तथ्य का उपयोग करता है कि कस्टम सॉफ़्टवेयर HTTP2 हेडर में न्यूलाइन को फ़िल्टर नहीं करता है, और तस्करी 100% होती है (मैं अन्य उपयोगकर्ताओं के अनुरोध देख सकता हूँ)।
हालाँकि, मैं समझता हूँ कि इस प्रकार की कमजोरियाँ निराशाजनक रूप से दुर्लभ होनी चाहिए: HTTP/1.1 के विपरीत, HTTP/2 के इतने सारे कार्यान्वयन नहीं हैं, और उनमें से अधिकांश सुरक्षा को ध्यान में रखकर बनाए गए हैं, इस प्रकार संदिग्ध या अमान्य हेडर को अस्वीकार करते हैं।
टूल में एक उपकमांड है जो स्वचालित रूप से पता लगाने का प्रयास करता है कि कोई लक्ष्य HTTP अनुरोध तस्करी हमले के लिए संवेदनशील है या नहीं। इस सुविधा के पीछे एल्गोरिदम इस खंड में वर्णित है।
HTTP अनुरोध तस्करी हमला करने के लिए, हमें वास्तव में पहले एक हेडर (या तो Content-Length या Transfer-Encoding) की "तस्करी" करने की आवश्यकता है। इसका मतलब है कि हमें एक ऐसा हेडर भेजना होगा जो क) यह नियंत्रित करता है कि अनुरोध का मुख्य भाग कहाँ समाप्त होता है और ख) फ्रंटएंड द्वारा संसाधित नहीं किया जाता है लेकिन बैकएंड द्वारा संसाधित किया जाता है।
यह आमतौर पर एक हेडर को किसी तरह से संशोधित करके प्राप्त किया जाता है: इसके नाम के अंत में स्थान या टैब जोड़ना, मान को अर्ध-समतुल्य से बदलना आदि।
कमजोरी का पता लगाने वाले एल्गोरिदम का मूल विचार यह पता लगाना है कि क्या सर्वर वास्तव में एक तस्करी किए गए हेडर को संसाधित करता है जैसे कि वह Content-Length या Transfer-Encoding हो। हम एकाधिक अनुरोध भेजकर ऐसा करते हैं: कुछ हेडर के लिए मान्य और अन्य के लिए अमान्य मानों के साथ। फिर यह पता लगाने का प्रयास करते हैं कि क्या इन दोनों समूहों के प्रतिक्रियाओं को अलग करने का कोई तरीका है।
इसीलिए टूल आउटपुट में "संवेदनशील/असंवेदनशील" शब्द नहीं होते: यह केवल यह बताता है कि क्या वह इन दो समूहों के अनुरोधों पर आई प्रतिक्रियाओं को अलग कर सकता है।
टूल HTTP प्रतिक्रियाओं के दो सेटों को अलग-अलग मानता है यदि निम्नलिखित दो शर्तों में से कम से कम एक पूरी होती है:
टाइमआउट को एक अद्वितीय स्थिति कोड मान के रूप में माना जाता है जो किसी अन्य के बराबर नहीं है; इस प्रकार, टूल शास्त्रीय "समय द्वारा पता लगाने" योजना से बेहतर है।
आइए एक उदाहरण पर विचार करें। मान लीजिए कि हम डैश को अंडरस्कोर से बदलकर transfer-encoding हेडर की तस्करी करने का प्रयास कर रहे हैं।
यदि ऐसा प्रतीत होता है कि सर्वर हर बार जब हम transfer_encoding:zalupa भेजते हैं तो स्थिति 400 के साथ प्रतिक्रिया करता है और जब यह transfer_encoding:chunked होता है तो हैंग हो जाता है, तो हम कह सकते हैं कि सर्वर संभवतः हेडर को ट्रांसफर एन्कोडिंग के मान के रूप में संसाधित करता है। सैद्धांतिक रूप से, यह फ्रंटएंड या बैकएंड सर्वर हो सकता है।
पहला मामला दिलचस्प नहीं है क्योंकि हम वैसे भी हेडर का गैर-तस्करी संस्करण भेज सकते हैं, और दूसरा वह है जिसकी हम तलाश कर रहे हैं। चूंकि सब कुछ HTTP/2 पर होता है, पहला मामला अधिकांश समय टाला जा सकता है: HTTP/2 सर्वर अनुरोध के मुख्य भाग के अंत को दूसरे तरीके से निर्धारित करता है जो अनुरोध हेडर से असंबंधित है और कभी भी HTTP/1.1 चंक्ड प्रारूप (हेक्साडेसिमल चंक लंबाई वाला) में मुख्य भाग की अपेक्षा नहीं करता है।
पता लगाने की तकनीकों के ठोस रूपांतर हैं:
हम Transfer-Encoding: chunked का तस्करी किया गया संस्करण (जैसे transfer_encoding:chunked) और अलग-अलग मुख्य भाग भेजते हैं: मान्य है 0\r\n\r\n और अमान्य है 999\r\n।
यदि प्रतिक्रियाएँ अलग हैं, तो हम निश्चित हो सकते हैं कि बैकएंड सर्वर तस्करी किए गए हेडर को प्राप्त करता है और संसाधित करता है। फ्रंटएंड के ऐसा करने का कोई कारण नहीं है: HTTP/2 हमारे द्वारा भेजे गए चंक्ड प्रारूप का उपयोग नहीं करता है, इसलिए यह अमान्य होगा।
हम उम्मीद करते हैं कि बैकएंड अमान्य अनुरोधों के लिए हैंग हो जाता है (अर्थात, अनुरोध टाइमआउट हो जाता है) क्योंकि यह अधिक डेटा आने की प्रतीक्षा करता है।
यह सबसे विश्वसनीय पता लगाने का रूपांतर है: यदि मुख्य भाग पढ़ते समय सर्वर हैंग हो जाता है, तो संभवतः कुछ गलत हो गया है क्योंकि HTTP/2 अनुरोध में HTTP/1.1 ट्रांसफर एन्कोडिंग का कोई उपयोग नहीं है।
हम Transfer-Encoding का तस्करी किया गया संस्करण और फिर से अलग-अलग मुख्य भाग भेजते हैं: 0\r\n\r\n मान्य मुख्य भाग के रूप में और X\r\n\r\n अमान्य मुख्य भाग के रूप में।
मामला ऊपर जैसा ही है, लेकिन मुख्य भाग पढ़ने के बजाय, हम उम्मीद करते हैं कि बैकएंड कम से कम इसे मान्य करेगा।
हम Content-Length हेडर का तस्करी किया गया संस्करण मान 1 और -1 के साथ भेजते हैं।
दोनों मान फ्रंटएंड के दृष्टिकोण से अमान्य हैं: HTTP/2 में मुख्य भाग की लंबाई निर्धारित करने का एक और तंत्र है, और दोनों मामलों में वास्तव में कोई अनुरोध मुख्य भाग नहीं भेजा जाता है। यदि प्रतिक्रियाएँ अलग हैं, तो हम मानते हैं कि यह बैकएंड सर्वर था जिसने हेडर को पार्स किया।
यह विधि सबसे कम विश्वसनीय है: हो सकता है कि फ्रंटएंड अलग-अलग त्रुटियाँ जारी करे जब Content-Length का मान अमान्य हो, और जब यह वास्तविक लंबाई से मेल नहीं खाता हो।
कई जोड़े मान्य/अमान्य अनुरोध भेजकर, हम यादृच्छिक गलत सकारात्मकता की संभावना को कम कर सकते हैं। दूसरी ओर, हम जल्दी रुक सकते हैं यदि हम देखते हैं कि "मान्य" अनुरोधों पर प्रतिक्रियाओं को "अमान्य" अनुरोधों पर प्रतिक्रियाओं से अलग करने का कोई तरीका नहीं है।
टूल एक हेडर को विभिन्न तरीकों से संशोधित करके कई तस्करी तकनीकों का उपयोग करने का प्रयास करता है। उनमें से कोई भी नई या एक ही समय में स्पष्ट नहीं है।
एक हेडर की तस्करी करने के लिए, हम इसमें एक स्थान जोड़ते हैं। यह सबसे सामान्य और क्लासिक विधि है। हम उम्मीद करते हैं कि हेडर फ्रंटएंड द्वारा संसाधित नहीं किया जाता है बल्कि ज्यों का त्यों बैकएंड को भेजा जाता है, जो स्थान को हटा देता है।
टूल स्थान के रूप में विभिन्न प्रकार के वर्णों का प्रयास करता है: इसमें , \t, \v, \x00 और यूनिकोड शामिल हैं।
एक हेडर की तस्करी करने के लिए, हम डैश (-) को अंडरस्कोर (_) से बदलते हैं। यदि बैकएंड किसी तरह से CGI-प्रेरित है, तो यह हेडर जैसे Header-Name को HEADER_NAME रूप में परिवर्तित कर सकता है; इस प्रकार, डैश वैसे भी अंडरस्कोर बन जाएगा। यह निर्धारित करते समय कि मुख्य भाग को कैसे पार्स किया जाए, ऐसा बैकएंड संभवतः अपने हेडर शब्दकोश से CONTENT_LENGTH / TRANSFER_ENCODING का मान मांगता है, और वह वहाँ होगा।
यह HTTP/2 विशिष्ट है। चूंकि HTTP/2 एक बाइनरी प्रोटोकॉल है, हम हेडर नाम या मान में न्यूलाइन भेजने का प्रयास कर सकते हैं। मानक इसे प्रतिबंधित करता है, लेकिन हम एक ऐसा कार्यान्वयन खोजने की उम्मीद करते हैं जो फिर भी इसे स्वीकार करता है।
HTTP/2 -> HTTP/1.1 रूपांतरण के दौरान, हेडर दो अलग-अलग हेडर में विभाजित हो जाता है, जिसका अर्थ है कि अनुरोध बैकएंड के लिए अलग दिखाई देगा।
एक हेडर की तस्करी करने के लिए, हम एक न्यूलाइन के बाद इसका नाम और मान रखते हैं: नाम "Transfer-Encoding" और मान "chunked" वाला एक हेडर "fake" नाम और "fake\r\ntransfer-encoding: chunked" मान वाला हो जाता है।
मान लीजिए कि बैकएंड किसी उच्च-स्तरीय भाषा का उपयोग करता है और हेडर का पर्याप्त सत्यापन नहीं करता है। उस स्थिति में, यह कुछ और करने से पहले नामों को अपरकेस में परिवर्तित कर सकता है और यूनिकोड-जागरूक फ़ंक्शन का उपयोग करके ऐसा कर सकता है। सौभाग्य से, TRANSFER-ENCODING में अक्षर S है, जो ſ (\u017f) का अपरकेस है।
इसी तरह, हम एक ऐसे बैकएंड की खोज कर सकते हैं जो Transfer-Encoding के मान को लोअरकेस में परिवर्तित करेगा: हम chunKed के बजाय chunked भेजते हैं जिसमें K के बजाय \u212a होता है।
बेशक, यह आवश्यक है कि फ्रंटएंड UTF-8 हेडर नाम/मान बैकएंड को पास करे।
टूल को स्थापित करने के लिए, go install github.com/neex/http2smugl@latest चलाएँ।
टूल में दो उपकमांड हैं: request और detect। पहला केवल HTTP/2 अनुरोध तैयार करने के लिए है: अधिकांश क्लाइंट टूल अमान्य हेडर स्वीकार नहीं करते हैं, इसलिए एक ऐसा होना सुविधाजनक है जो उपयोगकर्ता इनपुट को सर्वर पर ज्यों का त्यों भेजता है।
दूसरा है detect। यह लक्ष्य के संवेदनशील होने का पता लगाने के लिए HTTP अनुरोध तस्करी की विभिन्न तकनीकों का प्रयास करता है। पता लगाने का एल्गोरिदम जटिल है; मैं इसकी सराहना करता हूँ यदि आप इसे पढ़ें और मुझे अपनी टिप्पणियाँ भेजें। इसका वर्णन नीचे संबंधित खंड में किया गया है।
http2smugl requestएक (संभवतः थोड़ा विकृत) HTTP/2 अनुरोध भेजने के लिए इस उपकमांड का उपयोग करें। पहला पैरामीटर URL है, और अन्य केवल name:value प्रारूप में हेडर हैं (ध्यान दें कि कोलन के बाद कोई स्थान नहीं है)। बैकस्लैश एस्केपिंग समर्थित है: आप \r, \n और \xXX एस्केप कोड का उपयोग कर सकते हैं। उदाहरण के लिए, एक हेडर नाम भेजने के लिए जिसमें कोलन है, \x3a का उपयोग करें (जैसे name\x3awith\x3acolons:value)।
http2smugl detectयह उपकमांड विभिन्न तकनीकों का उपयोग करके HTTP अनुरोध तस्करी का पता लगाने का प्रयास करता है। इसका उपयोग करने के लिए, बस http2smugl detect [HTTPS URL] चलाएँ।
कमांड तभी कुछ आउटपुट देगा जब वह पता लगा सके कि सर्वर एक तस्करी किए गए हेडर को पार्स करता है। इसका क्या अर्थ है, यह समझने के लिए कृपया संबंधित खंड पढ़ें।
HTTP/3 (quic) के लिए एक प्रायोगिक समर्थन लागू किया गया है। हालाँकि, मैं इसका उपयोग करने का सुझाव नहीं देता क्योंकि मुझे HTTP/3 से संबंधित एक भी बग नहीं मिला।
request उपकमांड के साथ HTTP/3 का उपयोग करने के लिए, URL में सिर्फ https के बजाय https+h3:// प्रोटो प्रदान करें। detect कमांड में भी यही समर्थित है।
request उपकमांड के लिए एक --try-http3 फ्लैग भी है, जो प्रोटोकॉल URL में निर्दिष्ट न होने पर व्यवहार को बदलता है (सिर्फ होस्टनाम)। यदि फ्लैग मौजूद है, तो कमांड कमांड लाइन पर ऐसी प्रविष्टियों या लक्ष्य फ़ाइल के लिए https+h3 प्रोटो का प्रयास करेगा (साथ ही HTTP/2 भी)। उदाहरण के लिए, http2smugl detect --try-http3 www.example.com HTTP/3 और HTTP/2 दोनों का प्रयास करेगा, लेकिन http2smugl detect --try-http3 https://www.example.com/ केवल HTTP/2 का प्रयास करेगा।
इस खंड में, मैं कुछ ऐसे मामलों का वर्णन करता हूँ जब उपकरण कहता है कि प्रतिक्रियाएँ "अलग-अलग" हैं, लेकिन कोई कमजोरी मौजूद नहीं हो सकती।
Amazon का Elastic Load Balancer HTTP अनुरोध तस्करी से कई शमन उपायों को लागू करता है। जबकि उनमें से सभी डिफ़ॉल्ट रूप से अनुरोध को अस्वीकार नहीं करते हैं, ELB "संदिग्ध" हेडर के साथ अनुरोध भेजने के बाद कनेक्शन का पुन: उपयोग नहीं करेगा। इस प्रकार एक वास्तविक हमला संभव नहीं है।
यह पता लगाने के लिए कि आप ELB से निपट रहे हैं, आप Server प्रतिक्रिया हेडर का उपयोग कर सकते हैं। यदि यह फ़िल्टर हो जाता है, तो आप content__length हेडर (दो अंडरस्कोर नोट करें) और मान -1 के साथ एक अनुरोध भेज सकते हैं: यदि यह 400 है, तो यह संभवतः ELB या अन्य WAF है (नीचे देखें)।
Apache Traffic Server मुख्य रूप से Yahoo पर उपयोग किया जाता है। यह HTTP/2 को एक असामान्य तरीके से संसाधित करता है: यह इसे मेमोरी में HTTP/1.1 में परिवर्तित करता है और फिर परिणामी अनुरोध को फिर से पार्स करता है। इस प्रकार, जबकि एक हेडर तकनीकी रूप से "तस्करी" किया जा सकता है, इसके परिणामस्वरूप कमजोरी होने का कोई तरीका नहीं है: आप HTTP/1.1 कनेक्शन पर वही बाइट्स भेज सकते हैं।
ATS से निपटने का पता लगाने का सबसे आसान तरीका (Server हेडर के अलावा) TRACE अनुरोध भेजना है। यदि अनुरोध में Max-Forwards: 0 हेडर मौजूद है, तो ATS बिना बैकएंड को अग्रेषित किए डिफ़ॉल्ट रूप से TRACE अनुरोधों का उत्तर लौटाएगा।
ऐसा प्रतीत होता है कि Microsoft IIS HTTP/2 मुख्य भागों के अंदर चंक्ड एन्कोडिंग को डिकोड करने का समर्थन करता है। यह अजीब व्यवहार है; हालाँकि, यह सुरक्षा के दृष्टिकोण से निर्दोष है।
यह पता लगाने के लिए कि आप IIS से निपट रहे हैं, आप "transfer-encoding:chunked" हेडर और गलत चंक्ड मुख्य भाग के साथ एक अनुरोध भेज सकते हैं। यदि आप Server हेडर में Microsoft-HTTPAPI या ऐसा कुछ देखते हैं, तो यह वही है।
WAF संदिग्ध हेडर का पता लगाने का प्रयास करते हैं - यह उनका काम है। कभी-कभी इसके परिणामस्वरूप उपकरण "अलग-अलग" कहता है: WAF content_length:-1 जैसी चीज़ को ब्लॉक कर सकता है और content_length:1 को अनुमति दे सकता है, सिर्फ इसलिए कि उसके फ़िल्टर इन निर्णयों के साथ बाहर आते हैं।
यदि आप Server हेडर में एक WAF देखते हैं, तो यह संभवतः एक गलत सकारात्मकता है।
यदि आपके पास इस विषय पर कोई विचार हैं, तो मुझसे Twitter पर @emil_lerner या Telegram पर @neexemil के माध्यम से संपर्क करें - या यहाँ GitHub पर एक मुद्दा पोस्ट करें।