频道栏目
首页 > 资讯 > 网络安全 > 正文

利用EMET漏洞来禁用EMET保护

16-11-04        来源:[db:作者]  
收藏   我要投稿

Microsoft开发的增强减灾体验工具包(EMET)是一种为用户模式程序添加安全缓解措施的项目,而不是内置于操作系统中的程序。 它是作为动态链接库(DLL)在“受保护”程序中运行的,通过对代码进行各种更改,以使得漏洞利用变得更加困难。

在过去的研究和实际攻击中已经观察到了EMET缓解措施被绕过的情况[2,3,4,5,6,7,8]。 通常,Microsoft通过更改或添加缓解措施来作为应对,以击败任何现有的绕过手段。 EMET旨在提高exploit开发者的开发成本,而不是一个“傻瓜防御利用缓解解决方案”[1]。 因此,毫不奇怪,在受保护程序的进程空间内具有读/写能力的攻击者可以通过逐条的击败每个缓解措施来绕过EMET [2]。

如果攻击者可以通过很少的工作来绕过EMET,那么就已经挫败了EMET增加expoit开发者成本的目的。我们在禁用EMET的新技术部分就提出了一个这样的技术。Microsoft已经发布了一个修补程序来解决EMET 5.5中的此问题。

在讲解这种新技术之后,我们又讲述了一些以前的用于绕过或者禁用EMET的技术。如果想知道EMET具体包含了什么样的保护,请参见附录。

一种可以禁用EMET的新技术

EMET将emet.dll或emet64.dll(取决于是x64还是x86)注入到每个受保护的进程中,该进程安装Windows API Hook(由DLL例如kernel32.dll、ntdll.dll和kernelbase.dll导出的函数)。 这些钩子使EMET能够分析关键API中的任何代码调用,并确定它们是否合法。 如果代码被认为是合法的,EMET挂钩代码跳回到请求的API,否则就会触发异常。

然而,在EMET中存在着负责卸载EMET的代码。这些代码系统地禁用EMET的保护并将程序设置回到以前未受保护的状态。只需定位并调用此函数即可完全的禁用EMET。 在EMET.dll v5.2.0.1中,此函数位于偏移量0x65813。 跳转到此函数将导致后续调用,从而删除EMET已安装的Hook。

此功能之所以存在,是因为emet.dll需要包含从进程干净退出的代码。而且很方便的是,它可以从DllMain被调用。

DllMain的函数原型如下:

BOOL WINAPI DllMain(

_In_ HINSTANCE hinstDLL,

_In_ DWORD fdwReason,

_In_ LPVOID lpvReserved

);

第一个参数是此DLL模块加载到的基地址。 第二个参数提供了PE加载器在DLL加载或卸载时使用的参数,1或0。 如果fdwReason为1,则DLL知道它正在加载和初始化。 如果fdwReason参数为0(DLL_PROCESS_DETACH),则emet.dll会执行卸载代码,假设Dll被卸载那么他就会删除它自己设置的钩子和异常处理程序,从而简单地删除EMET的检查机制。 注意,这不会从内存中删除EMET模块,它只是确保其所有的保护都被禁用。
[page]

这种特性可以存在于任何的检测产品中,因为它依赖于用户空间的Hook,并且为了保证产品不会挂掉,必须有一个卸载例程来去除所有之前设置的保护检查。 EMET的DllMain可以通过一个小的ROP gadgets链找到(下一节会讲这个)。它只是跳到DllMain,并且带着正确的参数去卸载EMET设置的保护检查。

BOOL WINAPI DllMain (GetModuleHandleA("EMET.dll") , DLL_PROCESS_DETACH , NULL);

GetModuleHandleA函数并没有被EMET Hook,因为EMET不认为它是关键的Windows API。 我们使用这个函数来获取emet.dll的基地址。 由于PE头位于基址,我们必须使用它来传递给DllMain正确的第一个参数。

禁用EMET - 详解

删除EMET钩子的功能代码位于偏移量0x27298处(相对于模块基地址),如下图所示。

 

 

首先,函数循环遍历所有Detoured_API结构体,并且为每个关联的Detoured_API_Config结构体清零DetourFunctionPrologue域(下面展示了结构体的结构)。

Detoured_API结构体(如下所述)是一个链接列表,用于跟踪API是否主动绕行(就是是否使用Hook),并且指向有DetouredAPIConfig:

struct Detoured_API {

BOOL isActive; // 是否启用?,值为1时启用

PVOID DetouredAPIConfig; // 指向Detoured_API_Config结构的指针

PVOID nextDetouredAPI; // 指向下一个Detoured_API结构的指针

};

Detoured_API_Config结构(部分显示如下)存储关于绕行和其原始API的信息。

struct Detoured_API_Config {

PVOID DetouredWindowsAPI; //指向绕行的Windows API的指针

PVOID EMETDetouringFunction; // 指向EMET保护实现的函数

PVOID DetouredFunctionPrologue; // 指向Windows API序列的指针

...

}

对于每个Hook_Config,Patch_Functions恢复原始API的序列,如图2所示.Patch_Functions从EMETDetouringFunction检索原始函数序列的大小和地址,并将值传递给memcpy。 在每个API恢复到其原始状态后,Patch_Functions将ptrEffectiveFunction更改为直接指向原始API。

 

 

在循环遍历所有的detoured API并使用memcpy修补它们之后,你会发现Windows API中的所有跳转路径都已经消失了,如下面两张图所示,分别是在修复前后。

 

 

修复前

 

 

修复后

然后EMET会紧接着禁用EAF和EAF+保护,在偏移量为0x609D0的函数中,EMET将清零并重新初始化CONTEXT结构,并操作调试寄存器(如下图所示)。 然而,在函数结束时,EMET又调用了NtSetContextThread函数,这会导致调零寄存器置零,因此EAF

 

 

和EAF+保护都会被禁用掉。

最后,在偏移为0x60FBF的函数结束时,EMET调用了位于偏移0x60810的函数,那个函数又会调用RemoveVectoredExceptionHandler来删除之前被AddVectoredExceptionHandler函数添加的向量化异常处理(VEH)。
[page]

禁用EMET - 通过ROP实现

这里使用一个以前的漏洞CVE-2011-2371来演示,目前这个漏洞已经被修补了。我们在现有的漏洞exp上添加了rop gadgets,然后在启用EMET保护的情况下执行。当我们的ROP gadgets使用参数(EMET.dll基地址,0,0)来调用EMET.dll的DllMain函数之后,我们返回继续执行,所有放置在Windows API Hook中的跳转路径与EAF和EAF+保护一起都消失了。

MOV ESP,44090000 # ~ # RETN // STACKPIVOT

POP EAX # RETN // STORE GetModuleHandleA IAT POINTER INTO EAX

MOZCRT19+0x79010 // MOZCRT19!_imp__GetModuleHandleA

MOV EAX,DWORD PTR DS:[EAX] # RETN // GET GetModuleHandleA ADDRESS

PUSH EAX # RETN # // Call GetModuleHandleA("EMET.dll")

Return Address XOR EDX,EDX # RETN // ZERO OUT ECX

0x44090108 // "EMET" STRING ADDRESS (GetModuleHandleA PARAMETER)

OR EDX,EAX # ~ # RETN // STORE EMET.dll EMET_BASE_ADDRESS INTO EDX

POP EBX # RETN // STORE DllMain() PARAMETER1 ADDRESS (i.e. hinstDLL) INTO EBX

0x440900A4 // DllMain() PARAMETER1 (i.e. hinstDLL) ADDRESS

MOV DWORD PTR DS:[EBX],EAX # ~ # RETN // hinstDLL PATCH WITH EMET_BASE_ADDRESS

POP ECX # RETN # // STORE 0x3C (i.e. IMAGE_DOS_HEADER) INTO ECX

0x0000003C // IMAGE_DOS_HEADER OFFSET

ADD ECX,EDX # ADD EAX,ECX # ~ # RETN // EAX = EMET_BASE_ADDRESS+0x3C

MOV EAX,DWORD PTR DS:[EAX] # RETN // GET PE_HEADER OFFSET

POP ECX # RETN # // STORE AddressOfEntryPoint OFFSET INTO ECX

0x00000028 // AddressOfEntryPoint OFFSET

ADD ECX,EDX # ADD EAX,ECX # ~ # RETN // EAX = EMET_BASE_ADDRESS+PE_HEADER+0x28

MOV EAX,DWORD PTR DS:[EAX] # RETN // GET DllMain() OFFSET

POP ECX # RETN # // ZERO OUT ECX

0x00000000

ADD ECX,EDX # ADD EAX,ECX # ~ # RETN // EAX = EMET_BASE_ADDRESS+DllMain

Call EAX // CALL DllMain(GetModuleHandleA("EMET.dll") , DLL_PROCESS_DETACH , NULL)

0x42424242 // hinstDLL = GetModuleHandleA("EMET.dll") (TO BE PATCHED)

0x00000000 // fdwReason = DLL_PROCESS_DETACH

0x00000000 // lpvReserved = 0x00000000

在此之前的EMET绕过技术

以前的绕过EMET保护的技术通常是利用设计或者是代码实现的缺陷,EMET之所以可能会被绕过是因为一些模块或是API被设计者忽略了,并且这些模块和API都很不安全。我们在这里将描述部分这些绕过技术。

由于LoadLibrary是一个关键的API,所以如果它被一个ret或jmp指令调用,那么EMET 4.1就会引发一个异常。但是Jared DeMott发现如果通过call指令调用LoadLibrary ,而不是ret或jmp那么就可以成功的绕过EMET LoadLibrary保护[2]。

EMET会监视LoadLibrary API来防止加载UNC路径(即\\ evil \ bad.dll)。 但是Aaron Portnoy表明,这可以通过使用MoveFile API(这个函数没有被EMET 4.0监控)下载一个DLL文件,然后再由LoadLibrary API加载,从而绕过EMET。

EMET 4.1中的调用者保护会检查关键的API函数是被call指令调用,还是被ret指令或jmp指令调用的。因为后两种调用是ROP的常用手段,所以这种保护措施用于阻止攻击者进行ROP。 DeMott展示了通过一个合法的call来调用关键API函数从而绕过调用者保护的方法[2]。 DeMott并没有使用ret或jmp指令(这将导致EMET引发异常)直接调用VirtualAlloc API,而是在加载的模块之中找到一个对VirtualAlloc API的call调用指令,然后返回到该call指令的地址,EMET就不再会阻止函数的调用。

一些关键的Windows API函数位于kernel32.dll,ntdll.dll和kernelbase.dll模块中; EMET 3.5Hook了前两个模块导出的函数,但是却忘记了kernelbase.dll。 Shahriyar Jalayeri通过这一点来执行位于kernelbase模块中的VirtualProtect API,使内存变的可写和可执行[4]。但是,在EMET 4.0发布后,Deep Hooks保护会Hook住关键Windows API函数所调用的最底层函数。

Jalayeri还通过使用位于0x7ffe0000的_KUSER_SHARED_DATA结构(其具有固定地址)绕过EMET,这个结构在偏移0x300处存在指向KiFastSystemCall的SystemCallStub指针,这是一种执行sysenter指令的典型方式。这样,他能够通过在EAX寄存器中指定值的方式来调用任何系统调用(例如,ZwProtectVirtualMemory是0x0D7)。此外,Jalayeri能够通过用ret指令修改函数头的方式来使EMET完全失效。

EAF保护在我们访问指定模块(例如kernel32.dll,ntdll.dll和kernelbase.dll)中导出的函数时会使用调试寄存器来设置断点。可以使用访问导入表的shellcode来绕过这些断点保护(因为此保护仅保护了导出表)。

上一个EMET禁用技术

与保护的bypass技术不同,禁用EMET会完全关闭它所有的保护。例如,可以通过清除硬件断点(即清零调试寄存器)来禁用EAF保护(包括EAF+)。Piotr Bania使用未公开的Windows API NtSetContextThread和NtContinue来实现这一点,但是由于NtSetContextThread已经被EMET钩住,所以应该首先禁用其他EMET保护使NtSetContextThread可用。

Offensive Security发现大多数的EMET 4.1保护首先会检查emet.dll模块中导出的偏移为0x0007E220的全局变量值;如果该变量的值为零,则保护主体继续运行但是不会干涉攻击者执行代码[6]。事实证明,全局变量是用于打开/关闭EMET保护的全局开关,并且这个值处于可写数据段中,攻击者可以通过创建ROP gadgets来轻松地清零该变量。

在做了一些分析之后,我们发现EMET v2.1在偏移0xC410处拥有相同的全局开关,出于这个原因,我们怀疑EMET早期版本具有相同的全局开关的弱点,并且这个全局变量是在固定的地址上。这是EMET 5.0发布之前的情况。

Offensive Security发现,在EMET 5.0中,微软把这个全局变量放在一个大型的堆中结构体(即CONFIG_STRUCT)上,结构体的大小为0x560字节。然而,相同的思想仍然适用,因为存在指向CONFIG_STRUCT结构的指针位于固定偏移0x0AA84C处。作为保护,EMET使用EncodePointer函数对该指针值进行编码,并且每次EMET保护要使用该值时,它将使用DecodePointer函数对其进行解码,以获取CONFIG_STRUCT地址。清零CONFIG_STRUCT + 0x558将关闭大多数EMET保护。此外,要关闭EAF和EAF +,他们使用存储在CONFIG_STRUCT + 0x518的未钩指针指向NtSetContextThread。

在EMET 5.1中,攻击安全发现全局变量将编码的指针值保存到某个结构(即EMETd),该结构存储在偏移0xF2A30中。 EMETd结构具有指向CONFIG_STRUCT结构的指针字段,其将偏移CONFIG_STRUCT + 0x558处的全局开关保持为指针的编码的附加保护层。 EMET 5.1使用cpuid指令将返回的值与编码指针的值进行XOR。要解码CONFIG_STRUCT,它们使用emet.dll偏移量0x67372中的代码,它解码EMETd结构,然后返回CONFIG_STRUCT的解码指针。由于全局开关(即CONFIG_STRUCT + 0x558)存储在只读存储器页中,因此Offensive Security找到了一种方法,通过使用存储在EMET中的未挂钩指针以固定地址进行更改。他们使用一个指向存储在CONFIG_STRUCT + 0x1b8的ntdll!NtProtectVirtualMemory的未钩指针,将其标记为可写内存页,因此他们可以将CONFIG_STRUCT + 0x558处的全局开关置零。为了禁用EAF和EAF+,他们使用存储在CONFIG_STRUCT + 0x518处的指向NtSetContextThread的指针,与在禁用EMET 5.0时所做的是一样。

结论

这种新技术使用EMET本身来卸载EMET保护。它比以前发布的任何EMET禁用或bypass技术更加可靠,也更容易实现。整个技术适用于一个简单,直接的ROP链。它只需要通过调用GetModuleHandleA泄漏一个DLL的基地址(如mozcrt19.dll),而不需要进程空间的完全读取。因为emet.dll的DllMain函数被导出,所以这个技术不需要任何的针对版本的硬编码,并且该技术适用于所有测试版本的EMET(4.1,5.1,5.2,5.2.0.1)。

通过EMET来禁用EMET的代码包含了一个重要的新的攻击向量。找到DllMain并调用它关闭所有的EMET的保护明显比绕过每个EMETs保护更加容易。

特别感谢:Michael Sikorski,Dan Caselden,Corbin Souffrant,Genwei Jiang和Matthew Graeber。

附录

EMET保护

近几年来,EMET已经经过了逐步的发展,在这里提供了一个简要的介绍:

EMET 1.x, 2009年10月27日发布

SEHOP:防止结构化异常处理SEH被覆盖和篡改。

DEP:强制的DEP,因此栈和堆等数据段不再有执行权限。

空页面保护:预分配空页面,防止攻击者对空页面进行利用。

堆喷射保护:防止进行堆喷射(其实是把常用的堆喷射内存区域预先分配了。)

EMET 2.x,2010年9月2日发布

强制ASLR:强制执行ASLR,即使是不支持ASLR的老旧dll模块也会被强制启用ASLR。

导出表访问过滤:有的shellcode会遍历模块的导出函数,以解析如kernel32.dll, ntdll.dll和kernelbase.dll等模块导出的重要函数。EMET使用硬件断点来阻止访问这些模块导出表的任何线程,并且EMET会验证它们是否合法。

EMET 3.x,2012年5月25日发布

ROPGuard:使用ROPGuard来阻止ROP。

加载库检查:阻止通过通用命名约定(UNC)路径加载DLL文件。(ROP中会使用这种技术)

ROP缓解措施 - 内存保护检查:保护关键Windows API(如VirtualProtect),这些API可能被用于将堆栈标记为可执行。

ROP缓解措施 - 调用者检查:防止使用跳转或返回指令调用关键Windows API。

ROP缓解措施 - 堆栈翻转:检测堆栈是否已被翻转。

ROP缓解措施 - 模拟执行流程:通过操纵和跟踪堆栈寄存器,在调用关键Windows API后检测ROP Gadgets。

自底向上的ASLR:将随机化8位的熵添加到自底向上分配(包括堆,堆栈和其他内存分配)的基址。

EMET 4.x,2013年4月18日发布

深度Hook:启用此功能后,EMET不再只是挂钩上层的API函数,而是会一并挂钩那些上层API调用的底层函数。

反绕道:由于EMET在跳转(挂钩)Windows API函数的前言中放置跳转指令,所以攻击者可以制作一个返回到绕行跳转指令之后的指令的ROP。 此保护尝试停止这些旁路。

禁止重要函数调用:默认情况下,它不允许调用ntdll!LdrHotpatchRoutine以防止DEP / ASLR被绕过。 还可以配置其他功能。

证书信任(可配置证书锁定):在证书链信任验证过程中提供更多的检查和验证。 默认情况下,它仅支持Internet Explorer。

EMET 5.x,于2014年7月31日发布

攻击面减少(ASR):允许配置在某些应用程序中阻止加载模块的列表。

EAF +:与EAF类似,它提供了额外的功能来保护kernel32.dll,ntdll.dll和kernelbase.dll的导出表。 它还检测MZ / PE读取以及堆栈指针是否指向堆栈边界之外的某处或者是否与堆栈指针之间不匹配。

相关TAG标签
上一篇:一图看懂华为Mate9和Mate9保时捷设计全球限量版手机
下一篇:MS16-124:微软内核整型溢出漏洞
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站