OpenSSL में हृदय की त्रुटियों का निदान। (अंतिम निदान अभी तक नहीं किया गया है, हालांकि उपचार पहले से ही जारी है)

अनुवादक की प्रस्तावना
इस लेख का अनुवाद शुरू करते हुए, मैंने यह माना कि इसके लेखक ने इस समस्या का पता लगाया।
हालाँकि, जैसा कि कुछ Habr उपयोगकर्ताओं ने सही तरीके से दिखाया है ( VBart के लिए धन्यवाद), सब कुछ इतना सरल नहीं है, और लेखक के मल्क, mmap और sbrk के उल्लेख ने उसे और भी अधिक भ्रमित कर दिया।
इस संबंध में, लेख तकनीकी से अधिक ऐतिहासिक हित का है।
अपडेट लेखक ने इस अनुवाद में टिप्पणियों में चर्चा के रूप में उसी पोस्ट में अपनी पोस्ट को अपडेट किया।


जब मैंने GnuTLS में त्रुटि के बारे में लिखा, तो मैंने कहा कि टीएलएस स्टैक में यह अंतिम भारी त्रुटि नहीं है जो हम देखेंगे। हालांकि, मुझे उम्मीद नहीं थी कि सब कुछ इतना घटिया होगा।

हार्टब्लेड में एक बग विशेष रूप से बुरा बग है। यह हमलावर को 64 केबी तक की मेमोरी पढ़ने की अनुमति देता है, और सुरक्षा शोधकर्ताओं का कहना है:

किसी भी गोपनीय जानकारी या क्रेडेंशियल के उपयोग के बिना, हम अपने X.509 प्रमाणपत्र, उपयोगकर्ता नाम और पासवर्ड, त्वरित संदेश, ईमेल और महत्वपूर्ण व्यावसायिक दस्तावेज़ और संचार के लिए उपयोग की जाने वाली गुप्त कुंजी को चोरी करने में सक्षम थे।


बग


यहाँ ssl / d1_both.c पर फिक्स शुरू होता है:

int dtls1_process_heartbeat(SSL *s) { unsigned char *p = &s->s3->rrec.data[0], *pl; unsigned short hbtype; unsigned int payload; unsigned int padding = 16; /* Use minimum padding */ 


तो, पहले हमें SSLv3 प्रविष्टि में डेटा के लिए एक पॉइंटर मिलता है, जो इस तरह दिखता है:
 typedef struct ssl3_record_st { int type; /* type of record */ unsigned int length; /* How many bytes available */ unsigned int off; /* read/write offset into 'buf' */ unsigned char *data; /* pointer to the record data */ unsigned char *input; /* where the decode bytes are */ unsigned char *comp; /* only used with decompression - malloc()ed */ unsigned long epoch; /* epoch number, needed by DTLS1 */ unsigned char seq_num[8]; /* sequence number, needed by DTLS1 */ } SSL3_RECORD; 


अभिलेखों का वर्णन करने वाली संरचना में प्रकार, लंबाई और डेटा शामिल हैं। Dtls1_process_heartbeat पर वापस:

 /* Read type and payload length first */ hbtype = *p++; n2s(p, payload); pl = p; 

अनुवादक का नोट: कोड n2s (c, s);
 #define n2s(c,s) ((s=(((unsigned int)(c[0]))<< 8)| \ (((unsigned int)(c[1])) )),c+=2) 



SSLv3 प्रविष्टि का पहला बाइट एक दिल की धड़कन प्रकार है। N2s मैक्रो पी से दो बाइट्स लेता है और उन्हें पेलोड में डालता है। यह वास्तव में उपयोगी डेटा की लंबाई है। कृपया ध्यान दें कि SSLv3 रिकॉर्ड में वास्तविक लंबाई की जाँच नहीं की गई है।
तब पीएल चर अनुरोधकर्ता द्वारा प्रदान किए गए "दिल की धड़कन" डेटा प्राप्त करता है।
निम्नलिखित कार्य में होता है:

 unsigned char *buffer, *bp; int r; /* Allocate memory for the response, size is 1 byte * message type, plus 2 bytes payload length, plus * payload, plus padding */ /*    ,   * 1    ,  2  -    , *   ,   */ buffer = OPENSSL_malloc(1 + 2 + payload + padding); bp = buffer; 


अनुरोध के रूप में अधिक मेमोरी आवंटित की जाती है: सटीक होने के लिए 65535 + 1 + 2 + 16 तक।
Bp वैरिएबल इस मेमोरी को एक्सेस करने के लिए इस्तेमाल किया जाने वाला पॉइंटर है। तो:

 /* Enter response type, length and copy payload */ *bp++ = TLS1_HB_RESPONSE; s2n(payload, bp); memcpy(bp, pl, payload); 

मेम्नेपी के बारे में अनुवादक का नोट
शीर्षक

memcpy - स्मृति क्षेत्र की प्रतिलिपि बनाएँ
SYNOPSIS

 #include <string.h> void *memcpy(void *dest, const void *src, size_t n); 

वर्णन

Memcpy () फ़ंक्शन src मेमोरी क्षेत्र से गंतव्य मेमोरी क्षेत्र में n बाइट्स की प्रतिलिपि बनाता है। स्मृति के क्षेत्र ओवरलैप नहीं हो सकते। यदि मेमोरी क्षेत्र ओवरलैप होते हैं तो मेमोव (3) का उपयोग करें।
रिटर्न्स वैल्यूज़

Memcpy () एक सूचक को नष्ट करने के लिए देता है।
मानक के साथ अनुपालन

एसवीआईडी ​​3, बीएसडी 4.3, आईएसओ 9899


S2n मैक्रो n2s मैक्रो के विपरीत करता है: 16-बिट मान लेता है और इसे दो बाइट्स में रखता है। फिर यह समान अनुरोधित पेलोड लंबाई निर्धारित करता है।
अनुवादक का नोट: कोड s2n (c, s);
 #define s2n(s,c) ((c[0]=(unsigned char)(((s)>> 8)&0xff), \ c[1]=(unsigned char)(((s) )&0xff)),c+=2) 



फिर, प्ले से पेलोड बाइट्स, उपयोगकर्ता द्वारा प्रदान किए गए डेटा, नए आवंटित बीपी सरणी में कॉपी किए जाते हैं। उसके बाद, यह सब उपयोगकर्ता को वापस भेज दिया जाता है।
तो गलती कहाँ हुई है?

उपयोगकर्ता पेलोड और pl को नियंत्रित करता है



क्या होगा यदि अनुरोधकर्ता ने वास्तव में पेलोड बाइट नहीं भेजा है, जैसा कि उसे करना चाहिए?
क्या होगा यदि pl वास्तव में केवल एक बाइट होता है?
तब मेमॉसी मेमोरी से सब कुछ पढ़ लेगा जो एसएसएलवी 3 रिकॉर्ड से दूर नहीं था।

और जाहिरा तौर पर पास में बहुत सारी चीजें हैं।

दो तरीके से मेमोरी आवंटित की जाती है गतिशील रूप से मॉलॉक ( लिनक्स पर कम से कम) का उपयोग करके: sbrk (2) और mmap (2) का उपयोग करके। यदि सिंक को आवंटित किया जाता है , तो पुराने हीप- ग्रो -अप नियमों का उपयोग किया जाता है, जो इसका उपयोग करते हुए पाया जा सकता है, हालांकि कई प्रश्नों (विशेषकर एक साथ) का उपयोग करके आप अभी भी कुछ दिलचस्प चीजें पा सकते हैं। [इस खंड में शुरू में PoC के बारे में मेरा संदेह था कि प्रकृति कैसे ढेर के माध्यम से काम करती है। हालांकि, कई पाठकों ने मुझे याद दिलाया कि mmap को इसके बजाय malloc में इस्तेमाल किया जा सकता है, और इससे सब कुछ बदल जाता है। धन्यवाद!]

लेखक से अपडेट - मूल लेख का यह हिस्सा हटा दिया गया है
हालांकि, अगर mmap का उपयोग किया जाता है, तो "सट्टेबाजी!" Mmap के लिए , किसी भी अप्रयुक्त मेमोरी को आवंटित किया जा सकता है। हार्टलेड के खिलाफ अधिकांश हमलों का लक्ष्य यही है।

और सबसे महत्वपूर्ण बात: आपका अनुरोधित ब्लॉक जितना बड़ा होगा, उतनी ही अधिक संभावना होगी कि यह mmap द्वारा सेवा की जाएगी, न कि sbrk

ऑपरेटिंग सिस्टम जो मॉलोक को लागू करने के लिए एमएमएपी का उपयोग नहीं करते हैं, वे सबसे कम संभावना वाले कमजोर हैं।


बीपी का स्थान वास्तव में बिल्कुल भी मायने नहीं रखता है। हालांकि, स्थान pl , का अत्यधिक महत्व है। इसके लिए मेमोरी लगभग निश्चित रूप से मॉलोक () में mmap सीमा के कारण sbrk () का उपयोग करके आवंटित की गई है। हालांकि, दिलचस्प सामग्री (उदाहरण के लिए, दस्तावेज़ या उपयोगकर्ता जानकारी) के लिए मेमोरी mmap () द्वारा आवंटित की जाने की संभावना है और इसे pl से एक्सेस किया जा सकता है। एक साथ कई प्रश्न कुछ दिलचस्प डेटा भी उपलब्ध कराएंगे।

तो इसका क्या मतलब है? खैर, pl के लिए मेमोरी एलोकेशन मॉडल हमारे लिए निर्धारित करते हैं कि हम क्या पढ़ सकते हैं। यहाँ भेद्यता के खोजकर्ताओं में से एक ने इस बारे में क्या कहा है:

हीप मेमोरी एलोकेशन मॉडल्स निजी महत्वपूर्ण समझौता करते हैं जो कि # हार्दिक # नोटबैनिक नहीं है।

- नील मेहता (@ neelmehta) 8 अप्रैल, 2014


सुधार


फिक्स का सबसे महत्वपूर्ण हिस्सा यह है:

 /* Read type and payload length first */ if (1 + 2 + 16 > s->s3->rrec.length) return 0; /* silently discard */ hbtype = *p++; n2s(p, payload); if (1 + 2 + payload + 16 > s->s3->rrec.length) return 0; /* silently discard per RFC 6520 sec. 4 */ pl = p; 


यह कोड दो काम करता है: पहला चेक शून्य लंबाई के "दिल की धड़कन" को रोकता है।
दूसरा, यदि यह सुनिश्चित करने के लिए कि वास्तविक रिकॉर्ड लंबाई पर्याप्त है, तो एक जांच करता है। वहां तुम जाओ।

सबक


इससे हम क्या सीख सकते हैं?

मैं सी फैन हूं। यह मेरी पहली प्रोग्रामिंग भाषा थी, और यह पहली भाषा थी जिसे मैं पेशेवर उद्देश्यों के लिए उपयोग कर रहा था। लेकिन अब मैं इसकी सीमाओं को पहले से अधिक स्पष्ट रूप से देखता हूं।

हार्दिक और गन्नट्लस बग के बाद, मुझे लगता है कि हमें तीन काम करने चाहिए:



यह देखते हुए कि सी में सुरक्षित रूप से लिखना कितना मुश्किल है, मुझे कोई अन्य विकल्प नहीं दिखता है।

Source: https://habr.com/ru/post/In218691/


All Articles