
SSRF फ़िल्टर ने होस्टनाम टेक्स्ट की जाँच की, लेकिन वास्तविक गंतव्य बाद में DNS द्वारा तय किया गया। उस अंतराल ने हमलावर-नियंत्रित वेबहुक URL को लूपबैक, मेटाडेटा और निजी नेटवर्क लक्ष्यों तक पहुँचने दिया।
SSRF फ़िल्टर ने होस्टनाम टेक्स्ट की जाँच की, लेकिन वास्तविक गंतव्य बाद में DNS द्वारा तय किया गया। इस अंतर ने हमलावर-नियंत्रित Webhook URL को लूपबैक, मेटाडेटा और निजी नेटवर्क लक्ष्यों तक पहुँचने दिया।
मुझे यह समस्या Typebot की समीक्षा करते समय मिली, जो एक ओपन-सोर्स चैटबॉट बिल्डर है, जिसमें एक सरल सुरक्षा प्रश्न था:
क्या होता है अगर SSRF सुरक्षा होस्टनाम टेक्स्ट को मान्य करती है, लेकिन वास्तविक गंतव्य बाद में DNS द्वारा तय किया जाता है?
इस मामले में, उस प्रश्न ने एक वास्तविक बग का पता लगाया।
Typebot की Webhook / HTTP Request ब्लॉक के लिए SSRF सुरक्षा ने केवल यह मान्य किया:
इसने अनुरोध की अनुमति देने से पहले होस्टनाम को हल (resolve) नहीं किया।
इसका मतलब था कि ssrf-repro.example जैसा होस्टनाम मान्यता के दौरान हानिरहित लग सकता था, फिर हल करने पर यह बन सकता था:
127.0.0.1169.254.169.254और फिर भी बैकएंड HTTP क्लाइंट द्वारा लाया जा सकता था।
वह समस्या CVE-2026-34207 बन गई।
Typebot: GitHub पर Typebot
CVE: CVE-2026-34207
में ठीक किया गया: 3.16.0
इसने Typebot को प्रभावित किया, जो एक व्यापक रूप से उपयोग किया जाने वाला ओपन-सोर्स चैटबॉट प्लेटफ़ॉर्म है। अपनी आधिकारिक साइट पर, Typebot को दुनिया भर की 650+ कंपनियों द्वारा विश्वसनीय बताया जाता है। साइट 2M+ मासिक चैट और 1.5M+ प्रकाशित बॉट का भी विज्ञापन करती है।
हमलावर-नियंत्रित Webhook URL -> होस्टनाम केवल शाब्दिक SSRF मान्यता पास करता है -> अनुमति निर्णय से पहले कोई DNS समाधान नहीं -> बैकएंड HTTP क्लाइंट होस्टनाम को आंतरिक लक्ष्य पर हल करता है -> सर्वर-साइड अनुरोध लूपबैक / मेटाडेटा / निजी नेटवर्क तक पहुँचता है -> प्रतिक्रिया डेटा निष्पादन लॉग के माध्यम से उपलब्ध होता है
Typebot एक चैटबॉट बिल्डर है।
यह उपयोगकर्ताओं को ऐसे फ़्लो बनाने देता है जो:
Webhook / HTTP Request ब्लॉक के माध्यम से आउटबाउंड HTTP अनुरोध ट्रिगर कर सकते हैंइसका मतलब है कि आउटबाउंड अनुरोध निष्पादन एक वास्तविक सुरक्षा सीमा है।
यहाँ महत्वपूर्ण प्रश्न यह नहीं था कि Typebot Webhook ब्लॉक का समर्थन करता है या नहीं।
असली प्रश्न था:
क्या SSRF सुरक्षा उस वास्तविक गंतव्य को मान्य करती है जिससे सर्वर कनेक्ट होगा, या केवल वह होस्टनाम टेक्स्ट जो URL में दिखाई देता है?
इस मामले में, इसने पहले केवल टेक्स्ट रूप को मान्य किया।
वह गलती थी।
SSRF रक्षा बहुत अनुमानित तरीकों से विफल होती है।
अधिकांश समय, दिलचस्प गलतियाँ ये नहीं हैं:
169.254.169.254 को ब्लॉक करना भूल गए"localhost को ब्लॉक करना भूल गए"मजबूत गलतियाँ सीमा गलतियाँ हैं:
यहाँ देखने के लिए यह सही जगह थी।
Typebot में पहले से ही शाब्दिक मेटाडेटा IP, लूपबैक, निजी रेंज और एन्कोडेड IP ट्रिक्स के लिए SSRF हार्डनिंग लॉजिक था।
इसने अगला प्रश्न स्पष्ट कर दिया:
क्या होगा अगर होस्टनाम शाब्दिक रूप से खतरनाक नहीं है, लेकिन बाद में एक खतरनाक गंतव्य पर हल होता है?
ठीक यही हुआ।
मूल समस्या थी होस्टनाम टेक्स्ट पर आधारित गंतव्य मान्यता, हल किए गए IP पते के बजाय।
कमजोर कार्यान्वयन में, validateHttpReqUrl():
http: और https: की अनुमति देता थाmetadata.google.internal, metadata.goog, metadata, और localhostवह महत्वपूर्ण भाग है।
यदि होस्टनाम एक सामान्य मान था जैसे:
ssrf-repro.example
तब parseIPAddress(hostname) ने null लौटाया, और मान्यकर्ता वहीं रुक गया।
अनुमोदन से पहले कोई DNS समाधान नहीं हुआ।
इसलिए कमजोर लॉजिक प्रभावी रूप से इस प्रकार था:
const ip = parseIPAddress(hostname);
if (ip) {
validateIPAddress(ip);
}
इसका मतलब है:
बग का दूसरा भाग निष्पादन पथ में था।
executeHttpRequest() में, Typebot ने पहले मान्यता चलाई और फिर बाद में ky(request.url, ...) के साथ वास्तविक अनुरोध किया।
इसलिए अनुक्रम था:
यह पूरी भेद्यता है।
महत्वपूर्ण अंतर यह है कि भरोसे का निर्णय कहाँ लिया गया।
यदि आप इसे बुरी तरह से वर्णित करते हैं तो बहुत से बग छोटे लगते हैं।
यदि आप इसे इस प्रकार वर्णित करते हैं:
"होस्टनाम फ़िल्टर अधूरा था"
यह एक गुणवत्ता समस्या जैसा लगता है।
यह वास्तविक समस्या नहीं है।
वास्तविक समस्या थी:
यह एक कॉस्मेटिक फ़िल्टरिंग कमजोरी नहीं है।
यह एक भरोसे-सीमा विफलता है।
और क्योंकि HTTP निष्पादक ने निष्पादन लॉग में प्रतिक्रिया डेटा रिकॉर्ड किया, समस्या सबसे मजबूत मामलों में भी अंधी नहीं थी।
इसलिए यह केवल यह नहीं था:
बल्कि:
यह एक वास्तविक SSRF भेद्यता है।
मैंने दो प्रूफ़ परतों का उपयोग किया क्योंकि उन्होंने दो अलग-अलग चीजों का प्रदर्शन किया।
पहले PoC ने मूल कारण को स्पष्ट रूप से अलग किया।
मैंने एक छोटे स्थानीय हार्नेस का उपयोग किया जो:
127.0.0.1 पर एक लूपबैक HTTP सर्वर शुरू कियाhttp://ssrf-repro.example:18080/... जैसे URL को मान्य कियाssrf-repro.example 127.0.0.1 पर हल होइसने सटीक दोष प्रदर्शित किया:
कैप्चर किए गए आउटपुट ने दिखाया:
इसने मान्यता अंतर को सीधे साबित किया।
दूसरे PoC ने उस वास्तविक सुविधा पथ के माध्यम से बग दिखाया जो मायने रखता है।
सबसे सरल पुनरुत्पादन था:
Webhook ब्लॉक बनाएं जो उस सौम्य होस्टनाम की ओर इशारा करता हैएक प्रतिनिधि ब्लॉक इस तरह दिखता था:
{
"id": "blk-webhook",
"type": "Webhook",
"options": {
"webhook": {
"method": "GET",
"url": "http://ssrf-repro.example:8000/"
}
}
}
एक होस्ट्स-फ़ाइल प्रविष्टि जैसे:
127.0.0.1 ssrf-repro.example
के साथ, मान्यकर्ता ने अभी भी URL स्वीकार किया क्योंकि:
ssrf-repro.example blockedHostnames में नहीं थाlocalhost नहीं थाparseIPAddress("ssrf-repro.example") ने null लौटायाफिर बैकएंड HTTP क्लाइंट ने होस्टनाम को 127.0.0.1 पर हल किया और फिर भी कनेक्ट किया।
इसने पूर्ण दावा स्थापित किया:
पहला PoC मूल कारण साबित करता है।
दूसरा PoC उत्पाद प्रभाव साबित करता है।