MAP_STACK
For a memory region to be used as a stack, the
MAP_STACK
flag has to be passed to mmap
; otherwise, upon syscalls and pagefaults, if
the stack-pointer register doesn’t point to such a page, the program gets
killed.
Although this feature has been introduced in 2018 in OpenBSD by Theo de Raadt,
based on discussions with Ted Unangst and work done by Stefan Sperling, it was
already partially
present in Windows as a function named nt!PsValidateUserStack
,
documented in Ivan Fratic’s ropguard
project, and removed in
2012
likely because some of the
bypasses
were both obvious and generic:
- Write a stub that calls
mmap
with theMAP_STACK
flag, write your payload there, and jump on it, like done in 2023 by Thalium to pwn a TrustZone applet. - Write a stub to read the rest of your rop-chain onto the stack and pivot to it.
- Before each syscall, make point
esp
somewhere to the stack, and restore its value afterwards, as done in by @qwertyoruiop in yalu102 in 2017.
But checking the stack pointer on every pagefault is a bit more interesting,
unfortunately, someone told me about an OpenBSD-specific bypass, left as an
exercise to the reader: 647335652c535610ff7496e2b6624440
.
The commit implementing this mitigation, by Theo de Raadt mentions the following:
Observe that MAP_STACK can only be set/cleared by mmap(), which zeroes the contents of the region – there is no mprotect() equivalent operation, so there is no MAP_STACK-adding gadget.
I guess this means to say that an attacker has no way to directly map a
ROP-chain as MAP_STACK
. Except that this can be done by calling mmap
directly, and copying the ropchain there before jumping on it, which fits in a
couple of instructions.
The MAP_STACK
marking is also present in Linux
since the 13 August 2008,
but was added for different
reasons.
It’s not really used for anything super-interesting, beside pretty-printing via /proc
.
This mitigation adds a bit of complexity, but no overhead nor hinders debuggability, and the check-stack-pointer-on-pagefault trick might annoy an attacker for a couple of hours. It’s definitely not worth breaking the ABI for this.