SafeSEH原理及绕过技术浅析
SafeSEH原理及绕过技术浅析
作者:magictong
时间:2012年3月16日星期五
摘要:主要介绍SafeSEH的基本原理和SafeSEH的绕过技术,重点在原理介绍。
关键词:SafeSEH;绕过技术;异常处理
目录
前言
SafeSEH的保护原理
(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
[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
SafeSEH原理及绕过技术浅析的更多相关文章
- [转]STUN和TURN技术浅析
[转]STUN和TURN技术浅析 http://www.h3c.com.cn/MiniSite/Technology_Circle/Net_Reptile/The_Five/Home/Catalog/ ...
- Linux_x86下NX与ASLR绕过技术
本文介绍Linux_x86下NX与ASLR绕过技术,并对GCC的Stack Canaries保护技术进行原理分析. 本文使用存在漏洞代码如下: /* filename : sof.c */ #incl ...
- UAC 实现原理及绕过方法-打洞专用
首页 新随笔 订阅 管理 随笔 - 7 文章 - 0 评论 - 0 UAC 实现原理及绕过方法 目录 0x01 UAC 实现方法(用户登陆过程)0x02 UAC 架构0x03 触发UAC0x0 ...
- 《Python3反爬虫原理与绕过实战》作者韦世东
可以用(k1,k2)-k1来设置,如果有重复的key,则保留key1,舍弃key2/打印appleMap{1=Apple{id=1,name=苹果1,money=3.25,num=10},2=Appl ...
- Web安全--XSS现代WAF规则探测及绕过技术
XSS现代WAF规则探测及绕过技术初始测试 1.使用无害的payload,类似<b>,<i>,<u>观察响应,判断应用程序是否被HTML编码,是否标签被过滤,是否过 ...
- typecho流程原理和插件机制浅析(第二弹)
typecho流程原理和插件机制浅析(第二弹) 兜兜 393 2014年04月02日 发布 推荐 1 推荐 收藏 14 收藏,3.7k 浏览 上一次说了 Typecho 大致的流程,今天简单说一下插件 ...
- typecho流程原理和插件机制浅析(第一弹)
typecho流程原理和插件机制浅析(第一弹) 兜兜 393 2014年03月28日 发布 推荐 5 推荐 收藏 24 收藏,3.5k 浏览 虽然新版本0.9在多次跳票后终于发布了,在漫长的等待里始终 ...
- 爬虫技术浅析 | WooYun知识库
爬虫技术浅析 | WooYun知识库 爬虫技术浅析 好房通ERP | 房产中介软件最高水准领导者 undefined
- 爬虫技术浅析 | z7y Blog
爬虫技术浅析 | z7y Blog 爬虫技术浅析
随机推荐
- 学习《概率机器人》中英文PDF+Probabilistic Robotics
研究机器人时,使机器人能够应对环境.传感器.执行机构.内部模型.近似算法等所带来的不确定性是必须面对的问题. <概率机器人>对概率机器人学这一新兴领域进行了全面的介绍.概率机器人学依赖统计 ...
- Scrapy框架之日志等级
一.日志等级 CRITICAL:严重错误 ERROR:一般错误 WARNING:警告 INFO: 一般信息 DEBUG:调试信息 [注意:默认的日志等级是DEBUG] 二.日志等级设置 修改setti ...
- pip版本及升级 pip安装指定模板
昨天在微信聊天,一妹子9点的时候告诉我她要看书了,让明天聊,瞬间自己心中那颗学习的种子燃烧起来,思来想去还是继续学习自己之前未学好的python吧,因为之前有了点点的python基础,所以本次打算从p ...
- 今日SGU 5.23
SGU 223 题意:给你n*n的矩形,放k个国王,每个国王不能放在别的国王的8连边上,问你有多少种方法 收获:状态DP,因为每行的放置只会影响下一行,然我们就枚举每行的状态和对应的下一行的状态,当两 ...
- 今日SGU 5.17
SGU 119 题意:给你一个0-15组成的4*4的矩形,问你能不能回到正常 收获:把矩形变成一维数组,然后判断当前矩形状态到目标状态(逆序对为15)逆序对和0到目标的奇偶性是否不相同,证明题,引荐大 ...
- 【Uva 1627】Team them up!
[Link]: [Description] 给你n个人; 有一些人之间有认识关系 a认识b,b不一定认识a 让你把这n个人分成两组 使得这两组中的每一组: 组内的人与人之间都相互认识. 并且,使得两组 ...
- android 动态设置TextView值,例:金额添加
一说到动态递增设置TextView值,非常多人应该立即就想到起个线程,让后在线程中睡眠指定时间,使用handler发送消息更新TextView值! 这样是实现了动态递增设置TextView值可是效率不 ...
- .Net视图机制
.Net会有默认的约定. HomeController下面的Index,会默认渲染Home/Index.cshtml. 当然可以设置成别的,比如设置成About. using System; usin ...
- solrj简介
SolrJ基于httpClient: 使用SolrJ操作Solr会比利用httpClient来操作Solr要简单. SolrJ是封装了httpClient方法,来操作solr的API的. SolrJ底 ...
- ElasticSearch概述和定义
福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号: 大数据躺过的坑 Java从入门到架构师 人工智能躺过的坑 Java全栈大联盟 ...