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_{
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[
163 sizeof *req_ca_vrfy_chain, req_ca_vrfy_chain->cert_len, "lka:ca_vrfy");
- In the IMSG_{SMTP,MTA}_SSL_VERIFY_
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_
153 sizeof (unsigned char *), "lka:ca_vrfy");
154 req_ca_vrfy_smtp->chain_cert_
155 sizeof (off_t), "lka:ca_vrfy");
156 return;
- In the IMSG_{SMTP,MTA}_SSL_VERIFY_{
not sanity-check chain_offset (out-of-bounds memory write,
CVE-2015-ABCD):
. in the IMSG_{SMTP,MTA}_SSL_VERIFY_
initialized to 0, but PROC_LKA trusts PROC_PONY to do so (arbitrary
memory write);
. in the IMSG_{SMTP,MTA}_SSL_VERIFY_
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[
163 sizeof *req_ca_vrfy_chain, req_ca_vrfy_chain->cert_len, "lka:ca_vrfy");
164 req_ca_vrfy_smtp->chain_cert_
165 req_ca_vrfy_smtp->chain_
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_
re-initializes this static pointer) before sending an
IMSG_{SMTP,MTA}_SSL_VERIFY{_
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_
187 free(req_ca_vrfy_smtp->chain_
188 free(req_ca_vrfy_smtp->chain_
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->
....
2059 xchain = SSL_get_peer_cert_chain(s->io.
....
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_
subsequent IMSG_{SMTP,MTA}_SSL_VERIFY_
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ą