Hi folks,
I'm passing the gauntlet for anyone who wants to analyze this for impact etc. There's a remotely triggerable buffer overflow in OpenBSD's OpenSMTPD -- the latest version, 5.7.2 -- reachable by sending messages with huge header lines. Qualys recently published a result of a big audit, but it seems like they based their investigations primarily on an older version of OpenSMTPD that didn't have as much of the "filter" infrastructure. I'd recommend interested parties spend some time looking through the filter code, as there could be more problems. Here's a vulnerability in the filter io path:
static void
filter_tx_io(struct io *io, int evt)
{
struct filter_session *s = io->arg;
size_t len, n;
char *data;
char buf[65535];
log_trace(TRACE_FILTERS, "filter: filter_tx_io(%p, %s)", s,
io_strevent(evt));
switch (evt) {
case IO_DATAIN:
data = iobuf_data(&s->ibuf);
len = iobuf_len(&s->ibuf);
memmove(buf, data, len);
buf[len] = 0;
log_trace(TRACE_FILTERS, "filter: filter_tx_io: datain
(%zu) for req %016"PRIx64": %s",
len, s->id, buf);
In this block, it's possible for `len` to be greater than 65535. I believe the maximum is 65536, but I've spent not too much time playing with this to be sure. Either way, the memmove() overflows `buf`. When len=65536, GNU's fortify source catches it and kills the program. I'm not sure the behavior on OpenBSD or other platforms. Depending on the layout of the stack, being able to overwrite two bytes (or perhaps more? which would be even worse) could lead to execution, but, again, I haven't looked for more than a few minutes, so I'm not sure.
I'm attaching here a crash reproducer. Running `sendmail instance@using.opensmtpd < this.email.attachment` will result in a remote OpenSMTPD dying (at least on GNU systems with fortify source; on others, the behavior is undefined).
If the execution makes it past that memmove, and filter tracing is enabled, this line is printed:
filter: filter_tx_io: datain (65536) for req 8e6eab5b833e834b: x-xxx-xxxxx: xx
If execution hits a fortify source abort point, this is the stack trace:
(gdb) bt
#0 0x0000003c6f433468 in raise () from /lib64/libc.so.6
#1 0x0000003c6f43483a in abort () from /lib64/libc.so.6
#2 0x0000003c6f472381 in __libc_message () from /lib64/libc.so.6
#3 0x0000003c6f4f8077 in __fortify_fail () from /lib64/libc.so.6
#4 0x0000003c6f4f6110 in __chk_fail () from /lib64/libc.so.6
#5 0x0000000000412840 in memmove (__len=65536, __src=0x1cff900,
__dest=0x7ffe12cf0e10)
at /usr/include/bits/string3.h:59
#6 filter_tx_io (io=<optimized out>, evt=<optimized out>) at
../../smtpd/filter.c:739
#7 0x00000000004154c0 in io_dispatch (fd=<optimized out>,
ev=<optimized out>, humppa=0x1c957d8)
at ../../smtpd/ioev.c:580
#8 0x0000003c82820044 in event_process_active_single_queue () from
/usr/lib64/libevent-2.1.so.5
#9 0x0000003c82820ddf in event_base_loop () from /usr/lib64/libevent-2.1.so.5
#10 0x000000000042d069 in pony () at ../../smtpd/pony.c:221
#11 0x00000000004071cc in fork_peers () at ../../smtpd/smtpd.c:829
#12 main (argc=<optimized out>, argv=<optimized out>) at ../../smtpd/smtpd.c:724
If my assumption is correct about 65536 being the maximum of len, I'd recommend making that buffer be size 65537, or making it size 65536 and being smart about that buf[len]=0 line. If my assumption isn't correct about 65536, perhaps the code will have to be more significantly refactored. Probably the OpenSMTPD/OpenBSD developers can make the best choice here though.
At some point we might want a CVE for this.
Thanks,
Jason