I'm trying to protect my application against buffer overflow exploits. Among other things, I'm using non-executable stacks and link my binaries with the noexecstack
flag (by passing -Wl,-z,noexecstack
to gcc).
Everything seems fine - readelf
confirms that PT_GNU_STACK
specifies correct permissions:
$ readelf -l target | grep -A1 GNU_STACK
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 10
So does execstack
:
$ execstack -q target
- target
There's only one problem. All my stacks are actually executable:
root@170ubuntu16p04-64smp-1:~# cat /proc/12878/task/*/maps | grep stack
7ffcac654000-7ffcac675000 rwxp 00000000 00:00 0 [stack]
7fe540e66000-7fe541666000 rwxp 00000000 00:00 0 [stack]
7fe540665000-7fe540e65000 rwxp 00000000 00:00 0 [stack]
7fe53b800000-7fe53c000000 rwxp 00000000 00:00 0 [stack]
I've trapped allocate_stack calls and examined protection flags. In theory, they should be initialized according to PT_GNU_STACK
. But in my case, it seems like PT_GNU_STACK
was ignored and _dl_stack_flags
was initialized with default permissions.
Does anyone know what could have caused this? Everything seems correct, but stacks are still executable.
I'm using gcc 4.8.3 / glibc 2.11.
The stack is, naturally, readable, writeable and executable - not of much use otherwise. As far as I know we use the stack for data and code addresses (functions and their parameters/variable).
Non-executable stack (NX) is a virtual memory protection mechanism to block shell code injection from executing on the stack by restricting a particular memory and implementing the NX bit. But this technique is not really worthy against return to lib attacks, although they do not need executable stacks.
Executable stack is necessary in some cases such as GCC trampoline for nested functions. A trampoline is a small piece of code that is created at run time when the address of a nested function is taken. It normally resides on the stack, in the stack frame of the containing function.
By setting the NX bit, parts of the stack can be designated as non-executable which would mean that in order to execute shellcode from the stack an attacker must either find a way to disable the execution protection from memory or find a way to put her/his shellcode payload in a non-protected region of memory.
what could have caused this?
In addition to the main executable's PT_GNU_STACK
having correct permissions, you also need to have PT_GNU_STACK
with correct permissions in every directly-linked shared library.
If any one of these libraries does not have PT_GNU_STACK
at all, or has one with executable permissions, it will "poison" all of your stacks with executable permission.
So run
for j in $(ldd target | grep -o '=> .* ' | sed -e 's/=> //' -e '/^ *$/d' ); do
out=$(readelf -Wl $j | grep STACK)
[[ -z "$out" ]] && echo "missing GNU_STACK in $j"
echo $out | grep -q RWE && echo "executable GNU_STACK in $j"
done
and you will likely see at least one library with missing or executable stack.
P.S. I see that Olaf has already (partially) suggested this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With