【安全編譯選項-windows平臺】/NXCOMPAT(DEP數據執行保護)保護原理解讀

0x00 缺陷代碼與利用

1、實驗環境

 

環境

備註

編譯器

VS2017

 

操作系統

Windows7專業版

 

實驗其他條件

VS2017中工程禁用NXCOMPAT、SDL

爲研究/NXCOMPAT作用,先關閉其查看程序運行狀態。爲使得實驗代碼有漏洞利用關閉SDL選項(SDL開啓將造成示例代碼中有漏洞的危險函數無法使用)

2、實驗代碼

實驗代碼如下所示:

#include <stdio.h>
#include <stdlib.h>
#include<string.h>

#define PASSWORD "1234567"
#define BUFFERSIZE 1024

int verify_password(char *password,int len)
{
   int authenticated = 128;
   char buffer[4]; //字符串數組不大於4,GS將失效,代碼不會增加Cookie的檢查
   //char buffer[5];//字符串數組超過4,GS將生效,代碼增加Cookie的檢查
   authenticated = strcmp(password, PASSWORD);
   memcpy(buffer, password,len);//over flowed here!
   return authenticated;
}

int main()
{
   int valid_flag = 0;
   char password[BUFFERSIZE];
   memset(password,0, BUFFERSIZE);
   FILE * fp;
   if (!(fp = fopen("dep.txt", "r")))
   {
                  printf("open file failure!\n");
                  return 0;
   }
   fgets(password, BUFFERSIZE, fp);
   fseek(fp, 0, SEEK_END);
   int fileSize = ftell(fp);
   valid_flag = verify_password(password, fileSize);
   if (valid_flag)
   {
                  printf("incorrect password!\n");
   }
   else
   {
                  printf("Congratulation! You have passed the verification!\n");
   }
   fclose(fp);
   system("pause");
   return 0;
}

和上一期的/GS原理中的代碼業務邏輯是一樣的,只是改變了讀取文件的方式和溢出點使用的函數。

3、漏洞利用

在開啓/GS安全編譯選項的情況下,Buffer數組長度爲5時,將受到保護。但是當Buffer長度小於等於4時,verify_password函數將不會受到/GS的保護,如下圖所示:

如在/GS保護原理中所述的繞過方式,可尋找類似的不受保護的函數進行利用。下面是一個簡單的利用。

沒有/GS的保護,這樣不僅能夠覆蓋eip,可通過ret_to_libc執行任意函數或者跳到堆區或者.data數據區解釋數據爲指令並執行。也可以通過緩衝區溢出在棧中注入shellcode惡意代碼,並執行該惡意代碼。這裏只介紹利用棧溢出注入shellcode的利用方式。

爲了使用棧溢出進行漏洞利用,需要掌握該函數的棧幀情況,通過調試繪製如下函數棧幀情況和溢出漏洞利用原理:

我們在dep.txt中輸入如下二進制數據:

根據上述棧幀圖可知:

紅色框是填充數據

藍色框覆蓋了存儲的EBP數據

綠色框覆蓋了返回地址EIP,該地址爲底層庫中數據爲FFE4的地址,不同OS系統下或者通過重啓後(操作系統開啓了ASLR,每次重啓底層庫基地址會隨機化)是不一樣的,如果您要復現,通過調試並搜索數據找到該地址(再次不介紹如何查找,不是本文重點)。通過該指令作爲跳板地址,根據棧幀原理,函數退出後esp必然會指向shellcode處的地址,jmp esp則達到了通用跳轉到惡意代碼的地址,則不用關閉/GS原理介紹中需要事先關閉/DYNAMICBASE選項。

黑色框是Shellcode惡意代碼,功能是彈框,內容和標題都是“failwest”

    編譯該程序並運行,點擊忽略後(/GS中做了備註說明),出現了期望的彈框。表明漏洞利用成功。

0x01 安全編譯選項防護

所以並不是所有函數都會受到/GS保護,可利用沒有使用/GS保護措施或者不受保護的函數執行惡意流程或代碼。一般執行惡意代碼有兩種形式:

一種就是通過向數據區(堆、棧和.data數據區,例如全局變量)植入惡意代碼,並修改eip使其最終指向數據區中shellcode惡意代碼的地址,當函數返回時執行緩衝區中的代碼。PE文件生成時有段的概念,也就是將相同操作和屬性的數據放在同一個區域並標註權限。其中可執行代碼被分配在.text段,顯然在上述區域執行代碼都是不合理的。

另一種惡意代碼執行是通過改變棧中eip指向內存中已經存在的函數或者指令地址並push入惡意參數,函數返回時程序將被惡意用戶控制。

本文講解是針對第一種方式進行防護,防護在不合理區域進行指令解析與執行,這就是DEP(Data Execution Prevention)防護。

DEP功能的支持需要兩個方面的要求:CPU能力支持(識別內存頁是否有執行權限)和/NXCOMPAT安全編譯選項(指示該PE文件映射內存頁時是否採用DEP防護)。具體的,CPU需要支持DEP的功能,在執行代碼時,虛擬地址映射到物理地址,最小單元是頁,每個頁都有屬性數據結構標識該頁屬性。AMD中有一個NX標識,Intel有個DX標識位,他們的作用都是表明該頁是否可執行代碼,0標識該頁可執行代碼,一般text段對應的頁該值都是0,堆、棧、數據區的頁該爲則爲1 表明不可執行代碼。

首先需要CPU的支持,查看本機是否支持DEP的功能,按如下方式查看。若機器不支持DEP功能,即使編譯程序時加入/NXCOMPAT編譯選項保護也是無效。

也可通過如下方式查看某程序是否開啓了DEP保護。打開進程管理器,若沒有點擊查看->選擇列,勾選數據執行保護。可看到沒有使用DEP保護的應用,則可以逆向這些程序查找緩衝區溢出漏洞並利用。

在VS工程中加入/NXCOMPAT編譯選項:通過菜單中項目->工程屬性->C/C++->配置屬性的代碼生成開啓/GS編譯選項,如下圖所示:

    開啓該編譯選項後,重新編譯程序。再次使用上述惡意dep.txt數據運行本程序,發現程序直接崩潰,沒有彈出惡意顯示框。所以該編譯選項預防了在不合理區域的執行指令。

0x02 /NXCOMPAT原理解讀

爲了瞭解/NXCOMPAT的原理,我們查看開啓該選項並研究函數和PE文件的變化。

首先查看彙編發現增加該編譯選項前後verify_password函數彙編是沒有變化的。

猜測是通過PE文件某個標誌位表明需要採用DEP功能,通過在微軟官方PE文件的解讀中發現如下數據結構的解釋(截取了頭部和關注部分):

https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-image-only

PE可選文件頭數據結構有下述紅色框屬性:

    該值有如下說明:

    可知通過可選頭部該值可標識該PE文件是否採用DEP保護。所以我們通過比較工具查看增加/NXCOMPAT編譯選項前後的PE文件,具體的在二進制文件中“PE”字符串開頭偏移94字節的數據:

    上圖左邊是沒有設置選項的的PE, 這兩個字節爲80 40 ,右邊變爲 81 00 。根據該字段解釋0x01 00標識DEP保護,PE相應的進行了變化,使得PE加載映射時內存頁屬性執行DEP保護

    通過如下二進制工具可確認某程序是否採用了DEP保護。下圖所述數據爲81 40。DEP標識位爲0x01 00表明開啓了DEP保護。

0x03 /NXCOMPAT侷限性

然而/GS+/NXCOMPAT仍然不能杜絕惡意代碼的執行。正如上述所述的惡意代碼的第二種方式,可通過改變eip指向內存存在的指令或者函數(例如ret_to_libc方式),這也是下一次研究並引申出/DYNAMICBASE安全編譯選項的作用。

另一種侷限性就是機器CPU不支持DEP的功能,這個即使編譯代碼時加入/NXCOMPAT編譯選項,程序也不會受到保護。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章