
Kubernetes CVE-2020-8558 के बारे में जानकारी, जिसमें प्रूफ ऑफ कॉन्सेप्ट एक्सप्लॉइट भी शामिल है।
CVE-2020-8558 एक Kubernetes भेद्यता है जिसे इसलिए प्रकाशित किया गया क्योंकि kube-proxy अप्रत्याशित रूप से localhost से बंधी होस्ट सेवाओं को नेटवर्क पर अन्य लोगों के लिए उपलब्ध कराता है। मैं "अप्रत्याशित रूप से" पर जोर इसलिए देता हूं क्योंकि यह भेद्यता एक डिज़ाइन दोष (उपेक्षा/चूक) के कारण है, न कि कार्यान्वयन दोष (बग) के कारण। कोड वही करता है जो वह कहता है, लेकिन हम सभी उस निर्णय के सुरक्षा निहितार्थों को पहचानने में विफल रहे।
होस्ट प्रक्रियाओं को 127.0.0.1 (localhost) पते के माध्यम से NodePort सेवाओं तक पहुंचने की अनुमति देने के लिए, kube-proxy net.ipv4.conf.all.route_localnet=1 sysctl सेटिंग सेट करता है। कर्नेल दस्तावेज़ीकरण के अनुसार, यह सेटिंग कर्नेल को "लूपबैक पतों को martian नहीं मानने" के लिए बनाती है — जिसका एक परिणाम यह है कि उन्हें नेटवर्क पर अन्य नोड्स द्वारा एक्सेस किया जा सकता है। यह एक बड़ी बात है यदि आपके पास संवेदनशील बिना-प्रमाणीकरण वाली सेवाएं हैं जिनकी एकमात्र सुरक्षा localhost से बंधा होना है!
इस लेखन के समय, Kubernetes समुदाय अभी भी CVE-2020-8558 को संबोधित करने का सबसे अच्छा तरीका खोज रहा है। दो स्पष्ट विकल्प हैं: पहले स्थान पर route_localnet sysctl सेट करना बंद करना, या iptables का उपयोग करके अनुचित रूप से रूट किए गए localnet पैकेटों को ब्लॉक करना। बाद की रणनीति का उपयोग करने वाला एक फिक्स पहले ही kubelet >= 1.18.4, 1.17.7, या 1.16.11 में जारी किया जा चुका है। आप इस CVE के लिए Kubernetes issue का संदर्भ लेकर इसे स्वयं भी लागू कर सकते हैं, जो नीचे लिंक किया गया है।
net.ipv4.conf.all.route_localnet=1 सेट करना एक CVE ID के योग्य क्यों है? मुख्य रूप से क्योंकि यह IP नेटवर्क के बारे में हमारे अंतर्ज्ञान का उल्लंघन करता है।
कम से कम 1989 के RFC 1122 के बाद से, localhost नेटवर्क 127.0.0.1/8 के पैकेटों के साथ विशेष व्यवहार किया जाता रहा है, और उन्हें "किसी होस्ट के बाहर दिखाई देने" से प्रतिबंधित किया गया है। (यदि आप 127/8 के विशेष गुणों के किसी पहले संदर्भ के बारे में जानते हैं तो कृपया मुझसे Twitter पर संपर्क करें।) किसी भी RFC-अनुरूप होस्ट में अनिवार्य रूप से एक अंतर्निहित, गैर-हटाने योग्य फ़ायरवॉल नियम होता है जो 127.0.0.1 (और उस नेटवर्क के अन्य IP — यदि आपने पहले कभी नहीं किया है तो 127.127.127.127 को ping करके देखें!) से बंधी सेवाओं तक बाहरी पहुंच को ब्लॉक करता है। हम उस व्यवहार पर निर्भर हो गए हैं और उसकी अपेक्षा करते हैं। हम अक्सर बिना प्रमाणीकरण या एन्क्रिप्शन के संवेदनशील सेवाएं चलाते हैं, और सुरक्षा के लिए उन्हें localhost से बांधते हैं। उदाहरण के लिए, plaintext HTTP बैकएंड, redis key-value स्टोर, और अवशिष्ट Kubernetes api-server insecure पोर्ट — ये सभी आमतौर पर इसी तरह घुसपैठ से सुरक्षित रहते हैं। हम इस व्यवहार के इतने आदी हैं कि यह हमारी सहज समझ का एक अंतर्निहित हिस्सा है कि IP होस्ट होने का क्या अर्थ है। इस दृष्टिकोण से देखें, तो यह समझ में आता है कि कई विशेषज्ञ इतने लंबे समय तक इस दोष को अनदेखा कर सकते थे।
यह कैसे काम करता है?
आइए किसी भी इकाई को IP पते वाली "नोड" कहें। IP पैकेट एक नोड से दूसरे नोड को भेजे जाते हैं, जिन्हें पैकेट हेडर में स्रोत और गंतव्य IP पते द्वारा पहचाना जाता है। प्रत्येक IP नोड या तो एक राउटर (RFC1122 में गेटवे कहा जाता है) या एक होस्ट होता है। प्राथमिक अंतर यह है कि जब कोई होस्ट किसी और के पते के लिए नियत पैकेट प्राप्त करता है, तो वह उन्हें अनदेखा कर देता है। एक राउटर अपनी रूटिंग टेबल देखता है और पैकेटों को उनके अंतिम गंतव्य के करीब लाने के प्रयास में उन्हें पुनः प्रसारित (फॉरवर्ड) करता है। एक होस्ट कुछ स्थानीय रूप से जुड़े नोड्स के बारे में जानता होगा; अन्य नोड्स तक पहुंचने के लिए, उसे अपने पैकेट किसी स्थानीय रूप से जुड़े राउटर को भेजने होंगे। वे स्थानीय कनेक्शन point-to-point (जैसे PPP लिंक या कुछ वर्चुअल नेटवर्क) या shared-medium (जैसे Ethernet) हो सकते हैं।
आपके मेलबॉक्स की कल्पना आपके घर और स्थानीय डाकघर के बीच एक point-to-point लिंक के रूप में की जा सकती है। किसी point-to-point लिंक पर पैकेट को रूट करने के लिए, होस्ट को केवल सही गंतव्य पता लगाना और पैकेट को प्रसारित करना होता है। (यह OSI मॉडल की लेयर 3 पर होता है।) किसी shared-medium लिंक पर पैकेट को रूट करने के लिए, होस्ट को पहले साझा माध्यम पर एक वर्चुअल point-to-point सर्किट बनाना होता है। Ethernet/IP नेटवर्क में, यह OSI मॉडल की लेयर 2 पर ARP द्वारा किया जाता है। अनिवार्य रूप से, यदि आप एक ARP पैकेट प्रसारित कर सकते हैं, तो आप दूसरे होस्ट से कह सकते हैं "अरे, मैं यहाँ हूँ" और वह आप पर विश्वास करेगा। (जब यह अनुचित तरीके से किया जाता है तो इसे ARP कैश पॉइज़निंग कहा जाता है।) फिर आप अपने पैकेटों पर उपयुक्त Ethernet स्रोत और गंतव्य पते लगाकर संवाद कर सकते हैं।
एक सामान्य नोड कभी भी 127.0.0.1 के गंतव्य पते वाला पैकेट प्रसारित नहीं करेगा, क्योंकि RFC 1122 के कारण। यदि किसी सामान्य नोड को 127.0.0.1 के गंतव्य पते वाला पैकेट प्राप्त होता है, तो वह उसे अनदेखा (ड्रॉप) कर देगा, फिर से RFC 1122 के कारण। net.ipv4.conf.all.route_localnet=1 सेट करना उसे बदल देता है — यह 127.0.0.1 पैकेटों को भेजने और प्राप्त करने की अनुमति देता है जैसे कि वे विशेष न हों।
इसलिए, यदि किसी हमलावर के पास net.ipv4.conf.all.route_localnet=1 वाले लक्ष्य नोड से स्थानीय कनेक्शन है, तो हमलावर उसे 127.0.0.1 गंतव्य पते वाला पैकेट भेज सकता है, और वह लक्ष्य नोड उसी के अनुसार प्रतिक्रिया देगा जैसे कि 127.0.0.1 पूरी तरह से सामान्य पता हो। आज किसी लक्ष्य नोड से स्थानीय कनेक्शन रखने के दो सबसे सामान्य तरीके हैं: लक्ष्य के समान Ethernet नेटवर्क (ब्रॉडकास्ट डोमेन) पर होना, या लक्ष्य पर चल रहा कंटेनर होना।
ध्यान दें कि सामान्य रूप से कॉन्फ़िगर होने पर, Linux हमलावर नोड को 127.0.0.1 के लिए नियत सामान्य पैकेट प्रसारित करने की अनुमति नहीं देगा। इसे हमलावर के Linux नोड को पुनः कॉन्फ़िगर करके (यदि उनके पास रूट एक्सेस है), या raw सॉकेट का उपयोग करके पैकेट जालसाज़ी करके दूर किया जा सकता है। Raw सॉकेटों के लिए केवल Linux कर्नेल क्षमता CAP_NET_RAW की आवश्यकता होती है, जो डिफ़ॉल्ट रूप से गैर-विशेषाधिकार प्राप्त (unprivileged) कंटेनरों को दी जाती है। इसका मतलब है कि एक हमलावर-नियंत्रित गैर-विशेषाधिकार प्राप्त कंटेनर CVE-2020-8558 का शोषण करने में सक्षम है।
संक्षेप में, यदि आप kube-proxy का उपयोग कर रहे हैं या net.ipv4.conf.*.route_localnet के साथ कुछ चतुर काम कर रहे हैं, तो आप उजागर हैं। आपको यह निर्धारित करने के लिए कुछ समय थ्रेट मॉडलिंग में बिताना चाहिए कि वह जोखिम आपके लिए कितना जोखिम भरा है, और एक उपयुक्त शमन रणनीति की योजना बनानी चाहिए।
मूल रूप से, net.ipv4.conf.all.route_localnet=1 सेट वाला प्रत्येक Linux होस्ट भेद्य है। वह भेद्यता किसी हमलावर के लिए दिलचस्प है या नहीं, यह कई कारकों पर निर्भर करता है:
CVE-2020-8558 का मूल्यांकन करने के लिए, आपको विभिन्न क्षमताओं वाले हमलावरों की कल्पना करनी होगी, और इन सवालों का उत्तर उन हमलावरों के दृष्टिकोण से देना होगा। (एडम शॉस्टैक की पुस्तक "Threat Modeling: Designing for Security" इस प्रक्रिया का विस्तार से वर्णन करती है।) दो प्रासंगिक हमलावर जिन पर आपको निश्चित रूप से विचार करना चाहिए: एक हमलावर जिसका नोड आपके Ethernet नेटवर्क पर है, और एक हमलावर जो आपके होस्ट पर गैर-विशेषाधिकार प्राप्त पॉड में कोड चला सकता है। आपके वातावरण और आवश्यकताओं के आधार पर, अन्य दिलचस्प हमलावर भी हो सकते हैं जिन पर आपको विचार करना चाहिए।
उदाहरण के लिए, यहाँ एक आंशिक रूप से हल किया गया उदाहरण है:
होस्ट निश्चित रूप से दोनों हमलावरों के लिए सुलभ है; हमने प्रत्येक मामले में यह मान लिया है।
पैकेट फ़िल्टर किए जा सकते हैं या नहीं भी। आपको जांच करनी होगी। कई क्लाउड वातावरणों और सख्ती से प्रबंधित on-prem नेटवर्कों में, पैकेट तब ब्लॉक किए जाते हैं जब IP गंतव्य नेटवर्क द्वारा अपेक्षित Ethernet गंतव्य से मेल नहीं खाता। यह अकेले ही नोड-वाले हमलावर के लिए game-over हो सकता है। यदि आपके सभी नोड्स में उपयुक्त स्थानीय फ़ायरवॉल नियम हैं (जैसे कि अपडेटेड kubelet द्वारा प्रदान किए गए), तो यह दोनों हमलावरों को विफल कर देगा।
आपके विचार से अधिक दिलचस्प सेवाएं होने की संभावना है। जाहिर है, Kubernetes api-server insecure पोर्ट एक अत्यंत आकर्षक लक्ष्य है, और यदि आप कर सकते हैं तो आपको इसे अक्षम कर देना चाहिए। 127.0.0.0/8 नेटवर्क में IP पतों से बंधी सभी प्रक्रियाओं की जांच करें: क्या उनके पास मजबूत प्रमाणीकरण है? यदि नहीं, तो वे CVE-2020-8558 के माध्यम से उजागर हो सकती हैं। भले ही आपकी सभी सामान्य localhost सेवाएं सुरक्षित हों, अस्थायी (ephemeral) सेवाएं भी चिंता का विषय हो सकती हैं। उदाहरण के लिए, SSH पोर्ट फॉरवर्डिंग का उपयोग अक्सर अस्थायी, अधिकृत उद्देश्यों के लिए नेटवर्क प्रतिबंधों को बायपास करने के लिए किया जाता है। डिफ़ॉल्ट रूप से, SSH-फॉरवर्डेड पोर्ट localhost से बंधे होते हैं ताकि अस्थायी पहुंच केवल अधिकृत उपयोगकर्ताओं के लिए अनुमत हो। CVE-2020-8558 के साथ, वे "सुरक्षित" पोर्ट-फॉरवर्ड आपके हमलावरों के लिए भी उपलब्ध हैं।
यह मानते हुए कि आपके पास लक्ष्य के समान ब्रॉडकास्ट डोमेन पर स्थित Linux बॉक्स पर रूट है, निम्नलिखित कॉन्फ़िगरेशन सेटिंग्स आपको CVE-2020-8558 का शोषण करने की अनुमति देंगी:
ip addr add 127.0.0.2/8 dev lo
ip addr del 127.0.0.1/8 dev lo
ip route add 127.0.0.1/32 via YOUR-TARGET-HERE
sysctl net.ipv4.conf.all.route_localnet=1
क्योंकि कुछ महत्वपूर्ण सेवाएं (खांसी, खांसी, systemd-resolved) 127.0.0.0/8 पते पर चलती हैं, हम होस्ट को तोड़ने से रोकने के लिए एक नया पता जोड़ते हैं। फिर, होस्ट को अपने डिफ़ॉल्ट 127.0.0.1/8 पते के बारे में भूल जाना होगा। इसके बाद, हम कर्नेल को 127.0.0.1 के लिए ट्रैफ़िक को वायर पर आपके लक्ष्य तक रूट करने का निर्देश देते हैं, जो 127.0.0.1 तक पहुंचना जानता है। अंत में, हम कुख्यात sysctl सेट करते हैं जो अन्यथा इस कॉन्फ़िगरेशन को काम करने से रोक देगा।
CVE-2020-8558 के लिए परीक्षण करने हेतु raw पैकेट भेजने वाली सरल Python स्क्रिप्ट। यह एक scapy वन-लाइनर हो सकती थी, लेकिन मैं घर की थोड़ी और सुविधाएं जोड़ना चाहता था। यह आपके लक्ष्य के माध्यम से 127.0.0.1 को एक पैकेट भेजती है, और देखती है कि कोई उत्तर मिलता है या नहीं।
CVE-2020-8558 का शोषण करने वाली Python स्क्रिप्ट, जो सामान्य TCP या UDP क्लाइंट अनुप्रयोगों को जाली (forged) पैकेटों के माध्यम से दूरस्थ localhost IP के साथ संवाद करने में सक्षम बनाती है। इस स्क्रिप्ट को चलाएं, फिर अपने फेक गंतव्य (fakedestination) (डिफ़ॉल्ट रूप से 198.51.100.1) से कनेक्ट करने के लिए किसी भी सामान्य TCP या UDP क्लाइंट (जैसे kubectl या nc) का उपयोग करें।
ध्यान दें कि फेक गंतव्य को एक ऐसा IP पता होना चाहिए जो कभी भी पैकेटों का जवाब नहीं देता है, और उसके लिए आपका रूट उसी इंटरफ़ेस पर होना चाहिए जिसके माध्यम से आप अपने लक्ष्य तक पहुंचते हैं। सामान्य मामले में, फेक गंतव्य और लक्ष्य दोनों आपके डिफ़ॉल्ट गेटवे इंटरफ़ेस के माध्यम से सुलभ होंगे, और यह कोई बड़ी बात नहीं होगी।
क्योंकि यह स्क्रिप्ट "localhost" पैकेट भेजने और प्राप्त करने के लिए raw सॉकेट का उपयोग करती है, यह एक सामान्य गैर-विशेषाधिकार प्राप्त कंटेनर के अंदर ठीक काम करती है।
इस CVE के लिए GitHub पर Kubernetes issue
कर्नेल IP sysctl दस्तावेज़ीकरण
इयान कोल्डवाटर, ब्रैड गीसमैन, डफी कूली और लॉरेंट बर्नैल को विशेष धन्यवाद। विचारों, सलाह और हँसी-मज़ाक के लिए धन्यवाद, आप सभी। Honk the planet!