ღ Miranda

IE11 0day: CVE-2015-2425 UAF分析

前言

CVE-2015-2425Hacking team泄露出来的一个IE110day漏洞,影响了IE11及之前的版本。在一封Hacking Team高层收到的来自Vectra Networks安全公司的信件中被发现。Vectra Networks公司的研究者在信中向Hacking Team提供了对于Windows 7/8.1最新版的IE11的poc代码。但Hacking Team并没有购买,所以只泄露了poc,并没有攻击代码。

环境

测试环境是win8.132位,IE版本是IE11。

poc

poc.html:

把IE11附加到windbg上,然后运行poc.html,IE11崩溃到一个无法读取的地址:

windbg+IDA分析

由于崩溃在一个不可访问的地址,不好确定之前的指令,这时需要用到与栈回溯相关的命令,就是windbg中的k一系列命令:

每一行描述当前的一个栈帧,最上面的一行描述的是当前指令的返回地址:

由此可知崩溃的返回地址是63dfcf3c,在JavascriptFunction::CallFunction<1>中,看当前的esp也可以得出同样的结论:

为了看到函数是怎么调用的,需要用到.frame命令,使用.frame /c 1回到崩溃栈的第一层,也就是上层函数调用时的状态:

看到回到了上层函数中,eip的值为崩溃处的返回地址,在反汇编窗口可以看到上层函数,也可以用u命令:

产生崩溃的地方是63dfcf39call函数,调用的是[ebp-1Ch]处的函数指针,在IDA中看一下CallFunction<1>的定义:

这个函数是__fastcall方式调用的,__fastcall是一种快速调用方式,规定将前两个参数由寄存器ecxedx来传递,其余参数还是通过堆栈传递(从右到左),不同编译器编译的程序规定的寄存器不同。在Intel 386平台上,使用ECXEDX寄存器。

往前找更改ebp-1Ch内容的指令,只有63dfcec6处的mov指令:

edx是函数的第二个参数,也就是一个函数指针,所以63dfcf39处的指令是调用了函数指针所指向的函数,这样的话,还需要向上层看,到底是什么样的函数指针,回到上一层函数:

看到edx来源是[eax+0Ch],而eax的来源是[ebx+4]ebx来源于下面一句:

看一下这个函数的定义:

__thiscall为了解决类成员调用中this指针传递而规定的,__thiscall要求把this指针放在特定寄存器中,该寄存器由编译器决定。VC使用ecx。所以这里ecx里的指针就是this指针。梳理一下这里的过程:

推测这里调用了某个对象的成员函数,而这个对象是JavascriptFunction对象。

弄清楚后我们回到崩溃点所在的函数内,选择在call [ebp-1Ch]处下断点,断点会多次触发,触发5次后,停下,步入函数看:

63e0631b处的call后,eax就会变成一段已经释放内存的地址,看到这句的jmp指令跳到了eax指向的值,把这段函数汇编看一下:

eax里存放的应该是NativeCodeGenerator::CheckCodeGen的返回值,这次在这个函数上下断点再次调试,会触发六次断点,在返回前调用了NativeCodeGenerator::CheckCodeGenDoneeax置为了不可访问的地址:

看看NativeCodeGenerator::CheckCodeGenDone,这次还在调用NativeCodeGenerator::CheckCodeGen上下断点,六次断点后进入CheckCodeGenDone,发现里面还有一个call改变了eax的值,然后还有mov eax,edi:

再次用相同的方式调试,这次进入ScriptFunction::UpdateThunkEntryPoint看看,这个函数如下:

63e06604mov语句改变了eax:

这个函数返回后有一个mov eax,esi的指令,但这里edi值也已经是返回后eax的值了,看来还要追踪edi,看一下CheckCodeGenDone函数:

edi是作为参数传入UpdateThunkEntryPoint的,并且在函数内没有被改变,那么还要往回追踪,经过回溯,从CheckCodeGenDone函数开头开始跟,由于中间还有跳转,所以edi的值还会改变,跟到edi变成崩溃时的返回地址时可以得出:

那么还需要跟踪一下esi,可以看到来自eax,而eax来自ecx:

所以ecx中是CheckCodeGen的第一个参数:

ScriptFunction对象,说明在最后一次调用CheckCodeGen前这个对象就已经被释放了,用poi看看指针引用的过程,通过三次引用找到了那块已经释放的内存:

重启后在第五次断在63e0631b时,ebx指向了ScriptFunction对象,这时看一下触发漏洞的指针,然后在第六次时看一下指针:

两次指针并不相同,说明指针被改写了一次,重新运行一样在第五次断点被触发时看一下这里的内存,此时还没有被改写:

在这里可以下一个内存写入断点,看看什么时候被改写了:

真正的写入发生在上一句mov语句,这时的esi05a49120eax05a50000:

eax里的是上一个call的返回值,也就是InterpreterThunkEmitter::GetNextThunk的返回值,这次在63e06cd7下断点,会触发五次,最后一次时步入函数:

由于这个函数是__thiscall,所以ecx就是this指针,63e06df5就是把类的一个成员赋给了eax

为IE开启堆页,命令是:

再次下断点调试,看看这里堆的情况:

这时eax指向的内容还没有被释放,是一段函数代码:

那我们要找到这段内存是如何释放的,还是回到63e06cd7断点处,这次不进入函数,步过后那段内存并没有被释放,为了弄清楚在哪里被释放,给this指针和那段uaf的内存下访问断点:

断点触发时,09f90000还没有被释放,函数返回时已经被释放了,内存就是用FreeAllocations来释放,在EmitBufferManager类中还有NewAllocation函数,应该是分配内存的函数。

总结

所以漏洞的成因应该是在内存被FreeAllocations释放后又在JavascriptFunction::CallFunction<1>中使用而造成的UAF。由于这个漏洞的返回地址不可控,所以要用堆喷的方法的话可能还需要结合其他方法来绕过DEPASLR,暂时网上也没有成功的exp,在分析过程中参考了:
https://blog.vectra.ai/blog/microsoft-internet-explorer-11-zero-day

发表评论

电子邮件地址不会被公开。