
CVE-2022-3602 के लिए तकनीकी गहन अध्ययन और एंटी-पीओसी, जो OpenSSL 3.0.x में एक पुनिकोड बफर ओवरफ्लो है, प्रतिकृति स्क्रिप्ट, स्टैक विश्लेषण और कंपाइलर शमन मूल्यांकन के साथ।
यह दस्तावेज़ और रिपॉजिटरी CVE−2022-3602 का एक राइट-अप है, जो OpenSSL में एक punycode बफर ओवरफ्लो समस्या है। यह एक "एंटी-पीओसी" है (समस्या शोषण योग्य प्रतीत नहीं होती) जो उन लोगों के लिए है जो अपने स्वयं के OpenSSL बिल्ड का रखरखाव करते हैं और कंपाइलर रखरखावकर्ताओं के लिए।
उसी रिलीज़ में एक अलग CVE, CVE-2022-3786 भी है, जो बफर ओवरफ्लो की ओर ले जाता है, लेकिन उस स्थिति में हमलावर सामग्री को नियंत्रित नहीं कर सकता। यहाँ उस समस्या के लिए कोई प्रतिकृति नहीं है, लेकिन वह समस्या क्रैश के कारण सेवा अस्वीकृति (DoS) का कारण बन सकती है।
क्रैश और बफर ओवरफ्लो कभी भी अच्छे नहीं होते हैं और यदि आप OpenSSL 3.0.x का उपयोग कर रहे हैं, तो जल्द से जल्द अपडेट करना उचित है।
कृपया GitHub issues या pull-requests के माध्यम से किसी भी त्रुटि या चूक की रिपोर्ट करने में संकोच न करें।
ossl_punycode_decode में punycode डिकोडिंग को संभालने के तरीके में एक ऑफ-बाय-वन समस्या है जिसके परिणामस्वरूप 4-बाइट का ओवरफ्लो होता है। यह समस्या केवल तभी उचित है जब OpenSSL एक प्रमाणपत्र श्रृंखला को संसाधित करता है और इसके लिए दो शर्तों की आवश्यकता होती है। पहली, श्रृंखला में एक CA या इंटरमीडियरी प्रमाणपत्र में एक name-constraint फ़ील्ड होना चाहिए जो punycode का उपयोग करता है।
nameConstraints = permitted;email:xn-maccrthaigh-n7a.com
दूसरी, लीफ प्रमाणपत्र में एक SubjectAlternateName (SAN) otherName फ़ील्ड होना चाहिए जो एक SmtpUTF8Mailbox स्ट्रिंग निर्दिष्ट करता है।
otherName = 1.3.6.1.5.5.7.8.9;UTF8:[email protected]
ट्रिगर होने पर, nameConstraints फ़ील्ड में punycode, लेकिन otherName फ़ील्ड में punycode नहीं, कमजोर OpenSSL punycode पार्सिंग द्वारा संसाधित किया जाएगा।
David Benjamin और Matt Caswell ने निर्धारित किया कि nameConstraint जाँच सामान्य प्रमाणपत्र श्रृंखला सत्यापन और हस्ताक्षर सत्यापन के बाद होती है। अधिकांश अनुप्रयोगों के लिए इसका मतलब है कि समस्या को स्व-हस्ताक्षरित प्रमाणपत्र या अमान्य श्रृंखला के साथ ट्रिगर नहीं किया जा सकता है।
ध्यान दें कि openssl के s_client और s_server एप्लिकेशन डिबगिंग के लिए हैं और श्रृंखला के अमान्य होने पर प्रसंस्करण बंद नहीं करते हैं।
एक विश्वसनीय CA या इंटरमीडिएट में दुर्भावनापूर्ण पेलोड होना चाहिए, और उसे लीफ प्रमाणपत्र पर हस्ताक्षर भी करना होगा जो समस्या को ट्रिगर करता है।
कुछ वातावरण ऐसे हो सकते हैं जहाँ अविश्वसनीय पक्ष CA या इंटरमीडिएरी हैं, उदाहरण के लिए एक होस्टिंग सेवा जो ग्राहक-प्रदत्त प्राइवेट CA का समर्थन करती है, लेकिन यह सामान्य नहीं है।
कई अनुप्रयोगों के लिए उत्तर "नहीं" होगा क्योंकि कंपाइलर ने स्टैक को कैसे व्यवस्थित किया है और स्टैक कैनरी / स्टैक कुकीज़, पैडिंग, PIE, FORTIFY_SOURCE जैसी अन्य सुरक्षाओं की उपस्थिति के कारण।
समस्या वास्तव में स्टैक पर 32-बिट के ओवरफ्लो की ओर ले जाती है। यह सीधे शेल कोड निष्पादित करने के लिए पर्याप्त नहीं है, लेकिन यह किसी एप्लिकेशन के नियंत्रण प्रवाह को बदलने के लिए पर्याप्त हो सकता है। उदाहरण के लिए, यदि यह डेटा (या इस डेटा की कॉपी किए गए टुकड़े) एक निष्पादन योग्य स्थान पर स्टैक पर कॉपी किया जाता है, तो X509 प्रमाणपत्र श्रृंखला में एम्बेडेड शेल-कोड पर कूदना संभव हो सकता है।
हर Linux प्लेटफ़ॉर्म पर जिसका मैंने परीक्षण किया, ओवरफ्लो पैडिंग में होता है और हानिरहित है। सिद्धांत रूप में, एक कंपाइलर चर को इस तरह व्यवस्थित कर सकता है कि ओवरफ्लो ossl_a2ulabel फ़ंक्शन के अन्य चरों में से एक में हो।
इनलाइनिंग के आधार पर मौजूद चरों की पूरी सूची है:
outptr, inptr, size, result, tmpptr, delta, seed, utfsize
और मुझे ऐसा लगता है कि उनमें से कोई भी विशेषाधिकार वृद्धि या दिलचस्प नियंत्रण के लिए स्पष्ट मार्ग प्रदान नहीं करता है।
मैंने एक टारबॉल संलग्न किया है जिसमें ऐसे उपकरण हैं जिनका उपयोग सभी चार बाइट्स पर जितना संभव हो उतना नियंत्रण के साथ प्रतिकृतियां और ओवरफ्लो बनाने के लिए किया जा सकता है। संदर्भ प्रतिकृति स्ट्रिंग (xn--ww90271...aaaa) चार बाइट्स को मानों 0xFF 0x0F 0x0F 0x0F के साथ ओवरफ्लो करती है। यदि वह किसी एप्लिकेशन को क्रैश नहीं करता है, तो संभव है (संभावित?) कि वह एप्लिकेशन असुरक्षित नहीं है।
शेल स्क्रिप्ट run-poc का उपयोग एक दुर्भावनापूर्ण प्रमाणपत्र श्रृंखला उत्पन्न करने के लिए किया जा सकता है। ca.cnf से एक दुर्भावनापूर्ण CA प्रमाणपत्र और leaf.cnf से एक ट्रिगरिंग लीफ प्रमाणपत्र उत्पन्न होता है।
CA प्रमाणपत्र निम्नलिखित संदर्भ पेलोड का उपयोग करता है:
xn--ww902716aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
एक python स्क्रिप्ट का उपयोग विभिन्न पेलोड के लिए अन्य punycode स्ट्रिंग उत्पन्न करने के लिए किया जा सकता है।
जब निष्पादित किया जाता है, run-poc एक openssl क्लाइंट और सर्वर चलाएगा और दस बार समस्या का शोषण करने का प्रयास करेगा।
एक कमजोर OpenSSL संभवतः क्रैश हो जाएगा। इसका मतलब यह नहीं है कि OpenSSL का संस्करण RCE के लिए कमजोर है, क्योंकि स्टैक कैनरी और स्टैक कुकी सुरक्षा भी आमतौर पर एक (सुरक्षित) एप्लिकेशन क्रैश का कारण बनती हैं। यह भी ध्यान दें कि यह किसी भी तरह से उसी रिलीज़ में दूसरे CVE की गंभीरता को नहीं बदलता है।
सभी चार ओवरफ्लो बाइट्स पर लगभग पूर्ण नियंत्रण प्राप्त करना आश्चर्यजनक रूप से सूक्ष्म है और इसके लिए OpenSSL के punycode डिकोडर का गैर-मानक / अमान्य punycode के साथ शोषण करना आवश्यक है। संलग्न टारबॉल में एक स्क्रिप्ट है जो सूक्ष्मता को संभालने वाली एक स्ट्रिंग का निर्माण कर सकती है। नीचे बताया गया है कि यह कैसे काम करता है।
सुरक्षा समस्या ossl_punycode_decode() में है
int ossl_punycode_decode(const char *pEncoded, const size_t enc_len,
unsigned int *pDecoded, unsigned int *pout_length)
ossl_punycode_decode को ossl_a2ulabel से बुलाया जाता है। pEncoded बफर अधिक या कम मनमाने आकार का बफर है जो X509 प्रमाणपत्र श्रृंखला से आता है। यह nameConstraint फ़ील्ड में किसी भी "xn--" के बाद आने वाला भाग है। ऐसी प्रमाणपत्र श्रृंखला को पुन: उत्पन्न करने के तरीके के लिए [reproduction] देखें।
pDecoded unsigned int की LABEL_BUF_SIZE आकार की एक सरणी है। LABEL_BUF_SIZE 512 है, और अधिकांश प्लेटफार्मों पर एक unsigned int 4 बाइट चौड़ा होगा। तो अधिकांश प्लेटफार्मों पर pDecoded 2048 बाइट लंबा है।
ossl_punycode_decode() के अंदर समस्या का मूल यह गलत लंबाई जाँच है:
if (written_out > max_out)
max_out *pout_length से मेल खाता है जो हमेशा 512 होता है। और written_out ट्रैक रखता है कि pDecoded में कितने unsigned int लिखे गए हैं। क्योंकि written_out बाद में केवल लिखने के बाद बढ़ाया जाता है, यह दोषपूर्ण जाँच 513 unsigned int को pDecoded में लिखने की अनुमति देती है। अंतिम परिणाम कुछ इस प्रकार दिखता है ...
pDecoded = [ ... , 'X , 'Y' , 'Z' ] 'P'
// Indices 509 510 511
यहाँ, C के सम्मेलन के अनुसार, सूचकांक शून्य-आधारित हैं, इसलिए स्लॉट नंबर 511 सरणी का 512वाँ तत्व है। 'P' एक चार-बाइट पेलोड है जिसे सीमा से बाहर रखा गया है, ossl_a2ulabel() में buf बफर के लिए स्टैक पर आवंटित स्थान से परे, जो कि pDecoded इंगित करता है।
चार बाइट एक छोटा ओवरफ्लो है, और एक nop-sled ले जाने या सीधे शेल कोड निष्पादित करने के लिए पर्याप्त नहीं है, लेकिन किसी एप्लिकेशन के नियंत्रण प्रवाह को बदलने के लिए पर्याप्त है। उदाहरण के लिए, x509 प्रमाणपत्र श्रृंखला में एम्बेडेड शेल-कोड पर कूदना संभव हो सकता है, यह इस बात पर निर्भर करता है कि यह डेटा (या इस डेटा की कॉपी किए गए टुकड़े) कैसे संग्रहीत किया जाता है और क्या वह मेमोरी निष्पादन योग्य है। हालांकि, एक संभावित हमलावर के लिए अभी भी अधिक कठिनाई है।
पहला, कंपाइलर का पैडिंग और स्टैक का संरेखण, या स्टैक कैनरी जैसी सुरक्षा, किसी भी शोषण को पूरी तरह से असंभव बना सकती है।
दूसरा, ossl_punycode_decode() के लिए केवल एक ही रास्ता है और यह रास्ता स्टैक पर एक बफर का उपयोग करता है। यह संभावना नहीं बनाता है कि समस्या का उपयोग अलग-अलग मेमोरी स्थानों में एक साथ 4 बाइट ओवरफ्लो के लिए किया जा सकता है।
Punycode स्ट्रिंग्स मूल रूप से दो रूपों में होती हैं। एक xn--c1yn36f (點看) है और दूसरा xn--maccrthaigh-n7a (maccárthaigh) है। अंतिम - सीमांकक के बाद आने वाला भाग किसी भी यूनिकोड कोड-पॉइंट का 36-ary bootstring एन्कोडिंग है जो मूल साधारण ascii नहीं हैं, साथ ही उन्हें सम्मिलित करने के लिए स्ट्रिंग स्थिति है। अभी के लिए महत्वपूर्ण यह है कि ossl_punycode_decode() में डिकोडिंग प्रक्रिया दो मान उत्पन्न करती है। एक 'n' है जो सम्मिलित किया जाने वाला unsigned int कोड-पॉइंट मान है, और दूसरा 'i' है जो इसे सम्मिलित करने के लिए बफर में स्थिति है।
लेखन दो अलग-अलग तरीकों से हो सकता है। यदि i स्ट्रिंग के बीच में कहीं है, तो एक memmove() होता है जो पहले सब कुछ एक स्लॉट दाईं ओर कॉपी करके "जगह बनाता है":
memmove(pDecoded + i + 1, pDecoded + i,
(written_out - i) * sizeof *pDecoded);
और फिर यह अभी बनाई गई जगह पर n लिखता है:
pDecoded[i] = n;
यदि i स्ट्रिंग के अंत में है, तो memmove() का कोई प्रभाव नहीं होता क्योंकि अंतिम पैरामीटर 0 होगा। दूसरी पंक्ति एक साधारण append बन जाती है।
अब हम पेलोड 'P' को ओवरफ्लो स्थिति में लाने के तीन अलग-अलग तरीकों को देखेंगे और ये बाधाएँ क्यों उत्पन्न होती हैं।
ओवरफ्लो को ट्रिगर करने का सबसे सरल तरीका एक punycode स्ट्रिंग तैयार करना है जिसमें 511 ascii वर्ण हों, और दो गैर-ascii वर्ण हों। "ÁÁAAAAAAAA...AAA" जैसी 513-वर्ण लंबी स्ट्रिंग का punycode एन्कोडिंग ऐसा करेगा। इस मामले में क्या होगा जब written_out 510 है, हमारे पास बफर इस प्रकार व्यवस्थित होगा ...
pDecoded = [ 'A' , 'A' , ... , 'A' , 'A', ]
// Indices 0 1 ... 509 510 511
यह केवल मूल ascii वर्ण हैं जिन्हें कॉपी किया गया है। फिर हम punycode bootstring पार्स करते हैं और स्थिति 0 पर एक 'Á' सम्मिलित करते हैं। यद्यपि यह 0 और 511 के बीच कोई भी स्थिति हो सकती है।
pDecoded = [ 'Á' , 'A' , ... , 'A' , 'A', 'A' ]
// Indices 0 1 ... 509 510 511
हम इसे दोहराते हैं:
pDecoded = [ 'Á' , 'Á' , ... , 'A' , 'A', 'A' ] 'A'
// Indices 0 1 ... 509 510 511 512
इससे साधारण ascii 'A' ओवरफ्लो हो जाएगा क्योंकि इसे "स्थानांतरित" किया जा रहा है। इस मामले में चार-बाइट पेलोड 0x00 0x00 0x00 0x41 बन जाता है। जैसा कि हम देखेंगे, punycode कैसे काम करता है, यह एकमात्र तरीका है जिससे ascii श्रेणी में अंतिम बाइट वाले किसी भी मान को व्यक्त किया जा सकता है।
हमें दो गैर-ascii वर्णों का उपयोग करने की आवश्यकता है क्योंकि मूल वर्णों की संख्या पर एक सही सीमा जाँच है, इसलिए यह 512 से कम होना चाहिए।
एक अतिरिक्त बाधा यह है कि अंतिम बाइट मान 46 नहीं हो सकता, क्योंकि ossl_punycode_decode() को एक स्ट्रिंग के उस भाग पर बुलाया जाता है जो शाब्दिक . वर्ण से पहले आता है। Punycode डोमेन लेबल के लिए है, जिनमें डॉट नहीं हो सकते।
ओवरफ्लो को ट्रिगर करने का अगला सबसे सरल तरीका अंत में एक गैर-ascii वर्ण वाली 513-वर्ण स्ट्रिंग तैयार करना है। जैसे "AAAAAAAAAA...AAÁ"। इस मामले में हमारे अंतिम दो चरणों के लिए हमारे पास होगा:
pDecoded = [ 'A' , 'A' , ... , 'A' , 'A' ] // Indices 0 1 ... 510 511
और
pDecoded = [ 'A' , 'A' , ... , 'A' , 'A' ] 'Á'
// Indices 0 1 ... 510 511
गैर-ascii वर्ण सीधे ओवरफ्लो स्थिति में जाएगा। OpenSSL punycode पार्सर यह लागू नहीं करता है कि यहाँ ओवरफ्लो मान वास्तव में एक मान्य यूनिकोड वर्ण है। यह कमोबेश एक बाइनरी डिकोडिंग प्रक्रिया है। लेकिन punycode डिकोडिंग की बारीकियों का मतलब है कि विधि 2 उतनी लचीली नहीं है जितनी पहली नज़र में लगती है।
Punycode में मान n और i दोनों को एक एकल चर-लंबाई पूर्णांक के रूप में एन्कोड किया जाता है जिसे फिर base36 का उपयोग करके ascii में एन्कोड किया जाता है। दो असंबंधित संख्याओं को एक एकल पूर्णांक के रूप में एन्कोड करना असंभव लग सकता है, लेकिन punycode की चालाक चाल स्ट्रिंग की लंबाई (अब तक) को एक छिपे हुए क्षेत्र के रूप में उपयोग करना है।
उदाहरण के लिए, मान लीजिए कि हमारे पास 4 मूल वर्णों वाली एक punycode स्ट्रिंग है, और एक गैर-मूल, जैसे AAÁAA। इसे पहले केवल मूल वर्णों ... AAAA के रूप में दर्शाया जाएगा। 'Á' यूनिकोड मान 225 है और स्ट्रिंग में इसकी स्थिति 2 है। चाल यह है कि मान को लंबाई प्लस एक से गुणा करें, और फिर स्थिति जोड़ें। तो यह ((225 * (4 +1)) + 2) = 1127 हो जाता है, और इसी तरह इसे एन्कोड किया जाता है (चर-लंबाई base36 में)।
डिकोड करने के लिए, आप दूसरे तरीके से जाते हैं। 1127 / 5 = 225 और 1127 % 5 = 2। इस प्रकार आप एक संख्या से दो संख्याएँ प्राप्त करते हैं। लेकिन ध्यान दें कि स्ट्रिंग जितनी लंबी होगी, मान कितना बड़ा हो सकता है, इस पर आप उतने ही अधिक बाधित होंगे, अन्यथा गुणक unsigned int में फिट नहीं होगा। सामान्य तौर पर, यदि स्ट्रिंग M वर्ण लंबी है, तो आप मान से log M बिट चौड़ाई खो देते हैं।
जब तक आप 512वें पूर्णांक को संभाल रहे हैं, आप 9 बिट चौड़ाई खो देते हैं। विधि 2 का उपयोग करते हुए, प्रतीत होता है 32-बिट पेलोड का उच्चतम मान वास्तव में 2^23 है। तीन पूर्ण बाइट्स भी नहीं। विधि 2 उप-इष्टतम है।
4 बाइट्स का नियंत्रण वापस पाने का सबसे कुशल साधन पेलोड वर्ण को बार-बार दोहराना है। अब तक मैंने punycode को संभालने के दो अन्य प्रासंगिक विवरण छोड़े हैं।
पहला विवरण यह है कि गैर-ascii वर्ण स्ट्रिंग क्रम में एन्कोड नहीं होते हैं, बल्कि मान के आरोही क्रम में एन्कोड होते हैं। स्ट्रिंग "ÉÁ" को "Á at position 1, É at position 0" के रूप में एन्कोड किया जाएगा, क्योंकि Á का मान (225) É (233) से कम है।
दूसरा विवरण यह है कि गैर-ascii वर्णों को उनके शाब्दिक मानों के रूप में एन्कोड नहीं किया जाता है, बल्कि सबसे हाल ही में डिकोड किए गए मान के सापेक्ष डेल्टा के रूप में एन्कोड किया जाता है। चूंकि पहले मान के लिए कोई पिछला मान नहीं है, इसलिए 128 का एक हार्ड-कोडेड प्रारंभिक बिंदु है।
ये छोटी बारीकियां punycode को बहुत स्थान-कुशल बनाती हैं, लेकिन इसका यह भी अर्थ है कि एक गैर-ascii वर्ण को 128 से कम मान में डिकोड नहीं किया जा सकता है। सबसे छोटा डेल्टा 0 है, और ऋणात्मक डेल्टा व्यक्त करने का कोई तरीका नहीं है। इसलिए यदि आप 128 से कम संख्या चाहते हैं, तो आपको विधि 1 का उपयोग करना होगा।
इसका यह भी अर्थ है कि पेलोड पर जितना संभव हो उतना नियंत्रण के लिए सबसे अच्छी रणनीति पेलोड को पूर्ण स्ट्रिंग में एकमात्र मान बनाना है, क्योंकि इस तरह हमें एन्कोडिंग में 0वें स्थान से काम करने के लिए पूरी चौड़ाई मिलती है। जिस स्ट्रिंग को आप एन्कोड करते हैं वह इस प्रकार दिखती है:
[ 'P', 'P', ... 'P', 'P', 'P' ]
0 1 510 511 512
जिसे OpenSSL द्वारा इस प्रकार डिकोड किया जाएगा ...
pDecoded = [ 'P', 'P', ... 'P', 'P' ] 'P'
0 1 510 511 512
जहाँ P ओवरफ्लो स्थिति में है, और 128 और (2^32 - 1) के बीच किसी भी मान का प्रतिनिधित्व करने में सक्षम है।
इन सबके लिए एक गैर-मानक punycode एन्कोडर की आवश्यकता है और मैंने एक स्क्रिप्ट शामिल की है जो आवश्यकतानुसार विधि 1 या विधि 3 का उपयोग करके पेलोड तैयार कर सकती है।
OpenSSL को अपडेट करने के अलावा, क्या अन्य शमन उपाय हैं?
अधिकांश वातावरणों में प्रमाणपत्र श्रृंखलाएं सादे पाठ में पारित की जाती हैं और एक दुर्भावनापूर्ण श्रृंखला को उन TCP कनेक्शनों को अस्वीकार करके अवरुद्ध किया जा सकता है जिनमें SubjectAlternateName OtherName फ़ील्ड में DER एन्कोडेड 1.3.6.1.5.5.7.8.9 NID होता है।
दुर्भाग्य से, इस फ़ील्ड को दो या अधिक पैकेटों के बीच मनमाने ढंग से विभाजित किया जा सकता है और वास्तव में अवरुद्ध करने के लिए किसी प्रकार के स्टेटफुल पैटर्न मैचर की आवश्यकता है। प्रमाणपत्रों को संपीड़ित भी किया जा सकता है, लेकिन OpenSSL 3.0.x इस समय प्रमाणपत्र संपीड़न का समर्थन नहीं करता है।
इसके अतिरिक्त, TLS1.3 के साथ क्लाइंट प्रमाणपत्र श्रृंखला वायर पर एन्क्रिप्ट की जाती है, और TLS के पिछले संस्करण मौजूदा कनेक्शन को फिर से बातचीत करते समय एन्क्रिप्टेड प्रमाणपत्र श्रृंखला का समर्थन करते हैं। यह कभी-कभी सर्वर-आरंभिक प्रमाणपत्र प्रमाणीकरण के लिए किया जाता है। उन मामलों में नेटवर्क फ़िल्टर प्रभावी नहीं होगा।
मैं कैसे बता सकता हूँ कि मैं स्थिर रूप से लिंक किए गए बाइनरी में openssl 3 का उपयोग कर रहा हूँ?
readelf -a [binary] | grep -i ossl_punycode_decode
एक स्थिर रूप से लिंक किए गए बाइनरी में कमजोर फ़ंक्शन की खोज करेगा। केवल OpenSSL >= 3.0 में यह फ़ंक्शन है।