Search code examples
clinuxgcclinkerglibc

Stacks are executable even with `noexecstack`


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.


Solution

  • Olaf and Employed Russian pushed me in the right direction. A third-party shared object was poisoning my stacks.

    But it wasn't linked to my main executable directly. Both ldd and lddtree weren't showing any libraries with RWE stacks, so I've decided to dig deeper and wrote a script that checked all shared objects currently mapped into a process memory:

    #!/bin/bash
    
    if [ -z "$1" ]; then
        echo "Usage: $0 <target>"
        exit 1;
    fi
    
    kav_pid=`pidof $1`
    for so in `cat /proc/$kav_pid/task/*/maps | awk '/.so$/ {print $6}' | sort | uniq`; do
        stack_perms=`readelf -Wl $so | awk '/GNU_STACK/ {print $7}'`
        if [ -z "$stack_perms" ]; then
            echo "$so doesn't have PT_GNU_STACK"
        elif [ "$stack_perms" != "RW" ]; then
            echo "$so has unexpected permissions: $stack_perms"
        fi
    done
    

    And it worked! I found a library with RWE permissions:

    $ ./find_execstack.sh target
    /target/dir/lib64/lib3rdparty.so has unexpected permissions: RWE
    

    To make sure that it was this library that poisoned my stacks, I opened my application with gdb and set a breakpoint in dlopen. And Bingo! Here are the permissions before dlopening lib3rdparty.so:

    7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]
    

    And here are they right after the dlopen:

    7ffffffde000-7ffffffff000 rwxp 00000000 00:00 0                          [stack]
    

    As it turned out, lib3rdparty.so was built using a different toolchain and that went unnoticed utill now.

    Olaf, Employed Russian, thank you!