SafeSEH原理及繞過技術淺析

SafeSEH原理及繞過技術淺析html

 

 

做者:magictongshell

時間:2012年3月16日星期五windows

 

摘要:主要介紹SafeSEH的基本原理和SafeSEH的繞過技術,重點在原理介紹。數組

關鍵詞:SafeSEH;繞過技術;異常處理安全

 

目錄cookie

前言框架

SafeSEH的保護原理ide

(1)      二進制層面函數

(2)      系統層面工具

怎麼關掉編譯器的SafeSEH支持

怎樣檢測一個PE文件是否啓用了SafeSEH

繞過方法簡介

參考文獻

 

前言

設計SafeSEH保護機制的目的,覺得了防止那種攻擊者經過覆蓋堆棧上的異常處理函數句柄,從而控制程序執行流程的攻擊。

自Windwos XP SP2以後,微軟就已經引入了SafeSEH技術。不過因爲SafeSEH須要編譯器在編譯PE文件時進行特殊支持才能發揮做用,而xpsp2下的系統文件基本都是不支持SafeSEH的編譯器編譯的,所以在xpsp2下,SafeSEH尚未發揮做用(VS2003及更高版本的編譯器中已經開始支持)。

從Vista開始,因爲系統PE文件基本都是由支持SafeSEH的編譯器編譯的,所以從Vista開始,SafeSEH開始發揮他強大的做用,對於之前那種簡單的經過覆蓋異常處理句柄的漏洞利用技術,也就基本失效了。

 

SafeSEH的保護原理

SafeSEH的基本原理很簡單,即在調用異常處理函數以前,對要調用的異常處理函數進行一系列的有效性校驗,若是發現異常處理函數不可靠(被覆蓋了,被篡改了),當即終止異常處理函數的調用。不過SafeSEH須要編譯器和系統雙重支持,缺乏一個則保護能力基本就喪失了。下面從兩個方面來闡述怎樣來實現SafeSEH。

(1)二進制層面

首先咱們先看看編譯器作了些什麼事情(經過啓用連接選項/SafeSEH便可使編譯出來的二進制文件具有SafeSEH功能,微軟VS2003及之後的編譯器已經默認支持)。在編譯器生成二進制IMAGE的時候,把全部合法的SEH函數的地址解析出來,在IMAGE裏生成一張合法的SEH函數表,用於異常處理時候進行嚴格的匹配檢查。可使用VC下面的dumpbin工具查看一個二進制文件的config信息,這樣調用dumpbin /loadconfig file_all_path_filename。

輸出的多是下面這樣(注:tttt.exe使用vs2005編譯):

Dump of file H:\Prj_N\tttt\Release\tttt.exe

 

File Type: EXECUTABLE IMAGE

 

  Section contains the following load config:

 

            00000048 size

                   0 time date stamp

                0.00 Version

                   0 GlobalFlags Clear

                   0 GlobalFlags Set

                   0 Critical Section Default Timeout

                   0 Decommit Free Block Threshold

                   0 Decommit Total Free Threshold

            00000000 Lock Prefix Table

                   0 Maximum Allocation Size

                   0 Virtual Memory Threshold

                   0 Process Heap Flags

                   0 Process Affinity Mask

                   0 CSD Version

                0000 Reserved

            00000000 Edit list

            00403018 Security Cookie

            00402360 Safe Exception Handler Table

                   1 Safe Exception Handler Count

 

    Safe Exception Handler Table

 

          Address

          --------

          004018A1  __except_handler4

 

  Summary

 

        1000 .data

        1000 .rdata

        1000 .rsrc

        1000 .text

 

注意裏面加粗標紅的部分,這就是該二進制文件裏面的SEH異常處理函數地址表。上面的輸出實際上涉及以下的一個結構,是保存在二進制文件裏面的一份配置表:

#include <windows.h>

extern DWORD_PTR __security_cookie;  /* /GS security cookie */

 

/*

 * The following two names are automatically created by the linker for any

 * image that has the safe exception table present.

*/

 

extern PVOID __safe_se_handler_table[]; /* base of safe handler entry table */

extern BYTE  __safe_se_handler_count;  /* absolute symbol whose address is

                                           the count of table entries */

typedef struct {

    DWORD       Size;

    DWORD       TimeDateStamp;

    WORD        MajorVersion;

    WORD        MinorVersion;

    DWORD       GlobalFlagsClear;

    DWORD       GlobalFlagsSet;

    DWORD       CriticalSectionDefaultTimeout;

    DWORD       DeCommitFreeBlockThreshold;

    DWORD       DeCommitTotalFreeThreshold;

    DWORD       LockPrefixTable;            // VA

    DWORD       MaximumAllocationSize;

    DWORD       VirtualMemoryThreshold;

    DWORD       ProcessHeapFlags;

    DWORD       ProcessAffinityMask;

    WORD        CSDVersion;

    WORD        Reserved1;

    DWORD       EditList;                   // VA

    DWORD_PTR   *SecurityCookie;

    PVOID       *SEHandlerTable;

    DWORD       SEHandlerCount;

} IMAGE_LOAD_CONFIG_DIRECTORY32_2;

 

const IMAGE_LOAD_CONFIG_DIRECTORY32_2 _load_config_used = {

    sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_2),

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    &__security_cookie,

    __safe_se_handler_table,

    (DWORD)(DWORD_PTR) &__safe_se_handler_count

};

 

(2)系統層面

基本過程以下(XP SP2和VISTA同樣)。

加載準備過程:

加載PE文件時,定位和讀出合法SEH函數表的地址(若是該IMAGE是不支持SafeSEH的,則這個SEH函數表的地址爲0),並使用共享內存中的一個隨機數加密。將加密後的SEH函數表地址,IMAGE的開始地址,IMAGE的長度,合法SEH函數的個數,做爲一條記錄放入ntdll(ntdll模塊是進行異常分發的模塊)的加載模塊數據內存中。

異常發生後,異常處理過程以下(RtlDispatchException框架僞碼):

void RtlDispatchException(...)

{

if (exception record is not on the stack)

goto corruption;

if (handler is on the stack)

goto corruption;

if (RtlIsValidHandler(handler, process_flags) == FALSE)

goto corruption;

// execute handler

RtlpExecuteHandlerForException(handler, ...)

...

}

RtlDispatchException()這個函數的檢測主要分三步,首先檢查異常處理節點是否在棧上,若是不在棧上程序將終止異常處理,其次檢查異常處理句柄是否在棧上,若是在棧上程序將止異常處理,這兩個檢測能夠防止那種在堆上僞造異常鏈和把shellcode放置在棧上的狀況。最後檢測handler的有效性,這纔是SafeSEH的重點。

下面看一下RtlIsValidHandler的僞碼(vista sp1):

BOOL RtlIsValidHandler(handler)

{

if (handler is in an image)

{

         // 在加載模塊的進程空間

if (image has the IMAGE_DLLCHARACTERISTICS_NO_SEH flag set)

return FALSE; // 該標誌設置,忽略異常處理,直接返回FALSE

if (image has a SafeSEH table) // 是否含有SEH表

if (handler found in the table)

return TRUE; // 異常處理handle在表中,返回TRUE

else

return FALSE; // 異常處理handle不在表中,返回FALSE

if (image is a .NET assembly with the ILonly flag set)

return FALSE; // .NET 返回FALSE

// fall through

}

 

if (handler is on a non-executable page)

{

         // handle在不可執行頁上面

if (ExecuteDispatchEnable bit set in the process flags)

return TRUE; // DEP關閉,返回TRUE;不然拋出異常

else

raise ACCESS_VIOLATION; // enforce DEP even if we have no hardware NX

}

 

if (handler is not in an image)

{

         // 在加載模塊內存以外,而且是可執行頁

if (ImageDispatchEnable bit set in the process flags)

return TRUE; // 容許在加載模塊內存空間外執行,返回驗證成功

else

return FALSE; // don't allow handlers outside of images

}

 

// everything else is allowed

return TRUE;

}

 

對上面的僞碼的理解,請看代碼註釋和流程圖:

僞碼裏面的ExecuteDispatchEnable和ImageDispatchEnable位標誌是內核KPROCESS結構的一部分,這兩個位用來控制當異常處理函數在不能夠執行內存或者不在異常模塊的映像(IMAGE)內時,是否執行異常處理函數。這兩個位的值能夠在運行時修改,不過默認狀況下若是進程的DEP被關閉,則這兩個位置1,若是進程的DEP是開啓狀態,則這兩個位被置0。

在進程的DEP是開啓的狀況,有兩種異常處理函數被異常分發器認爲是有效的:

(a)異常處理函數在進程映像的SafeSEH表中,而且沒有NO_SEH標誌。

(b)異常處理函數在進程映像的可執行頁,而且沒有NO_SEH標誌,沒有SafeSEH表,沒有.NET的ILonly標誌。

在進程的DEP關閉的狀況下,有三種狀況異常處理函數被異常分發器認爲是有效的:

(a)異常處理函數在進程映像的SafeSEH表中,而且沒有NO_SEH標誌。

(b)異常處理函數在進程映像的可執行頁,而且沒有NO_SEH標誌,沒有SafeSEH表,沒有.NET的ILonly標誌。

(c)異常處理函數不在當前進程的映像裏面,可是不在當前線程的堆棧上。

這裏的僞碼是很是簡單的,還有不少值得探討的問題,譬如ntdll裏面,當前異常處理句柄是怎麼和SEH表進行對比的等等,不過這暫時不列入討論。

 

怎麼關掉編譯器的SafeSEH支持

雖然我不知道你爲何要這麼作,並且我以爲很瘋狂,可是方法仍是有的,在編譯器的屬性框Liker|CommandLine的Additional options 加入/SAFESEH:NO便可,見下圖。

 

怎樣檢測一個PE文件是否啓用了SafeSEH

前面介紹過數據目錄裏面的一個結構(IMAGE_LOAD_CONFIG_DIRECTORY),該結構的成員SEHandlerTable是指向合法SEH處理程序地址列表的指針,成員SEHandlerCount是數目。而IMAGE_LOAD_CONFIG_DIRECTORY這個結構體只有/SAFESEH選項設置了才存在,所以,就能夠根據它來判斷PE文件是否加了/SAFESEH連接選項。這個結構在PE中的偏移由PE附加頭IMAGE_DATA_DIRECTORY 數組的第11項指定。

#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG  10  // Load Configuration Directory

 

繞過方法簡介

(1)利用堆地址覆蓋SEH結構繞過SafeSEH

上面講過,在禁用DEP的進程中,異常分發器容許SEH handler位於除棧空間以外的非映像頁面。也就是說咱們能夠把shellcode放置在堆中,而後經過覆蓋SEH跳至堆空間以執行shellcode,這樣便可繞過SafeSEH保護。

 

(2)利用沒有啓用SafeSEH保護的模塊繞過SafeSEH

在介紹原理時講過,在國內,目前大部分的PC都是安裝的Windows XP,也就是說對於大部分Windows操做系統,其系統模塊都沒有受到SafeSEH保護,能夠選用未開啓SafeSEH保護的模塊來利用,另外,如今還有不少VC6編譯的軟件,這些軟件自己和自帶的dll文件,都是可能沒有SafeSEH保護的。這時就可使用它裏面的指令做爲跳板來繞過SafeSEH。

(3)利用加載模塊以外的地址繞過SafeSEH

一樣是根據SafeSEH的原理可知,對於加載模塊以外的地址,SafeSEH一樣是不進行有效性檢測的(固然假設是DEP是關閉的,或者DEP已經被繞過)。

注:繞過方法這裏沒有細講,緣由是沒有找到很好的例子,在《0day安全:軟件漏洞分析技術》上面有本身書籍做者本身寫的例子。之後這塊再詳說。

 

參考文獻

[1] Preventing the Exploitation of Structured Exception Handler (SEH) Overwrites with SEHOP

http://blogs.technet.com/b/srd/archive/2009/02/02/preventing-the-exploitation-of-seh-overwrites-with-sehop.aspx(能夠列入翻譯計劃)

 

[2] SafeSEH筆記http://pstgroup.blogspot.com/2007/08/tipssafeseh.html

 

[3] /SAFESEH (Image has Safe Exception Handlers)

http://msdn.microsoft.com/en-us/library/9a89h429(VS.80).aspx

 

[4] 0day安全:軟件漏洞分析技術(第二版)

 

[5] Bypassing Browser Memory Protections

 

[6] pecoff_v8

 

https://blog.csdn.net/magictong/article/details/7517630

 

寫的挺好,但IMAGE_LOAD_CONFIG_DIRECTORY這個結構體並非只有/SAFESEH選項設置了才存在,更準確的檢查是檢測這個結構不存在,或才存在時SEHandlerTable是否爲0

相關文章
相關標籤/搜索