2015 m. spalio 6 d., antradienis

Qualys Security Advisory - OpenSMTPD Audit Report 3prt

------------------------------------------------------------
------------
CVE-2015-ABCD - .forward stack-based buffer overflow
------------------------------------------------------------------------

In lka_expand_format(), the exptoklen bytes returned by
lka_expand_token() are memcpy()ed to ptmp (a pointer into the
stack-based tmpbuf) without first checking that there is enough space
left in tmpbuf:

799                 exptoklen = lka_expand_token(exptok, sizeof exptok, token, ep,
800                     ui);
801                 if (exptoklen == 0)
802                         return 0;
803
804                 memcpy(ptmp, exptok, exptoklen);

This stack-based buffer overflow can be triggered locally through
OpenSMTPD's .forward mechanism:

$ whoami
john

$ python -c 'print "/" * 1014 + "%{sender}"' > ~/.forward

$ python -c 'print "A" * 255 + "@" + "A" * 255'
AAA...AAA@AAA...AAA

$ telnet 127.0.0.1 25
EHLO 127.0.0.1
MAIL FROM:<AAA...AAA@AAA...AAA>
RCPT TO:<john@localhost>
Connection closed by foreign host.

As a result, in the logs:

smtpd[9305]: warn: format string error while expanding for user john
smtpd: stack overflow in function lka_submit

It does not appear to be exploitable on OpenBSD x86 (beyond a local
denial-of-service), where even a one-byte overflow ended up smashing the
stack canary of every smtpd binary we tried. However, it may lead to
arbitrary code execution on other operating systems or platforms.


========================================================================
Remote Vulnerabilities
========================================================================

------------------------------------------------------------------------
CVE-2015-ABCD - Remote denial-of-service (disk-space exhaustion)
------------------------------------------------------------------------

The maximum size of an email, env->sc_maxsize (by default 35 megabytes),
is enforced in dataline_callback() for the email's body:

 266         len = strlen(line) + 1;
 267
 268         if (s->datalen + len > env->sc_maxsize) {
 269                 s->msgflags |= MF_ERROR_SIZE;
 270                 return;
 271         }
 ...
 278         s->datalen += len;

but not in header_default_callback() for the email's headers:

 243         len = strlen(hdr->name) + 1;
 ...
 248         s->datalen += len;
 249
 250         TAILQ_FOREACH(l, &hdr->lines, next) {
 251                 len = strlen(l->buffer) + 1;
 ...
 256                 s->datalen += len;
 257         }

It is therefore possible to send a headers-only email (i.e., no empty
line between the last header and the DATA-ending ".") that is much
larger than 35 megabytes, and fill OpenSMTPD's queue or mailbox
hard-disk partition.

------------------------------------------------------------------------
Multiple vulnerabilities in IMSG_{SMTP,MTA}_SSL_VERIFY*
------------------------------------------------------------------------

These IMSG_{SMTP,MTA}_SSL_VERIFY* messages are exchanged between
PROC_PONY and PROC_LKA:

- after PROC_PONY successfully established an SSL connection with an
  SMTP client (a client-certificate request is always made, but not
  necessarily responded to), it calls smtp_verify_certificate() and
  sends a few IMSG_SMTP_SSL_VERIFY* messages to PROC_LKA, which verifies
  the client's SSL certificate (if any) on behalf of PROC_PONY.

- after PROC_PONY successfully established an SSL connection with an
  SMTP server (opportunistic STARTTLS encryption is always attempted,
  but not necessarily successful), it calls mta_verify_certificate() and
  sends a few IMSG_MTA_SSL_VERIFY* messages to PROC_LKA, which verifies
  the server's SSL certificate on behalf of PROC_PONY.

In lka_imsg(), PROC_LKA blindly trusts the contents of the
req_ca_vrfy_smtp, req_ca_vrfy_mta, and req_ca_vrfy_chain structures that
it receives from PROC_PONY (although this suggests vulnerabilities that
are inter-process only, they are also triggerable remotely through two
other low-level bugs in imsg and mproc, to be described shortly):

struct ca_vrfy_req_msg {
        uint64_t                reqid;
        char                    pkiname[SMTPD_MAXHOSTNAMELEN];
        unsigned char          *cert;
        off_t                   cert_len;
        size_t                  n_chain;
        size_t                  chain_offset;
        unsigned char         **chain_cert;
        off_t                  *chain_cert_len;
};

 63 static void
 64 lka_imsg(struct mproc *p, struct imsg *imsg)
 65 {
 ..
 70         static struct ca_vrfy_req_msg   *req_ca_vrfy_smtp = NULL;
 71         static struct ca_vrfy_req_msg   *req_ca_vrfy_mta = NULL;
 72         struct ca_vrfy_req_msg          *req_ca_vrfy_chain;

- In the IMSG_{SMTP,MTA}_SSL_VERIFY_{CERT,CHAIN} cases, PROC_LKA
  attempts to read cert_len bytes from imsg->data, but does not check
  first that PROC_PONY actually sent this amount of data (out-of-bounds
  memory read, CVE-2015-ABCD):

148                 case IMSG_SMTP_SSL_VERIFY_CERT:
149                         req_ca_vrfy_smtp = xmemdup(imsg->data, sizeof *req_ca_vrfy_smtp, "lka:ca_vrfy");
150                         req_ca_vrfy_smtp->cert = xmemdup((char *)imsg->data +
151                             sizeof *req_ca_vrfy_smtp, req_ca_vrfy_smtp->cert_len, "lka:ca_vrfy");

158                 case IMSG_SMTP_SSL_VERIFY_CHAIN:
159                         if (req_ca_vrfy_smtp == NULL)
160                                 fatalx("lka:ca_vrfy: chain without a certificate");
161                         req_ca_vrfy_chain = imsg->data;
162                         req_ca_vrfy_smtp->chain_cert[req_ca_vrfy_smtp->chain_offset] = xmemdup((char *)imsg->data +
163                             sizeof *req_ca_vrfy_chain, req_ca_vrfy_chain->cert_len, "lka:ca_vrfy");

- In the IMSG_{SMTP,MTA}_SSL_VERIFY_CERT case, PROC_LKA does not
  sanity-check n_chain, the number of certificates in the chain that
  will be sent by PROC_PONY:

148                 case IMSG_SMTP_SSL_VERIFY_CERT:
149                         req_ca_vrfy_smtp = xmemdup(imsg->data, sizeof *req_ca_vrfy_smtp, "lka:ca_vrfy");
150                         req_ca_vrfy_smtp->cert = xmemdup((char *)imsg->data +
151                             sizeof *req_ca_vrfy_smtp, req_ca_vrfy_smtp->cert_len, "lka:ca_vrfy");
152                         req_ca_vrfy_smtp->chain_cert = xcalloc(req_ca_vrfy_smtp->n_chain,
153                             sizeof (unsigned char *), "lka:ca_vrfy");
154                         req_ca_vrfy_smtp->chain_cert_len = xcalloc(req_ca_vrfy_smtp->n_chain,
155                             sizeof (off_t), "lka:ca_vrfy");
156                         return;

- In the IMSG_{SMTP,MTA}_SSL_VERIFY_{CERT,CHAIN} cases, PROC_LKA does
  not sanity-check chain_offset (out-of-bounds memory write,
  CVE-2015-ABCD):

  . in the IMSG_{SMTP,MTA}_SSL_VERIFY_CERT case, chain_offset should be
    initialized to 0, but PROC_LKA trusts PROC_PONY to do so (arbitrary
    memory write);

  . in the IMSG_{SMTP,MTA}_SSL_VERIFY_CHAIN case, chain_offset should be
    checked against n_chain, but PROC_LKA trusts PROC_PONY to send no
    more than n_chain certificates (heap-based buffer overflow):

158                 case IMSG_SMTP_SSL_VERIFY_CHAIN:
159                         if (req_ca_vrfy_smtp == NULL)
160                                 fatalx("lka:ca_vrfy: chain without a certificate");
161                         req_ca_vrfy_chain = imsg->data;
162                         req_ca_vrfy_smtp->chain_cert[req_ca_vrfy_smtp->chain_offset] = xmemdup((char *)imsg->data +
163                             sizeof *req_ca_vrfy_chain, req_ca_vrfy_chain->cert_len, "lka:ca_vrfy");
164                         req_ca_vrfy_smtp->chain_cert_len[req_ca_vrfy_smtp->chain_offset] = req_ca_vrfy_chain->cert_len;
165                         req_ca_vrfy_smtp->chain_offset++;
166                         return;

- In the IMSG_{SMTP,MTA}_SSL_VERIFY case, PROC_LKA does not reset the
  static pointer req_ca_vrfy_{smtp,mta} to NULL after free(), but trusts
  PROC_PONY to always send an IMSG_{SMTP,MTA}_SSL_VERIFY_CERT (which
  re-initializes this static pointer) before sending an
  IMSG_{SMTP,MTA}_SSL_VERIFY{_CHAIN,} (use-after-free, CVE-2015-ABCD):

168                 case IMSG_SMTP_SSL_VERIFY:
169                         if (req_ca_vrfy_smtp == NULL)
170                                 fatalx("lka:ca_vrfy: verify without a certificate");
...
185                         for (i = 0; i < req_ca_vrfy_smtp->n_chain; ++i)
186                                 free(req_ca_vrfy_smtp->chain_cert[i]);
187                         free(req_ca_vrfy_smtp->chain_cert);
188                         free(req_ca_vrfy_smtp->chain_cert_len);
189                         free(req_ca_vrfy_smtp->cert);
190                         free(req_ca_vrfy_smtp);
191                         return;

------------------------------------------------------------------------
CVE-2015-ABCD - Integer truncation in the imsg API
------------------------------------------------------------------------

There is a fundamental design flaw in the imsg_create(), imsg_add(), and
imsg_compose() functions (and imsg_composev(), which calls imsg_create()
and imsg_add()): their datalen argument is directly an u_int16_t, a fact
that is easily overlooked by their callers and makes them vulnerable to
integer truncation.

In OpenSMTPD, the nearly-identical functions smtp_verify_certificate()
and mta_verify_certificate() are vulnerable to this integer truncation,
and both are reachable remotely through SSL certificate verification:

2046 static int
2047 smtp_verify_certificate(struct smtp_session *s)
2048 {
....
2056         x = SSL_get_peer_certificate(s->io.ssl);
....
2059         xchain = SSL_get_peer_cert_chain(s->io.ssl);
....
2071         /* Send the client certificate */
2072         memset(&req_ca_vrfy, 0, sizeof req_ca_vrfy);
....
2083         req_ca_vrfy.cert_len = i2d_X509(x, &req_ca_vrfy.cert);
2084         if (xchain)
2085                 req_ca_vrfy.n_chain = sk_X509_num(xchain);
2086         iov[0].iov_base = &req_ca_vrfy;
2087         iov[0].iov_len = sizeof(req_ca_vrfy);
2088         iov[1].iov_base = req_ca_vrfy.cert;
2089         iov[1].iov_len = req_ca_vrfy.cert_len;
2090         m_composev(p_lka, IMSG_SMTP_SSL_VERIFY_CERT, 0, 0, -1,
2091             iov, nitems(iov));
....
2095         if (xchain) {
2096                 /* Send the chain, one cert at a time */
2097                 for (i = 0; i < sk_X509_num(xchain); ++i) {
2098                         memset(&req_ca_vrfy, 0, sizeof req_ca_vrfy);
....
2100                         x = sk_X509_value(xchain, i);
2101                         req_ca_vrfy.cert_len = i2d_X509(x, &req_ca_vrfy.cert);
2102                         iov[0].iov_base = &req_ca_vrfy;
2103                         iov[0].iov_len  = sizeof(req_ca_vrfy);
2104                         iov[1].iov_base = req_ca_vrfy.cert;
2105                         iov[1].iov_len  = req_ca_vrfy.cert_len;
2106                         m_composev(p_lka, IMSG_SMTP_SSL_VERIFY_CHAIN, 0, 0, -1,
2107                             iov, nitems(iov));
....
2109                 }
2110         }
2111
2112         /* Tell lookup process that it can start verifying, we're done */
2113         memset(&req_ca_vrfy, 0, sizeof req_ca_vrfy);
....
2115         m_compose(p_lka, IMSG_SMTP_SSL_VERIFY, 0, 0, -1,
2116             &req_ca_vrfy, sizeof req_ca_vrfy);
....
2119 }

If the cert_len returned by i2d_X509() exceeds 64k, integer truncation
occurs when m_composev() calls imsg_composev(). The following #define
from OpenSSL and LibreSSL confirms that this is indeed possible:

#define SSL_MAX_CERT_LIST_DEFAULT 1024*100 /* 100k max cert list :-) */

Surprisingly, this integer truncation in PROC_PONY triggers the
out-of-bounds memory read in PROC_LKA: xmemdup() tries to read cert_len
bytes (the non-truncated cert_len) from imsg->data, which contains only
the truncated number of cert_len bytes (i.e., xmemdup() tries to read an
extra 64k from imsg->data).

Our initial research suggests that this out-of-bounds memory read can be
transformed into a remote information leak that reveals heap addresses
and defeats the ASLR protection. Linux is almost certainly exploitable,
but OpenBSD's heavily randomized and hardened malloc significantly
raises the bar for successful exploitation.

------------------------------------------------------------------------
CVE-2015-ABCD - Missing return-value checks in the mproc API
------------------------------------------------------------------------

The m_forward(), m_compose(), and m_composev() functions do not check
the return value of imsg_compose() and imsg_composev(): if the message
to be sent is larger than MAX_IMSGSIZE (16k), these three functions will
fail to send the message, but they will not report this failure to their
callers, which have therefore no way of knowing whether the message was
actually sent or not.

Again, smtp_verify_certificate() and mta_verify_certificate() are
vulnerable: if the peer's certificate is larger than 16k (but smaller
than 64k, in order to avoid the integer truncation), PROC_PONY will fail
to send the IMSG_{SMTP,MTA}_SSL_VERIFY_CERT to PROC_LKA, and the
subsequent IMSG_{SMTP,MTA}_SSL_VERIFY_CHAIN will trigger the
use-after-free in PROC_LKA, which in turn will trigger the out-of-bounds
memory write in PROC_LKA.

Our initial research suggests that this use-after-free (and
out-of-bounds memory write) can be transformed into remote code
execution, when combined with the information leak described above.
Linux is almost certainly exploitable, but again, OpenBSD's heavily
randomized and hardened malloc significantly raises the bar for
successful exploitation.

Komentarų nėra:

Rašyti komentarą