Qualys' security team Threat Research Unit published
a couple of hours ago a linear two-step heap buffer overflow in glibc's
syslog():
206 buf = malloc ((bufsize + 1) * sizeof (char));
...
213 __snprintf (buf, l + 1,
214 SYSLOG_HEADER (pri, timestamp, &msgoff, pid));
...
221 __vsnprintf_internal (buf + l, bufsize - l + 1, fmt, apc,
222 mode_flags);
the tl;dr is that bufsize is 0 while l is user-controlled.
As mentioned in the advisory, messing with nss structures as done
in their (phenomenal) Baron Samedit sudo
exploit
is a good way to get a root shell on the glibc.
While the bug is in glibc's syslog, it's not unheard of for
people to run custom allocators for performance/security/speed/… reasons.
One of those could be, for example, hardened_malloc,
GrapheneOS's security-focused allocator, raising
the question "would hardened_malloc make this particular bug
unexploitable on my x86_64 Debian machine?"
After discussing this with friends, we don't think that it makes the bug completely unexploitable, but ridiculously complicated, which is good enough™ for me. But keep in mind that this "analysis" was done hastily at 2am, so caveat lector.
hardened_malloc uses size-based slabs isolation, popularised by
PartitionAlloc.
Since bufsize is zero, this is a 1-byte
allocation, falling into the
16 bytes size-class,
the smallest after the special 0 one. So to exploit this, one would have to find an
interesting object of size 16 bytes or lower to overwrite. But since
canaries are enabled by default, this becomes even more difficult: sizes of
allocations are actually bumped by 8 bytes, meaning that one would actually
have to find an interesting object of size 8 bytes or lower.
Moreover, 16-byte slabs can contain at most 256 allocations, and are
surrounded by guard pages, meaning that accessing anything below buf and
above buf+(256*16) will result in a crash.
Allocations are randomized, which might help for bruteforcing the heap layout:
if the current one isn't exploitable, just crash and start again. But it will
also result in a lot more crashes, since buf might be allocated closer to
the guard page.
There are of course other mitigations, but they aren't relevant in this
particular case, like canaries that are checked on free,
or ARM's MTE that completely kills linear-overflows.
Given the ludicrous amount of randomization hardened_malloc applies to heap bases (32G
per region), bruteforcing offsets of anything not on the heap is futile.
So one would have to find something interesting in an object of 8 bytes or less on
the heap, like a path to corrupt as in service_user,
or some partial-overwrite of a function-pointer to call a
one-shot-gadget, …
Thanks to strcat for the handholding, and
to jdoe, drvink and J for their diligent proofreading,