On 2023/03/18 15:06:43 +0100, [email protected] wrote:
> >Synopsis: segmentation fault in opensmtpd mda
> >Category: system
> >Environment:
> System : OpenBSD 7.2
> Details : OpenBSD 7.2 (GENERIC.MP) #2: Thu Nov 24 23:53:03 MST 2022
>
> [email protected]:/usr/src/sys/arch/arm64/compile/GENERIC.MP
>
> Architecture: OpenBSD.arm64
> Machine : arm64
> >Description:
> I would have waited another week after contacting Gilles at his address
> and at his openbsd.org address last week, but we're out of -beta and I'd like
> to see this addressed before release. It may already be too late though.
> I've found a way to crash the mda that is forked from opensmtpd before the
> exec. It is a specially crafted .forward file that does this. In the worst
> case scenario it will fill up /var with smtpd.core's when the
> kern.nosuidcoredump sysctl is set to 3. The queue files are stuck in queue
> and have to be removed either with smtpctl remove or until they time out.
> A lot of these could fill /var with corefiles quicker.
> >How-To-Repeat:
> Here is the "exploit" code that I stuck into my .forward, I also gave
> this to Gilles.
can be replicated with a way more simpler file:
% cat ~/.forward
"|%{mda}"
Here's the backtrace:
(gdb) bt
#0 _libc_strlcpy (dst=<optimized out>, src=<optimized out>,
dsize=<optimized out>) at /usr/src/lib/libc/string/strlcpy.c:36
#1 0x0000083cd79daac0 in mda_expand_token (dest=0x7f7ffffccf10 "", len=1024,
token=0x7f7ffffcce80 "mda[0:127]:raw", dlv=0x7f7ffffce9e0,
ui=0x7f7ffffcfce0, mda_command=0x0)
at /usr/src/usr.sbin/smtpd/smtpd/../mda_variables.c:162
#2 0x0000083cd79da1af in mda_expand_format (
buf=0x7f7ffffcdf90 "%{mda[0:127]:raw}", len=2048, dlv=0x7f7ffffce9e0,
ui=0x7f7ffffcfce0, mda_command=0x0)
at /usr/src/usr.sbin/smtpd/smtpd/../mda_variables.c:298
#3 0x0000083cd79d9a3d in mda_unpriv (dsp=0x83f4722d000,
deliver=0x7f7ffffce9e0, pw_name=0x7f7ffffcfce0 "op",
pw_dir=0x7f7ffffcfde0 "/home/op")
at /usr/src/usr.sbin/smtpd/smtpd/../mda_unpriv.c:46
#4 0x0000083cd7a13285 in forkmda (p=0x83f510fb000, id=18429899215001711955,
deliver=0x7f7ffffce9e0) at /usr/src/usr.sbin/smtpd/smtpd/../smtpd.c:1533
#5 0x0000083cd7a123e8 in parent_imsg (p=0x83f510fb000, imsg=0x7f7ffffd0230)
at /usr/src/usr.sbin/smtpd/smtpd/../smtpd.c:204
#6 0x0000083cd79db7fc in mproc_dispatch (fd=7, event=2, arg=0x83f510fb000)
at /usr/src/usr.sbin/smtpd/smtpd/../mproc.c:194
#7 0x0000083efaf30ebf in event_process_active (base=0x83f47232400)
at /usr/src/lib/libevent/event.c:333
#8 event_base_loop (base=0x83f47232400, flags=<optimized out>)
at /usr/src/lib/libevent/event.c:483
#9 0x0000083cd7a1030b in smtpd ()
at /usr/src/usr.sbin/smtpd/smtpd/../smtpd.c:1074
#10 0x0000083cd7a0f5a7 in main (argc=0, argv=0x7f7ffffd0660)
at /usr/src/usr.sbin/smtpd/smtpd/../smtpd.c:721
(gdb) f 1
#1 0x0000083cd79daac0 in mda_expand_token (dest=0x7f7ffffccf10 "", len=1024,
token=0x7f7ffffcce80 "mda[0:127]:raw|", '/' <repeats 110 times>,
dlv=0x7f7ffffce9e0, ui=0x7f7ffffcfce0, mda_command=0x0)
at /usr/src/usr.sbin/smtpd/smtpd/../mda_variables.c:162
162 if (strlcpy(tmp, string, sizeof tmp) >= sizeof tmp)
(gdb) p string
$1 = 0x0
(gdb) p mda_command
$2 = 0x0
We hit the else-if at line 147:
146 else if (!strcasecmp("mda", rtoken)) {
147 string = mda_command;
148 replace = 0;
149 }
but mda_command is NULL.
That NULL originates in mda_unpriv() (in mda_unpriv.c:46) where NULL
is passed to mda_expand_format(), which is then passed as-is to
mda_expand_token().
I don't see how this should work. I also don't have experience with
the internals of smtpd and the more complex .forward I've used until
now was "[email protected]". I hope this can save someone else some
time tho :)