Bugzilla – Bug 2199
Exim use-after-free vulnerability while reading mail header
Last modified: 2018-04-17 08:05:54 UTC
Created attachment 1049 [details] RIP-control PoC Hi, we found a use-after-free vulnerability which is exploitable to RCE in the SMTP server. According to receive.c:1783, 1783 if (!store_extend(next->text, oldsize, header_size)) 1784 { 1785 uschar *newtext = store_get(header_size); 1786 memcpy(newtext, next->text, ptr); 1787 store_release(next->text); 1788 next->text = newtext; 1789 } when the buffer used to parse header is not big enough, exim tries to extend the next->text with store_extend function. If there is any other allocation between the allocation and extension of this buffer, store_extend fails. store.c 276 if ((char *)ptr + rounded_oldsize != (char *)(next_yield[store_pool]) || 277 inc > yield_length[store_pool] + rounded_oldsize - oldsize) 278 return FALSE; Then exim calls store_get, and store_get cut the current_block directly. store.c 208 next_yield[store_pool] = (void *)((char *)next_yield[store_pool] + size); 209 yield_length[store_pool] -= size; 210 211 return store_last_get[store_pool]; However, in receive.c:1787, store_release frees the whole block, leaving the new pointer points to a freed location. Any further usage of this buffer leads to a use-after-free vulnerability. To trigger this bug, BDAT command is necessary to perform an allocation by raising an error. Through our research, we confirm that this vulnerability can be exploited to remote code execution if the binary is not compiled with PIE. An RIP controlling PoC is in attachment poc.py. The following is the gdb result of this PoC: Program received signal SIGSEGV, Segmentation fault. 0x00000000deadbeef in ?? () (gdb)
Apart from signal-handlers, exim is not threaded so "any other allocation between the allocation and extension of this buffer" cannot happen (assuming we don't do allocations in sig-handlers). There is presumably a bug, but I'm doubting the allegation of a race.
It does happen. In receive.c, exim used receive_getc to get message. 1831 ch = (receive_getc)(GETC_BUFFER_UNLIMITED); When exim is handling BDAT command, receive_getc is bdat_getc. In bdat_getc, after the length of BDAT is reached, bdat_getc tries to read the next command. smtp_in.c 536 next_cmd: 537 switch(smtp_read_command(TRUE, 1)) 538 { 539 default: 540 (void) synprot_error(L_smtp_protocol_error, 503, NULL, 541 US"only BDAT permissible after non-LAST BDAT"); synprot_error may call store_get if any non-printable character exists because synprot_error uses string_printing. string.c 304 /* Get a new block of store guaranteed big enough to hold the 305 expanded string. */ 306 307 ss = store_get(length + nonprintcount * 3 + 1); This all happens in the main thread.
Ah, you're not talking about the gap between receive.c:1783 and 1787, but because something is still using the old version of the header? Or of the store allocater by synprot_error()? Could you try your reproducer against the current master?
Yes, I am talking about the gap between 1671 and 1783. I tested on current master(01c594601670c7e48e676d6c6d32d0f0084067fa) and still found the same result.
There is no call to bdat_getc() between those lines.
receive_getc becomes bdat_getc when handling BDAT data.
There's no receive_getc either. Meantime, on test on an f27 I can't duplicate the crash. Script output: $ python2.7 poc.py [+] Opening connection to 127.0.0.1 on port 1225: Done [*] Switching to interactive mode 421 Lost incoming connection [*] Got EOF while reading in interactive $ [*] Closed connection to 127.0.0.1 port 1225 $ Debug output from exim (trimmed at top): 19:20:25 6725 SMTP<< MAIL FROM:<someone@some.domain> 19:20:25 6725 spool directory space = 35998396K inodes = 26760524 check_space = 10240K inodes = 100 msg_size = 0 19:20:25 6725 log directory space = 35998396K inodes = 26760524 check_space = 10240K inodes = 100 19:20:25 6725 SMTP>> 250 OK 19:20:25 6725 SMTP<< RCPT TO:<eximtest@test.ex> 19:20:25 6725 using ACL "check_recipient" 19:20:25 6725 processing "accept" 19:20:25 6725 check hosts = : 19:20:25 6725 host in ":"? no (end of list) 19:20:25 6725 accept: condition test failed in ACL "check_recipient" 19:20:25 6725 processing "accept" 19:20:25 6725 check domains = +local_domains 19:20:25 6725 test.ex in "@ : test.ex"? yes (matched "test.ex") 19:20:25 6725 test.ex in "+local_domains"? yes (matched "+local_domains") 19:20:25 6725 accept: condition test succeeded in ACL "check_recipient" 19:20:25 6725 end of ACL "check_recipient": ACCEPT 19:20:25 6725 SMTP>> 250 Accepted 19:20:25 6725 DSN: orcpt: NULL flags: 0 19:20:25 6725 SMTP<< aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa **** debug string too long - truncated **** 19:20:25 6725 LOG: smtp_syntax_error MAIN SMTP syntax error in "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa **** debug string too long - truncated **** 19:20:25 6725 SMTP>> 500 unrecognized command 19:20:25 6725 SMTP<< BDAT 1 19:20:25 6725 chunking state 1, 1 bytes 19:20:25 6725 search_tidyup called 19:20:25 6725 SMTP>> 250 1 byte chunk received 19:20:25 6725 chunking state 0 19:20:25 6725 SMTP<< BDAT ^? 19:20:25 6725 LOG: smtp_protocol_error MAIN 19:20:25 6725 SMTP protocol error in "BDAT \177" H=(test) [127.0.0.1] missing size for BDAT command 19:20:25 6725 SMTP>> 501 missing size for BDAT command 19:20:25 6725 host in ignore_fromline_hosts? no (option unset) 19:20:25 6725 >>Headers received: 19:20:25 6725 :<FD>aaaaaaᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ <DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ <DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ <DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ <DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ <DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ <DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ <DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????<EF><BE> **** debug string too long - truncated **** 19:20:25 6725 19:20:25 6725 search_tidyup called 19:20:25 6725 >>Headers after rewriting and local additions: 19:20:25 6725 :<FD>aaaaaaᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ <DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ <DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ <DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ <DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ <DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ <DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ <DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ<DE>????ᆳ <DE>????ᆳ<DE>????ᆳ<DE>???? **** debug string too long - truncated **** 19:20:25 6725 19:20:25 6725 Data file name: /home/jgh/git/exim/test/spool//input//1eHx2v-0001kT-8L-D 19:20:27 6725 LOG: MAIN 19:20:27 6725 SMTP connection from (test) [127.0.0.1] lost while reading message data 19:20:27 6725 SMTP>> 421 Lost incoming connection 19:20:27 6725 search_tidyup called 19:20:27 6718 child 6725 ended: status=0x0 19:20:27 6718 normal exit, 0 19:20:27 6718 0 SMTP accept processes now running 19:20:27 6718 Listening... I guess that a config file difference could be the discrepancy. Could you show the debug output from your test run (daemon running with "-d+all")?
Oh, I was talking about the source code of 4.89. In the current master, it is here: https://github.com/Exim/exim/blob/master/src/src/receive.c#L1790 What this PoC does is: 1. send unrecognized command to adjust yield_length and make it less than 0x100 2. send BDAT 1 3. send one character to reach the length of BDAT 3. send an BDAT command without size and with non-printable character -> trigger synprot_error and therefore call store_get // back to receive_msg and exim keeps trying to read header 4. send a huge message until store_extend called 5. uaf This PoC is affected by the block layout(yield_length), so this line: `r.sendline('a'*0x1250+'\x7f')` should be adjusted according to the program state. I tested on my ubuntu 16.04, compiled with the attached Local/Makefile (simply make -j8). I also attach the updated PoC for current master and the debug report.
Created attachment 1050 [details] debug log
Created attachment 1051 [details] Local/Makefile
Created attachment 1052 [details] updated PoC
Please test this possible fix: if (!store_extend(next->text, oldsize, header_size)) { + BOOL release_ok = store_last_get[store_pool] == next->text; uschar *newtext = store_get(header_size); memcpy(newtext, next->text, ptr); - store_release(next->text); + if (release_ok) store_release(next->text); next->text = newtext; } Also: you originally said "exploitable to RCE". Is that "Remote Code Execution"? If so, how? What about "uaf" - what is that?
CVE requested
Yes, the use-after-free(UAF) vulnerability leads to Remote code execution(RCE). The original Proof-of-Concept has already proved that hackers can gain code execution in exim server through this vulnerability. In the PoC, the memory area of current_block is freed so the content is modified by malloc.c. The struct member `next` is changed to somewhere should not be written and leads to RCE. We will publish a security advisory with more technical details after the disclosure process is completed. Besides, I've tested the patch and the bug is fixed.
Git commit: https://git.exim.org/exim.git/commitdiff/4e6ae6235c68de243b1c2419027472d7659aa2b4 commit 4e6ae6235c68de243b1c2419027472d7659aa2b4 Author: Jeremy Harris <jgh146exb@wizmail.org> AuthorDate: Fri Nov 24 20:22:33 2017 +0000 Commit: Jeremy Harris <jgh146exb@wizmail.org> CommitDate: Sat Nov 25 15:49:32 2017 +0000 Avoid release of store if there have been later allocations. Bug 2199 --- src/src/receive.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/src/receive.c b/src/src/receive.c index e7e518a..d9b5001 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1810,8 +1810,8 @@ for (;;) (and sometimes lunatic messages can have ones that are 100s of K long) we call store_release() for strings that have been copied - if the string is at the start of a block (and therefore the only thing in it, because we aren't - doing any other gets), the block gets freed. We can only do this because we - know there are no other calls to store_get() going on. */ + doing any other gets), the block gets freed. We can only do this release if + there were no allocations since the once that we want to free. */ if (ptr >= header_size - 4) { @@ -1820,9 +1820,10 @@ for (;;) header_size *= 2; if (!store_extend(next->text, oldsize, header_size)) { + BOOL release_ok = store_last_get[store_pool] == next->text; uschar *newtext = store_get(header_size); memcpy(newtext, next->text, ptr); - store_release(next->text); + if (release_ok) store_release(next->text); next->text = newtext; } }
Fix confirmed by reporter
CVE-2017-16943
By the way, I want to apologize for accidentally making this bug public directly. I read SecurityReleaseProcess in wiki, googled and didn't find an email address to report. So I decided to report to this bugzilla and did not notice any option to set it private (and I thought maybe security issues are default to private). Anyway, I see the notification in report process now. Sorry for that.
The notification is new, part of "lessons learned". You didn't miss it, I added it in reaction to this incident. We should have had something clearer up before. We didn't. Shit happens, we pick ourselves back up, learn from it, fix things and move on.
Seems to work. Thanks for the fix guys. Jhon https://mrkortingscode.nl/