Bug 2199 - Exim use-after-free vulnerability while reading mail header
Exim use-after-free vulnerability while reading mail header
Status: RESOLVED FIXED
Product: Exim
Classification: Unclassified
Component: Delivery in general
4.89
x86-64 Linux
: critical security
: Exim 4.90
Assigned To: Jeremy Harris
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2017-11-23 09:40 UTC by meh@devco.re
Modified: 2018-04-17 08:05 UTC (History)
5 users (show)

See Also:


Attachments
RIP-control PoC (448 bytes, text/x-python-script)
2017-11-23 09:40 UTC, meh@devco.re
Details
debug log (11.17 KB, text/plain)
2017-11-24 02:46 UTC, meh@devco.re
Details
Local/Makefile (63.24 KB, text/x-matlab)
2017-11-24 02:46 UTC, meh@devco.re
Details
updated PoC (494 bytes, text/x-python-script)
2017-11-24 02:48 UTC, meh@devco.re
Details

Note You need to log in before you can comment on or make changes to this bug.
Description meh@devco.re 2017-11-23 09:40:56 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)
Comment 1 Jeremy Harris 2017-11-23 09:57:56 UTC
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.
Comment 2 meh@devco.re 2017-11-23 10:11:09 UTC
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.
Comment 3 Jeremy Harris 2017-11-23 10:42:22 UTC
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?
Comment 4 meh@devco.re 2017-11-23 10:52:50 UTC
Yes, I am talking about the gap between 1671 and 1783.
I tested on current master(01c594601670c7e48e676d6c6d32d0f0084067fa) and still found the same result.
Comment 5 Jeremy Harris 2017-11-23 11:08:20 UTC
There is no call to bdat_getc() between those lines.
Comment 6 meh@devco.re 2017-11-23 11:15:08 UTC
receive_getc becomes bdat_getc when handling BDAT data.
Comment 7 Jeremy Harris 2017-11-23 19:29:02 UTC
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")?
Comment 8 meh@devco.re 2017-11-24 02:45:27 UTC
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.
Comment 9 meh@devco.re 2017-11-24 02:46:08 UTC
Created attachment 1050 [details]
debug log
Comment 10 meh@devco.re 2017-11-24 02:46:39 UTC
Created attachment 1051 [details]
Local/Makefile
Comment 11 meh@devco.re 2017-11-24 02:48:06 UTC
Created attachment 1052 [details]
updated PoC
Comment 12 Jeremy Harris 2017-11-24 20:21:32 UTC
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?
Comment 13 Phil Pennock 2017-11-25 03:49:09 UTC
CVE requested
Comment 14 meh@devco.re 2017-11-25 10:31:46 UTC
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.
Comment 15 Git Commit 2017-11-25 16:27:08 UTC
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;
       }
     }
Comment 16 Jeremy Harris 2017-11-25 17:39:31 UTC
Fix confirmed by reporter
Comment 17 Phil Pennock 2017-11-25 23:40:54 UTC
CVE-2017-16943
Comment 18 meh@devco.re 2017-11-26 08:19:43 UTC
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.
Comment 19 Phil Pennock 2017-11-26 22:10:16 UTC
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.
Comment 20 Jhon Merced 2018-04-17 08:05:54 UTC
Seems to work. Thanks for the fix guys.

Jhon
https://mrkortingscode.nl/