CVE-2015-0311溢出分析II:绕过Windows 8.1 Update3上的CFG保护

怪狗 2016-7-31 Exploit 0 0

在3月初我们发布了前一篇《CVE-2015-0311溢出分析I:Adobe Flash Player中的UAF漏洞》

CFG——在2014年11月的Windows 8.1 Update 3中引入,在编译时在每一个直接调用前插入检查调用地址是否非法的处理。这使得之前传统的覆盖虚表指针,而后调用的手法变得不再那么有效了。

在阅读下面内容前,建议您先读一下前一篇,因为接下来我们只关注如何接管浏览器的运行流程。

CFG介绍

对比Flash 16.0.0.287前一篇中调用被覆盖的关键位置的虚表与Windows 8.1Update 3的对应位置。

CVE-2015-0311溢出分析II:绕过Windows

Windows 8.1Update 3中

CVE-2015-0311溢出分析II:绕过Windows

新增加的___guard_check_icall_fptr调用指针指向了ntdll!LdrpValidateUserCallTarget,这里将检查接下来需要调用的目标地址是否非法。如果,判断非法,则直接调用INT 0x29终结进程。

CVE-2015-0311溢出分析II:绕过Windows

如果,你还想了解更多关于CFG的攻防相关的知识。请参考我们的MJ大牛在2014年Power Of Community安全会议上发表的《Windows 10 CFG防护本质》

攻击方法

大家可以看到CFG在编译时,已经将大量的直接调用保护了起来。例如本文的目标Win8.1 Update 3上的Flash Player 16.0.0.287。有29238直接调用保护。

CVE-2015-0311溢出分析II:绕过Windows

一些对抗CFG的方法(摘自MJ大牛的文章):

        覆盖返回地址

        利用没有开启CFG的模块

        找到一个没有被CFG保护的直接调用

对于第一种方法,需要我们获得栈地址。尽管我们通过修改向量object的长度拥有了当前进程空间的读写权,我也找不到一个堆上的object作为起始地址的指针。

关于第二种方法需要依赖其他模块,这太愚蠢了

我之前的想法就是找到一处没有受CFG保护的直接调用的位置。

CVE-2015-0311溢出分析II:绕过Windows

这个函数的指针位于data段。虽然,可以覆盖。但是,这个函数是否能被我们调用到?或者这个函数只是在进程初始化的时候被调用到?

那么,我们还能不能找到一个没有CFG保护且可以直接被我们在AS脚本中调用的位置?

好吧,让我们想想刚才的介绍,CFG保护是在程序编译时,插入的。而我们的Flash Player内置虚拟机,从而实时的将byte code转成native code。而正是因为实时性,所以这些位置是没有CFG保护的。

找到一个没有CFG保护的直接调用

好了,让我们回到溢出。通过向量object的长度,我们拥有了对当前进程内存的读写全。与此同时,我们还有一个存有ROP链的ByteArray object。在这个ByteArray object+8的偏移位置,是一个虚表(虚表结构参考Github上的core/VTable.h)

CVE-2015-0311溢出分析II:绕过Windows

跟踪这个虚表,我们可以看到很多指针,比较这些指针指向的空间,我们会发现他们非常类似。参考刚刚的头文件,发现这些指向了很多MethodEnv object。(参考core/MethodEnv.h)

CVE-2015-0311溢出分析II:绕过Windows

观察这个object(我们选择VTable_object + 0xD4这里的指针),头四字节指向了MethodEnv的虚表,接下来的四字节指向了一个函数地址(如图中的0x601C0A70)

CVE-2015-0311溢出分析II:绕过Windows

经过检验,这个位置是由Flash Player的虚拟机实时解析的,没有CFG保护。而且在ByteArray object调用toString时,是一定会被调用的。

我们可以通过在VTable_object + 0xD4这里下读操作的硬断点,来观察虚拟机中解析的代码。

通过观察,我们发现调流程是,BaseExecMgr::invokeGeneric先调用BaseExecMgr::endCoerce(这段代码摘自core/exec.cpp)

// Invoker for native or jit code used before we have jit-compiled,
// or after JIT compilation of the invoker has failed.
Atom BaseExecMgr::invokeGeneric(MethodEnv *env, int32_t argc, Atom* atomv)
{
    MethodSignaturep ms = env->get_ms();
    const size_t extra_sz = startCoerce(env, argc, ms);
    MMgc::GC::AllocaAutoPtr _ap;
    uint32_t *ap = (uint32_t *)avmStackAlloc(env->core(), _ap, extra_sz);
    unboxCoerceArgs(env, argc, atomv, ap, ms);
    return endCoerce(env, argc, ap, ms);
}

而BaseExecMgr::endCoerce就直接调用了这个解析后的函数

Atom BaseExecMgr::endCoerce(MethodEnv* env, int32_t argc, uint32_t *ap, MethodSignaturep ms)
{
    [...]
    switch(bt){
    [...]
    default:
    {
        STACKADJUST(); // align stack for 32-bit Windows and MSVC compiler
        const Atom i = (*env->method->_implGPR)(env, argc, ap);
        [...]

溢出

既然找到了靠谱的触发流程,那么我们需要的只需让VTable_object + 0xD4指向一个恶意的MethodEnv object。当然没图说个锤子~正常情况下流程如下:

CVE-2015-0311溢出分析II:绕过Windows

而我们所期望的是这样:

CVE-2015-0311溢出分析II:绕过Windows

接下来是具体操作,通过Nicolas Joly的读取方法获得ByteArray object虚表的地址

var vtable_object:uint = leak_8_bytes(bytearray_object_pointer + 8);

然后计算VTable_object + 0xD4的地址,并覆盖(这里对ROP链的地址右移3位,是因为虚拟机的机制,前一篇中已经提到过了)

var target_address:uint = vtable_object + 0xd4;
/* 0x28: offset of the first element within the Vector object */
var idx: uint = (target_address - (address_of_vector + 0x28)) / 4;
this.the_vector[idx] = address_of_rop_chain >> 3;

最终,我们只需要调用ByteArray object的toString方法

new Number(this.the_vector[0].toString());

总结

可以看出CFG保护增大了溢出的成本和难度。但就像我们刚刚所做的,只要抓住关键点,也是可以被绕过的。

而在解释性语言大行其道的今天,存在这样问题的不仅仅是Flash Player,而如果没有经过特殊设计的解释器内核,是不可能在解释性语言中加入CFG保护的。

而利用解释器来绕过各种保护措施,也早已不是什么秘密。可以参考《Pointer inference and JIT spraying》(Dion Blazakis发表于2010年)和《Flash JIT – Spraying info leak gadgets》(Fermín Serna发表于2013年)。

本文由 360安全播报 翻译,转载请注明“转自360安全播报”,并附上链接。
原文链接:https://blog.coresecurity.com/2015/03/25/exploiting-cve-2015-0311-part-ii-bypassing-control-flow-guard-on-windows-8-1-update-3/

原文地址:http://www.77169.com/exploits/2015/20150326095619.shtm

转载请注明来自华盟网,本文标题:《CVE-2015-0311溢出分析II:绕过Windows 8.1 Update3上的CFG保护》

喜欢 (0) 发布评论