Angr AEG:堆溢出之Exploit自動生成
本文主要介紹如何基於Angr 進行漏洞挖掘,並自動生成 Exploit。對於 Exploit 的自動生成問題,也被稱為 AEG(Automatic Exploit Generation),其中包含漏洞挖掘、Crash分析、約束條件構造、約束求解、Exploit 生成等環節,下文通過符號執行實現對二進位程序的自動化漏洞挖掘及利用,並展示完整的 AEG 過程。文中所分析的漏洞程序為Insomni`Hack 2016 題目之一。下載地址,其中包含漏洞程序源碼 demobin.c、編譯後的可執行程序 demobin 以及 Angr 腳本 solver.py。
0x00 漏洞原理
1、源碼分析
首先查看源碼 demo_bin.c,存在一處堆溢出漏洞,關鍵點如下:
1) component_name[128] 大於 component->name[32];
2) initializacomponent( char *cmpnaem) 函數中,在賦值時未檢查緩衝區大小;
3) 調用 do_something() 時,產生 Crash。
2、GDB 調試
分析過程序源碼後,利用 GDB 動態調試 demobin,以觸發 Crash。首先通過r2查看 initializacomponent( char *cmp_naem) 所對應的彙編代碼。
分析後可知,結構體 component 大小為 36 位元組,其中 component->name[32] 佔用 32 位元組,隨後 4 位元組為函數指針,因此構造 PoC 為 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB"。
利用 GDB 載入 demobin 並輸入 PoC,在 initializacomponent( char *cmp_naem) 函數中,調用 malloc() 之後設置斷點。查看此時 malloc() 所分配的內存情況。
繼續調試,單步至 initializacomponent( char *cmpnaem) 函數返回,查看此時內存情況。可見 cmp 在堆上的地址為 「0x804b410」,而 cmp->do_something(1) 所對應的地址 「0x804b430」 此刻已被 「x42x42x42x42」 所覆蓋,如下圖所示。
當程序執行至 cmp->do_something(1) 時觸發 Segmentation fault,此時 EIP 為 「x42x42x42x42」,表明程序的控制流已被劫持。
通過以上簡要分析可知,demo_bin 中存在堆溢出漏洞,可導致控制流劫持。在此基礎上,下文主要介紹如何通過 Angr 實現對該漏洞的自動化挖掘以及利用。
0x01 Angr AEG
完整的 AEG 過程,在邏輯上大致可分為以下幾個環節:
1) 漏洞挖掘,Angr 主要採用帶有前置約束及路徑選擇策略的符號執行;
2) 崩潰現場分析:EIP 狀態、內存布局;
3) 約束條件構造;
4) 約束求解,Exploit 生成;
1、漏洞挖掘
在本例中,主要是針對控制流劫持漏洞的挖掘。利用符號執行檢測控制流劫持,關鍵在於 EIP,若 EIP 完全被符號變數所覆蓋,則代表著控制流可以被劫持,此時 Angr 會拋出 unconstrained 狀態。
solve.py 中 65 ~ 84 行,通過搜索二進位程序的狀態空間以實現漏洞挖掘。由於 demo_bin.c 中的漏洞邏輯較為簡單,因此在挖掘過程中並未加入複雜的前置約束以緩解路徑爆炸,也未採用額外的路徑搜索策略,僅使用 SimulationManager 的 step() 方法,循環執行,直到出現 unconstrained 狀態。
注意,在設置 SimulationManager() 時,save_unconstrained 必須設置為 True。
2、崩潰現場分析
腳本運行不久後,便會觸發 unconstrained 狀態,此時需要對崩潰現場進行分析,以判定 unconstrained 狀態的可利用性。
1) EIP 可控性分析
solve.py 中使用 fullysymbolic() 方法檢查 EIP 中符號變數的數量。其中 state.arch.bits 代表系統字長(The number of bits in a word),state.solver.symbolic() 用以判斷輸入數據是否為符號變數,該方法在 ./angr/stateplugins/solver.py 中實現:
當 EIP 完全被符號變數覆蓋時,代表控制流已被劫持,此時堆及 EIP 狀態如下:
在實際調試過程中,會出現多次 unconstrained 狀態,但前若干次符號變數均未能完全覆蓋 EIP,因此並不能利用。最終 EIP 被符號變數完全覆蓋時,其內容如下:
2)內存布局分析
在觸發控制流劫持後,需要分析當前狀態下內存中符號變數的分布情況。solve.py 中的 findsymbolicbuffer() 實現相關功能,主要包括查找符號輸入、追蹤符號變數兩個部分。
(1) 查找符號輸入
Angr 在處理 scanf 的輸入數據時,採用 streams 模式。默認情況下,stdin、stdout、stderr 均採用該模式。state.posix.stdin 為輸入程序的全部符號變數,調試結果如下:
(2) 追蹤符號變數
通過 state.solver.getvariables() 追蹤內存中的符號變數。該函數位於 ./angr/stateplugins/solver.py 中,返回結果如下:
state.memory.addrsforname() 返回符號變數對應的地址,結果如下:
至此,已完成漏洞挖掘與崩潰現場的分析,後續需要結合 Exploit 的方式(ret2text、ret2syscall、ROP 等),構造完整的約束條件,並求解。
3、約束條件構造
對於 Exploit 自動生成問題來說,其關鍵是構造合適的約束條件,並利用 SMT(Satisfiability Modulo Theories) 約束求解器求解,若約束可解,則生成成功,否則生成失敗。其中包括路徑約束、EIP 約束、shellcode 約束等。
本例中,Exploit 方式採用 ret2text 且不考慮安全機制,因此僅需找到一片足以放置 shellcode 的連續空間即可。檢查空間連續性的函數如下:
solve.py 中的 shellcode 大小為 22 位元組,最終構造約束條件如下:
4、約束求解
通過 ep.satisfiable() 對約束條件 extraconstraints=(memory == scbvv,ep.regs.pc == buf_addr) 的可解性進行判斷。最終判定為約束可解,並生成 Exploit:
0x02 小結
本文以簡單的堆溢出為例,展示了如何利用 Angr 自動生成 Exploit。與此同時,也展示了 AEG 的完整過程。文中並未涉及複雜漏洞的自動利用及符號執行所面臨的路徑爆炸、路徑選擇、約束求解等問題。由於筆者接觸 Angr 時間不久,文中難免存在理解不當之處,望各位大佬批評指正(Orz...)。
*
本文原創作者:xiaohan0x00,本文屬FreeBuf原創獎勵計劃,未經許可禁止轉載


※某疑似針對中東地區的APT攻擊事件分析
※「三英戰呂布」,看我如何抓出那些流氓APP
TAG:FreeBuf |