如何确保程序中的崩溃不可利用?简而言之,答案很简单:假设每个崩溃都是可利用的,然后修复它!至少,这是一个质量问题,在产品交付给客户之前解决这个问题通常更便宜、更实用。执行确定可利用性所需的分析可能会相当昂贵。
分析与内存损坏相关的程序故障,以了解安全后果可能是一项复杂且容易出错的任务。必须考虑几个因素,包括缓冲区在内存中的位置、覆盖的可能目标、覆盖的大小、对覆盖期间可以使用的数据的限制、运行时执行环境的状态以及绕过任何现有缓解机制的能力。简而言之,您必须了解失败的根本原因,才能彻底回答这些问题。
记住,并非每一个失败都会以可观察的方式显现出来。其中一个例子是微软安全公告MS07-017中讨论的GDI远程代码执行问题。负责调用易受攻击的解析代码的软件使用异常处理程序来从几乎所有可能生成的异常中恢复,并像没有发生任何异常一样继续运行。另一个不太明显的例子可以在某些类型的堆栈和堆内存损坏中找到,可能发生了故障,但程序的当前状态及其执行环境没有显示任何明显的迹象。

本文提供了有关如何分析程序崩溃的指导,以考虑可能的安全隐患,例如启用任意代码执行的内存损坏或至少拒绝服务的情况。我们将列举您在查看这些类型的问题时可能遇到的常见硬件和软件异常。我们也会提供一些一般性的指导,你可以在这样的调查中使用。例如,下图给出了调查过程的图形路径,以帮助您确定特定崩溃是否可利用。重要的是要记住,这些只是指导原则,只有全面的根本原因分析才能确保您已正确诊断为不可利用的崩溃。新技术或现有攻击技术的变种一直在被发现。

 

最常见的崩溃原因是硬件或软件异常。典型的现代处理器可以生成许多不同类型的硬件异常,但在Windows环境中,只有其中一些会产生与软件安全相关的问题。最常见的硬件异常是访问冲突。我们将首先介绍如何分析硬件异常,然后是软件异常。

访问冲突

当指令或程序执行导致的内存访问不满足处理器体系结构或内存管理单元结构定义的某些条件时,现代处理器会生成访问冲突异常(0xc000005=状态访问冲突)。
虽然纯崩溃只能导致拒绝服务条件,但不能安全地假设崩溃不能用于实现更危险的效果,包括代码执行。在分析崩溃时,您应该假设整个内存体(除了一些小的异常)都处于潜在攻击者的控制之下;因此,在大多数情况下,访问冲突可能导致由攻击者控制的数据。此语句适用于指令读取或写入数据时发生的异常。
如果访问冲突会导致您的数据受到攻击者的控制,则从内存读取导致的每个访问冲突都会转化为加载攻击者控制的数据。这种行动的安全效果并不总是容易确定的。您可以对二进制或源代码执行完整的数据流分析,以找到源地址控制的范围以及在某些执行点向程序提供随机数据的结果。这是一项耗时且富有挑战性的任务。作为回应,我们开发了简单的启发式方法来快速分析代码执行潜力的读取访问冲突崩溃。
如下例所示,寄存器eax中的无效内存指针导致崩溃。在这种情况下,对内存内容的控制使攻击者能够完全控制程序流:

Application!Function+0x133:
3036a384 eb32            call     [eax]           ds:0023:6c7d890d=??
0:000> ub
mov    eax, [ebx]  ->  eax = invalid memory pointer
...                         (instructions not affecting register eax)
call        [eax]  ->  crash

如果攻击者无法充分控制正在读取的地址,则可以将其视为拒绝服务条件。例如,在典型的Windows用户模式环境中,在初始化的空指针处发生的、不受攻击者影响的崩溃本身不会导致代码执行。
在下面的示例中,您可以看到崩溃是由引用寄存器eax中的地址0值引起的:

Application!Function+0x133:
3036a384 8b14            mov     ecx, [eax]           ds:0023:00000000=??
0:000> ub
xor    eax, eax
...                         (instructions not affecting register eax)
cmp    ebx, 2
jne        label123
mov    ecx, [eax]  ->  crash, eax = 0 (NULL)

通过反汇编(使用Visual Studio命令行调试器中的ub命令),我们可以跟踪此寄存器中的数据流,直到确认寄存器中的值不会受到恶意输入的影响。事实上,在这个例子中,寄存器是通过自身的异或运算来调零的,直到到达崩溃指令时才使用它。有时,从崩溃的指令看不到缺陷的可利用性。例如,在分解以下指令之后,您可以看到,当失败的指令(在上一段中描述)后面紧跟着一个关键指令时,这是控制流的结果:

标签: none

添加新评论