========================================================================
CVE-2020-OCORK -- Use-after-free in tls-openssl.c
========================================================================

If Exim is built with OpenSSL, and if STARTTLS is enabled, and if
PIPELINING is enabled (the default), and if X_PIPE_CONNECT is disabled
(the default before Exim 4.94), then tls_write() in tls-openssl.c is
vulnerable to a use-after-free.

If PIPELINING is used, Exim buffers the SMTP responses to MAIL FROM and
RCPT TO commands (in tls-openssl.c):

2909 int
2910 tls_write(void * ct_ctx, const uschar *buff, size_t len, BOOL more)
2911 {
....
2915 static gstring * server_corked = NULL;
2916 gstring ** corkedp = ct_ctx
2917   ? &((exim_openssl_client_tls_ctx *)ct_ctx)->corked : &server_corked;
2918 gstring * corked = *corkedp;
....
2933 if (!ct_ctx && (more || corked))
2934   {
....
2940   corked = string_catn(corked, buff, len);
....
2946   if (more)
2947     {
2948     *corkedp = corked;
2949     return len;
2950     }

- at line 2910, ct_ctx is NULL, buff contains the SMTP response, and
  more is true;

- at line 2940, a struct gstring (a "growable string", mentioned in
  CVE-2020-STDIN) and its string buffer are allocated in POOL_MAIN
  memory:

 29 typedef struct gstring {
 30   int   size;           /* Current capacity of string memory */
 31   int   ptr;            /* Offset at which to append further chars */
 32   uschar * s;           /* The string memory */
 33 } gstring;

- at line 2948, a pointer to the struct gstring is saved to a local
  static variable, server_corked.

Unfortunately, if smtp_reset() is called (in smtp_setup_msg()), then
store_reset() is called and frees all allocated POOL_MAIN memory, but
server_corked is not reset to NULL: if tls_write() is called again, the
struct gstring and its string buffer are used-after-free.

Side note: another use-after-free, CVE-2017-16943, was discovered by Meh
Chang in November 2017 (https://bugs.exim.org/show_bug.cgi?id=2199).

To reliably control this vulnerability, we must prevent Exim from
calling tls_write() between our call to smtp_reset() and the actual
use-after-free:

- first, we send EHLO and STARTTLS (to initiate a TLS connection);

- second, we send EHLO and "MAIL FROM:<>\nNO" (to pipeline the first
  half of a NOOP command, and to buffer the response to our MAIL FROM
  command in tls_write());

- third, we terminate the TLS connection (and fall back to cleartext)
  and send "OP\n" (the second half of our pipelined NOOP command);

- next, we send EHLO (to force a call to smtp_reset()) and STARTTLS (to
  re-initiate a TLS connection);

- last, server_corked is used-after-free (in tls_write()) in response to
  any SMTP command that we send.

This use-after-free of a struct gstring (server_corked) and its string
buffer (server_corked->s) is the most powerful vulnerability in this
advisory:

1/ We overwrite the string buffer (which is sent to us by tls_write())
and transform this use-after-free into an information leak (we leak
pointers to the heap).

2/ We overwrite the struct gstring (with an arbitrary string pointer and
size) and transform the use-after-free into a read-what-where primitive:
we read the heap until we locate Exim's configuration.

3/ We overwrite the struct gstring (with an arbitrary string pointer)
and transform the use-after-free into a write-what-where primitive: we
overwrite Exim's configuration with an arbitrary "${run{command}}" that
is executed by expand_string() as the "exim" user (Digression 2).

We use a few noteworthy tricks in our exploit:

1/ Information leak: To overwrite the string buffer without overwriting
the struct gstring itself, we send several pipelined RCPT TO commands to
re-allocate the string buffer (far away from the struct gstring), and
overwrite it with header_line structures that contain pointers to the
heap.

2/ Read-what-where: We overwrite the struct gstring with arbitrary
binary data through the name=value parameter of a MAIL FROM command:

- we overwrite the s member with a pointer to the memory that we want to
  read (a pointer to the heap);

- we overwrite the ptr member with the number of bytes that we want to
  read;

- we overwrite the size member with the same number as ptr to prevent
  string_catn() from writing to the memory that we want to read (at line
  2940 in tls_write()).

3/ Write-what-where: We overwrite the struct gstring with arbitrary
binary data through the name=value parameter of a MAIL FROM command:

- we overwrite the s member with a pointer to the memory that we want to
  overwrite (a pointer to Exim's configuration);

- we overwrite the ptr member with 0 and the size member with a large
  arbitrary number;

- finally, we send a MAIL FROM command whose response overwrites Exim's
  configuration with our arbitrary "${run{...}}" (which is eventually
  executed by expand_string()).

Note: Debian's Exim packages are built with GnuTLS, not OpenSSL; to
re-build them with OpenSSL, we followed the instructions at
https://gist.github.com/ryancdotorg/11025731.

