当前位置: 首页 > 科技观察

如何保护您的应用程序免受堆喷射

时间:2023-03-16 16:27:21 科技观察

为您的软件构建强大的安全性至关重要。恶意行为者不断使用各种类型的恶意软件和网络安全攻击来破坏所有平台上的应用程序。您需要了解最常见的攻击并找到缓解这些攻击的方法。本文不是关于堆溢出或堆利用的教程。在其中,我们探讨了允许攻击者利用应用程序中的漏洞并执行恶意代码的堆喷射技术。我们定义什么是堆喷射,探索它的工作原理,并展示如何保护您的应用程序免受它的影响。什么是堆喷射,它是如何工作的?堆喷射是一种用于促进任意代码执行的漏洞利用技术。这个想法是在目标应用程序的可预测地址处提供一个shellcode,以便可以使用漏洞利用来执行这个shellcode。该技术由称为堆喷射的漏洞源代码的一部分实现。开发人员在实现动态内存管理器时面临许多挑战,包括堆碎片。一个常见的解决方案是以固定大小的块分配内存。通常,堆管理器对块的大小以及分配这些块的一个或多个保留池有自己的偏好。堆喷射导致目标进程不断地根据需要逐块分配内存,依靠其中一个分配将shellcode放置在所需地址(不检查任何条件)。堆喷射本身不会利用任何安全问题,但它可以用来使现有漏洞更容易被利用。必须了解攻击者如何使用堆喷射技术来了解如何缓解它。下面是常见攻击的样子:堆喷射如何影响进程内存堆喷射攻击有两个主要阶段:1.内存分配阶段。一些流连续分配大量具有相同内容的固定大小的内存块。2.执行阶段。这些堆分配之一接收进程内存的控制权。如您所见,堆喷射漏洞利用看起来像一个连续的垃圾邮件,以大小和内容相同的块形式出现。如果堆喷射攻击成功,则控制权将传递给这些块之一。为了执行此攻击,恶意行为者需要有机会在目标进程中分配大量所需大小的内存,并用相同的内容填充这些分配。这种说法似乎过于大胆,但最常见的堆喷射攻击示例涉及破坏Web应用程序漏洞。任何支持脚本语言的应用程序(例如,带有VisualBasic的MicrosoftOffice)都是堆喷射攻击的潜在受害者。因此,在一个流的上下文中预测攻击是有意义的,因为脚本通常在单个流中执行。然而,攻击者不仅可以使用脚本语言来执行堆喷射攻击。其他方法包括将图像文件加载到进程中,并使用HTML5引入的技术以非常高的分配粒度喷射堆。这里的问题是哪个阶段是可疑的,我们可以在哪里进行干预并试图弄清楚是否正在进行攻击?内存分配阶段,当一些流填满大量内存时,已经很可疑了。但是,您应该问问自己是否可能存在误报。例如,您的应用程序中可能存在确实在循环中分配内存的脚本或代码,例如数组或特殊内存池。当然,脚本会在完全相同的堆块中分配内存的可能性非常小。但是,它仍然不是堆喷射的关键要求。相反,您应该注意执行阶段,因为分析接收进程内存控制的堆分配总是有意义的。因此,我们的分析将特别注意包含潜在shellcode的已分配内存。为了区分堆喷射shellcode的执行与正常的JIT代码生成,您可以分析分配某个内存块的最新流分配,包括流中的相邻分配。请注意,堆中的内存始终分配有执行权限,这允许攻击者使用堆喷射技术。堆喷射缓解基础要成功缓解堆喷射攻击,我们需要管理接收内存控制的过程、应用挂钩并使用额外的安全机制。保护您的应用程序免受堆喷射执行的三个步骤是:1.拦截NtAllocateVirtualMemory调用2.在尝试分配可执行内存期间使其不可执行3.注册一个结构化异常处理程序(SEH)以在现在执行内存时处理异常让我们详细探讨每个步骤。接收内存控制我们需要监控目标进程如何分配内存,检测动态分配内存的执行情况。后者假定堆喷射期间分配的内存具有执行权限。如果数据执行保护(DEP)处于活动状态(对于x64,默认情况下它始终处于活动状态)并且尝试执行未经执行许可分配的内存,则会生成异常访问冲突。恶意shellcode可以预期在没有DEP的应用程序中执行(这不太可能),或者使用脚本引擎在堆中分配内存,默认情况下具有执行权限。我们可以通过拦截可执行内存的分配并以分配它的漏洞无法检测到的方式使其不可执行来阻止恶意代码的执行。因此,当exploit认为喷射可以安全执行并尝试将控制委托给喷射堆时,将触发系统异常。那么,我们就可以分析这个系统异常了。首先,让我们从用户模式进程的角度来探讨在Windows中工作的内存是什么样的。以下是通常分配大量内存的方式:其中:HeapAlloc和RtlAllocateHeap是从堆中分配一块内存的函数。NtAllocateVirtualMemory是一个低级函数,它是NTDLL的一部分,不应直接调用。sysenter是切换到内核模式的处理器指令。如果我们设法替换NtAllocateVirtualMemory,我们将能够拦截进程内存中的堆分配流量。应用程序挂钩为了拦截目标函数NtAllocateVirtualMemory的执行,我们将使用mhook库。您可以选择原始库或改进版本。使用mhook库很简单:您需要创建一个与目标函数具有相同签名的钩子,并通过调用Mhook_SetHook来实现它。挂钩是通过在函数体上使用jmp指令覆盖函数序言来实现的。如果您已经使用过钩子,那么应该没有任何困难。安全机制有两种安全机制可以帮助我们减轻堆喷射攻击:数据执行保护和结构化异常处理。结构化异常处理或SEH是一种特定于Windows操作系统的错误处理机制。当发生错误(例如,被零除)时,应用程序的控制权被重定向到内核,内核会找到一系列处理程序并逐个调用它们,直到其中一个将异常标记为“已处理”。通常,内核会在检测到错误的那一刻起允许进程继续执行。从进程的角度来看,DEP看起来像是在内存中执行时带有EXCEPTION_ACCESS_VIOLATION错误代码的SEH异常。对于x86应用程序,我们有两个问题:DEP可以在系统参数中关闭。指向处理程序列表的指针存储在堆栈中,这提供了两个潜在的攻击向量:处理程序指针覆盖和堆栈替换。在x64应用程序中,不会发生这些问题。防止堆喷射攻击现在,让我们开始吧。为了减轻堆喷射攻击,我们将采取以下步骤:1.表单分配历史2.检测shellcode执行3.检测喷射表单分配历史为了拦截动态分配内存的执行,我们将PAGE_EXECUTE_READWRITE标志更改为PAGE_READWRITE。让我们创建一个结构来保存分配:接下来,我们将为NtAllocateVirtualMemory定义一个钩子。这个钩子将重置PAGE_EXECUTE_READWRITE标志并使用重置标志保存分配:一旦我们设置了钩子,任何具有PAGE_EXECUTE_READWRITE位的内存分配都将被修改。当试图将控制权传递给这块内存时,处理器会产生一个我们可以检测和分析的异常。在本文中,我们忽略多线程问题。然而,在现实生活中,最好分别存储每个流的分配,因为shellcode执行预计是单线程的。检测shellcode执行我们现在将为SEH注册一个处理程序。这个处理程序通常是这样工作的:1.获取触发异常的指令的地址。如果此地址属于我们保存的区域之一,则此异常已由我们的操作触发。否则,我们可以跳过它,让系统继续寻找相关的handler。2.搜索heapspraying。如果可疑地执行动态分配的内存,我们必须对检测到的攻击做出反应。否则,我们需要还原以便应用程序可以继续工作。3、使用NtProtect函数(PAGE_EXECUTE_READWRITE)恢复该区域的原始参数。4.将控制返回给流程。以下是shellcode检测的代码示例:目前,我们有一种机制可以监视应用程序中的shellcode并检测它何时执行。在现实生活中,我们还需要执行两个步骤:拦截NtProtectVirtualMemory和NtFreeVirtualMemory函数。否则,我们将没有机会监视进程内存的相关状态。这是一个碎片问题:我们需要存储和更新进程的可执行内存映射,这是一项非常重要的任务。例如,我们的应用程序可以使用NtFree函数来释放我们保存区域中间的一些页面,或者将它们的标志更改为NtProtect。我们需要跟踪和监控此类案件。使用Execute分析所有可能的标志(一组允许我们执行内存内容的可能值),例如PAGE_EXECUTE_WRITECOPY标志。检测堆喷射使用上面的代码,我们在动态内存正在执行时停止了一个应用程序,并获得了最新分配的历史记录。我们将使用此信息来确定我们的应用程序是否已被破坏。让我们探讨堆喷射检测技术的两个步骤:首先,我们需要确定我们将存储多少分配以及在发生异常时我们将分析其中的多少分配。请注意,我们对相同大小的分配感兴趣。因此,如果流中的内存分配了不同的大小,我们可以允许流继续执行,因为这不太可能是堆喷射攻击。此外,在分配边界之间存在空间的情况下,我们可以排除堆喷射攻击的可能性,因为堆喷射意味着连续的内存分配。接下来,我们需要选择堆喷射检测的标准。检测堆喷射的一种有效方法是在内存分配中搜索相同的内容。这个重复的内容很可能是shellcode的副本。例如,假设我们有10,000个具有相同数据的相同位移的分配。在这种情况下,最好从接收控件当前分配的位移开始搜索。识别堆喷射的建议算法我们建议使用所描述的技术并注意以下四个标准,以排除可能显着降低应用程序速度的不必要检查:1.为每个线程定义节省的内存分配量。2.设置保存内存分配的最小大小。拦截一页大小的分配会造成不合理的内存节省。堆喷射通常使用为应用程序的特定堆管理器选择的巨大值。数十页和数百页似乎更相关。3.定义发生异常时要分析的最新分配数。如果我们处理过多的分配,就会降低应用程序的效率,因为对于动态内存的每次执行,我们都必须读取大区域的内容。4.设置预期的最小shellcode大小。如果我们搜索的代码太小,就会增加误报的数量。结论我们探索了一种使用挂钩和内存保护机制检测堆喷射攻击的方法。在我们的项目中,这种方法在测试和堆喷射检测中显示出极好的结果。