linuxwindowsassemblybuffer-overflow

How do buffer overflows execute instructions on stack?


I have read a bit about buffer overflows, one thing I don't understand is that normally space isn't executable to my knowledge, how can code placed in it be run?


Solution

  • There is a binary protection call NX(Non executable stack), you can check the protections on any binary by running the command

    ❯ checksec --file test
    
        Arch:       amd64-64-little
        RELRO:      Partial RELRO
        Stack:      No canary found
        NX:         NX enabled
        PIE:        No PIE (0x400000)
        Stripped:   No
    

    Here the NX bit is enabled so pages can be non-executable. Normal programs will use non-executable pages for their stack and anywhere else they copy user input to. This prevents the CPU from executing it as code: the CPU will raise a page-fault exception instead of doing code-fetch from such a page. Only data loads and stores are allowed. So for example overwriting a return address to jump to a buffer on the stack would result in a segfault.

    The CPU treats data and instructions differently. Data is handled by the Data TLB and L1d cache, while instructions use the Instruction TLB and L1i cache. In a page with read+write+execute permission, you can load and store as data and also execute the bytes as machine code; which of those things happens depends on what the program does.

    To execute the injected code an attacker can exploit a vulnerability that allows them to redirect the programs execution flow. This means finding a bug that allows attacker to change where the program thinks it should go next such as changing a return address to point to their injected code.

    In older systems NX bit was not present so all readable pages were executable. Hence shellcode injection was possible. However these days these protection is present in almost all binaries, if you compile any binary, by default these protections will be on unless you specify otherwise using flags while compiling your code.

    32-bit x86 without PAE page tables didn't have NX support in hardware. So until Pentium Pro / II / III, and OSes that take advantage, 32-bit x86 OSes couldn't provide NX support. x86-64 natively has NX. Many OSes were conservative about backwards compatibility issues for 32-bit programs when making pages non-executable, in case of programs that depended on that, which is why binaries have to opt-in to NX support, and it can be disabled even for 64-bit binaries.