On 2023/03/18 15:06:43 +0100, p...@delphinusdns.org 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
>                        
> r...@syspatch-72-arm64.openbsd.org:/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 "o...@omarpolo.com".  I hope this can save someone else some
time tho :)

Reply via email to