前言
由于网络攻击技术的迅猛发展趋势,很多浏览器开发商也都在自己的产品加入了类似于杀毒软件之类的功能,以帮助保护用户免受安全方面的威胁,比如 Internet Explorer ( IE ) 和 Edge 相比以前,除了在用户体验上更好外,在功能上也都发生了重大变化。它们都提供了一些基本的缓解措施,这些措施一起不仅使得各种漏洞无法被利用,而且还大大提高了攻击者开发利用漏洞的成本。
由于这些变化,确定它们崩溃的可利用性变得越来越复杂,因为在分析过程中必须考虑到这些缓解的影响。
Use-after-free 漏洞(简称 UaF 漏洞)是当前最流行的高危内存破坏漏洞,目前针对 UaF 漏洞的检测工作并不完善,原因是 UaF 漏洞产生的特征是分配内存、释放内存、使用已释放的内存并按顺序出现,而这 3 种事件可能出现在程序的任何位置,需要跟踪较长的执行序列并搜索潜在的危险事件序列才能检测到该漏洞,这很大程度上提高了检测的难度。
UaF 漏洞产生的主要原因是释放了一个堆块后,并没有将该指针置为 NULL,这样导致该指针处于悬空的状态,同样被释放的内存如果被恶意构造数据,就有可能会被利用。我们可以根据悬空指针的存储位置将 UaF 漏洞分为三大类:栈、堆和寄存器。
我们已经制定了两种主要的缓解措施来预防 UaF:
1. 内存保护(适用于 IE10 及以下版本),内存保护旨在保护对象免受 UAF 的影响,其中引用存储在栈中或寄存器中。
2.MemGC 机制(适用于 Edge 及 IE11),MemGC 的基本思想与内存保护(Memory Protector,MP)相似,都是利用类似标记清除法的 GC 去缓解 UAF 类型的漏洞,并且 MemGC 在 MP 的基础上进行了加强。当 MP 要实际释放内存块时会进行标记,如果栈中存在指向这个内存块指针那么就不会进行释放。不过,MP 没有保护堆中的指针,可能存在堆上的悬空导致 UAF 的情况。
MemGC 是 MP 的替代品,目前在 Edge 和 IE11 上启用。只有当栈、堆或寄存器上没有引用时,才会释放受保护的对象,从而提供完整的覆盖。
可利用性和服务
MemGC 机制(适用于 Edge 及 IE11)
1. 我们认为 MemGC 可以很好的缓解 UAF,并且不会为它们专门发布安全更新。
2. 在这种情况下,唯一的例外是零写入对象会导致可利用的状态,尽管我们还没有看到这种情况的发生。
内存保护(适用于 IE10 及以下版本)
1. 我们认为基于栈和基于寄存器的 UAF 可以通过这种保护得到极大缓解,并且不会为它们发布安全更新,除非有下面所说的特殊情况。
2. 内存保护是不会缓解基于堆引用的 UAF,因此仍将通过安全更新来解决它们。
内存保护
内存保护(MP)是最初于 2014 年 7 月针对所有受支持的 Internet Explorer 版本引入的一种缓解方式,但现在仅适用于 IE 10 及更低版本。它旨在缓解由于存储在栈或寄存器上的悬空指针而导致的 UaF 漏洞。从设计层面看,它的工作原理如下:
1. 在对象实例(object instance)上调用 delete 时,其内容为零写入,并将其放入队列中。一旦队列达到阈值大小,我们就开始查看释放队列中的每个对象实例是否安全。
2. 为了测试释放对象实例是否安全,我们扫描寄存器和所有指针对齐的栈条目,以查看是否存在指向该对象的指针。如果未找到指针,则释放该对象,否则该对象将保留在队列中。
工作原理的第(1)步会将对象的潜在释放延迟到稍后的某个时间点,由于这个时间点具体可由攻击者控制,因此它不具有安全性缓解属性。
为了更容易确定这些漏洞的可利用性,内存保护有一种称为 "Stress mode" 的模式。在此模式下,内存保护的延迟释放模块会被禁用,这样栈或寄存器扫描就会在每次释放时发生,而不是在队列达到阈值大小时发生。可以使用以下注册表项启用它:
HKLM:/Software/Microsoft/Internet Explorer/Main/Feature Control/FEATURE_MEMPROTECT_MODE/iexplore.exe DWORD 2
注意,此注册表项和 "Stress mode" 仅适用于内存保护机制,并不适用于 MemGC。
示例崩溃
通过强制在尽可能早的时间释放对象实例来禁用内存保护的延迟模块,现在我们可以根据第 2 步的原理,来集中精力确定可利用性,如下面的示例所示。
在这种情况下,我们有一个 UaF 漏洞所导致的接近于悬空(near-null )的解除引用。回顾一下,我们可以看到 eax 的值之前已经设置了一些指令:
如果我们在内存中查看这个对象,我们看到它已经被写为 0,通过检查PageHeap End Magic,我们可以看到这个堆块仍然在 "Stress mode" 下分配:
现在我们需要查看是否存在对此对象实例的任何栈引用,从调用 delete 时的调用帧 ( call frame ) 开始。这可以使用 windbg 脚本完成,例如,可以扫描一个对象的引用,该对象的基本地址存储在大小为 0x30 的 ebx 中。
使用内存保护检查栈引用位置
在本文的示例中,我们在栈中找到了对对象实例的单个引用。有了这些信息,我们现在必须检查哪个调用帧包含此引用。
以下,就是一个对象被删除时的调用栈示例。
如果存在对栈或寄存器上的对象实例的引用,则内存保护将永远不会释放该对象实例。因此,如果在 frame_2 中第一次调用 delete 点,直到在 frame_5 中出现几乎为空的解除引用而崩溃时,始终存在栈引用。那么,攻击者就无法释放和重新分配或控制对象实例。
在此示例中,我们通过扫描栈(在 0x1024ae9c 处)找到的引用存储在 frame_8 中,由于此引用始终存在于 frame_2 中的释放点和 frame_8 中的崩溃点之间,因此我们认为这种情况不可利用,因为它会被内存保护缓解。
除此之外,还可能出现另外两种主要情况:
1. 如果栈引用的位置是 frame_3 而不是 frame_8,则在没有栈引用时,在释放对象和崩溃点之间就存在一段空白时间。这种情况可能就会被攻击者利用的,因为如果这些点之间的代码路径可以稍微改变一下,就可以强制另一个调用被删除,从而发起攻击。
2. 在 "Stress mode" 下运行时,在压力模式下运行时,由于进行延迟的悬空模块被禁用 ( 通常是由于引用存储在堆上 ) ,所以崩溃可能发生在已释放的块上。
MemGC
MemGC 是内存保护的新替代品,目前可在 Edge 和所有 IE11 版本中使用,并以与内存保护类似的方式缓解 UaF 漏洞。但是,它还通过扫描堆以获取对受保护对象类型以及栈和寄存器的引用来提供额外的保护。MemGC 将自由写入 0 并将延迟实际的空闲时间,直到触发垃圾收集并且未找到对释放的对象的引用。
就像内存保护一样,缓解 UaF 漏洞很可能会导致 near-null 指针取消引用,或者根本就不会导致崩溃。如果你怀疑 near-null 指针取消引用是一个圈套,可以使用以下步骤验证这一点:
1. 找到读取 near-null 值的位置,确定对象的基本指针。
如果我们转储这个对象,如前所述,我们可以看到它已经被写为 0。
2. 使用在第一步中找到的基指针,跟踪并查找此块的分配调用栈。如果对象是用 edgehtml!MemoryProtection::HeapAlloc ( ) 或 edgehtml!MemoryProtection::HeapAllocClear ( ) 来分配的,这意味着该对象是由 MemGC e.g。
类似地,当对象被释放时,它将通过 edgehtml!MemoryProtection::HeapFree ( ) e.g 来执行。
为了再次检查漏洞是否已成功缓解,我们可以扫描堆和栈上对该对象的引用情况。
为了扫描栈,我们可以使用与内存保护部分中描述的技术。然后再使用与上述相同的标准来确定可利用性,如果在释放点和崩溃点之间存在栈引用,我们认为 MemGC 的缓解效果会特别好。
在扫描堆时,我们使用了类似的方法。首先扫描堆以查找基指针和基指针之间的值,其中还有我们感兴趣的对象的 object_size 的引用。如果找到任何引用,我们只需要检查一下它们与哪些对象相关联。如果包含引用的对象也被 MemGC 跟踪,即通过 HeapAlloc ( ) 或 HeapAllocClear ( ) 分配,那么 MemGC 将不会释放我们感兴趣的对象,所以我们认为是 MemGC 极大地缓解了它的影响。
在本文的示例中,如果我们使用上面的栈扫描命令,我们会看到栈上有一个引用,阻止对象在删除和崩溃点之间被释放,这使得 MemGC 成功地缓解了它的影响。
总结
总之,这些新的缓解措施通过阻止 UaF 漏洞的利用,来显着增强浏览器的安全性。当 IE 和 Edge 中对漏洞进行分类缓解时,需要考虑这些缓解的行为,以确定这些漏洞的可利用性。
留言与评论(共有 0 条评论) |