CVE-2017-5375&CVE-2017-5400&CVE-2016-9079浅析-firefox中的JIT喷射
来源:互联网 发布:怎样显示淘宝价格曲线 编辑:程序博客网 时间:2024/04/30 11:48
CVE-2016-9079是去年影响较大的一个firefox浏览器中SVG Animation模块的UAF漏洞。当用户使用firefox浏览器浏览包含恶意Javascript和SVG代码的页面时,会允许攻击者在用户的机器上远程执行代码。攻击者利用该漏洞,对Windows用户的firefox和Tor浏览器进行了针对性的攻击,并可能获取到了部分匿名用户的真实IP。分析认为,一些情报机构可能利用了该漏洞来收集个人信息。前一段时间,有人在github贴出了一份这个漏洞的exploit,本文将详细分析漏洞的成因,EXP的构造和JIT喷射。
漏洞成因
漏洞发生在nsSMILTimeContainer::NotifyTimeChange中。
先来看看EXP构造的页面和触发的过程。
在trigger中ic的结束时间和ia的结束时间相关,animateB的结束时间又和ic的结束时间相关,但是animateB起始时间又大于ia的结束时间,所以实际上animateB永远不会开始。在UpdateCurrentInterval函数中将mElementState标记为STATUS_POSTACTIVE,这会创建一个新的interval,并在随后调用RegisterMilestone函数。
当创建一个新的interval时,RegisterMilestone会向container的nsSMILTimeContainer对象的mMilestoneEntries数组中增加一个新的entry,数组因此被释放并重新分配,使得NotifyTimeChange中的指针p引用无效的内存。
在exploit函数中完成喷射操作之后调用pauseAnimations最终会调用到NotifyTimeChange触发漏洞。
执行流
我们来看看代码中heap spray的对象是怎么构造的。
首先,exploit函数中构造的对象将执行流引导到heap_spray_fake_objects函数中构造的对象。
如注释所示,我们可以看到,heap_spray_fake_objects函数中构造的对象最终将执行流引导到JIT payload。在101C0CB8 处call dword ptr [eax+138h]进入JIT payload。
JIT 喷射
JIT喷射大大降低了利用像UAF这种内存损坏错误的门槛,因为攻击者只需劫持指针跳转到JIT喷射的shellcode。JIT喷射中机器代码可以隐藏在诸如JavaScript的高级语言的常量中:这绕过了DEP;攻击者可以强制JIT编译器将这些常量发送到可预测地址的许多可执行代码区域中:这绕过了ASLR。实现第一点可以在ASM.JS代码中注入NOPS(0x90)。
VAL = (VAL + 0xA8909090)|0;VAL = (VAL + 0xA8909090)|0;
Firefox的ASM.JS编译器生成以下x86机器代码。
00: 05909090A8 ADD EAX, 0xA890909005: 05909090A8 ADD EAX, 0xA8909090
当我们跳到偏移01(第一个指令的中间)时,我们可以执行我们的隐藏代码。
01: 90 NOP02: 90 NOP03: 90 NOP04: A805 TEST AL, 0506: 90 NOP07: 90 NOP08: 90 NOP09: A8...
因此,在我们的四字节常量中有三个字节来隐藏我们的代码,一个字节(0xA8)将ADD EAX,…指令包含在像TEST AL,05这样类似NOP的指令中。实现第二点可以多次请求ASM.JS模块。以Firefox 50.1.0的源代码为例,每次请求ASM.JS模块都会调用CodeSegment::create,它会调用AllocateCodeSegment。
AllocateCodeSegment中进一步调用AllocateExecutableMemory。
AllocateExecutableMemory调用VirtualAlloc,返回一个64KB对齐的新的RW(PAGE_READWRITE)区域。
返回到CodeSegment::create之后,ASM.JS代码被复制到RW区域。ExecutableAllocator::makeExecutable调用VirtualProtect使得RW区域可执行(PAGE_EXECUTE_READ)。
多次请求一个ASM.JS模块导致会创建许多RX区域。由于VirtualAlloc(64KB)的分配粒度,我们可以选择一个固定地址(如0x1c1c0000),并且可以确定喷射的机器代码位于那里。可以在https://github.com/nmatt0/CVE-2016-9079找到去年的利用代码。它在内存中解析PE头寻找dll加载基址绕过ASLR。
很明显JIT喷射的代码量更少也更简单,是一种不错的思路。作者也给了JIT Spray的例子:WinExec_cmd_Firefox_50.1.0.html和WinExec_cmd_Firefox_50.1.0_dynamic.html,在windbg中手动设置EIP即可完成喷射。关于这个JIT喷射的漏洞编号是CVE-2017-5375。在详细介绍补丁及其绕过导致CVE-2017-5400之前,以下是Firefox中ASM.JS常量中隐藏x86代码的两种方法。如前所述,我们可以在ASM.JS常量赋值中隐藏x86指令。
VAL = (VAL + 0xA8909090)|0;VAL = (VAL + 0xA8909090)|0;
然而,常量折叠可以轻松地破坏我们隐藏的NOP。如果一个编译器折叠常量,它可能会发出一个指令,直接用MOV EAX 0x51212120分配加法的结果。因此,我们的代码将会消失。使用ASM.JS的外部函数接口(ffi)可以避免这种情况。
function asm_js_module(stdlib, ffi, heap){ 'use asm'; var ffi_func = ffi.func function payload_code(){ var val = 0; val = ffi_func( 0xa9909090|0, 0xa9909090|0, 0xa9909090|0, 0xa9909090|0, 0xa9909090|0, ...
在调用它之前,相应的x86代码准备函数ffi_func()的五个整数参数(0xa9909090)。
3f: c70424909090a9 mov dword ptr [esp],0A9909090h46: c7442404909090a9 mov dword ptr [esp+4],0A9909090h4e: c7442408909090a9 mov dword ptr [esp+8],0A9909090h56: c744240c909090a9 mov dword ptr [esp+0Ch],0A9909090h5e: c7442410909090a9 mov dword ptr [esp+10h],0A9909090h
再次,如果我们跳到偏移0x52处第三条指令的中间,我们就可以进入我们注入的代码(NOP-sled)。
52: 90 nop53: 90 nop54: 90 nop55: a9c744240c test eax,0C2444C7h5a: 90 nop5b: 90 nop5c: 90 nop5d: a9c7442410 test eax,102444C7h62: 90 nop
这样,我们可以再次隐藏ASM.JS常量中的三字节长指令,而不会在运行时间内重新同步原始指令流。但空间有限。指令MOV DWORD PTR [ESP+0x7c],0xa9909090由c744247C909090a9表示。如果堆栈偏移较高,另一个操作码使用具有四字节的偏移而不是一个字节的偏移,指令 MOV DWORD PTR [ESP+0x80],0xa9909090就会变成c7842480000000909090a9以保持符号正确。因此,我们可以使用上述技巧来隐藏0x80/4×3=0x60字节的payload。然而,我们可以使用两个字节的ASM.JS常量作为payload字节,另外两个字节作为相对短跳转(ebXX)到下一个payload字节。这允许我们摆脱0x80/4=32个参数和0x60个payload字节的限制。例如,我们使用0x07eb9090作为常量来隐藏两个NOP和一个JMP。
00: c78424800000009090eb07 mov dword [esp + 0x80], 0x07eb90900b: c78424840000009090eb07 mov dword [esp + 0x84], 0x07eb909016: c78424880000009090eb07 mov dword [esp + 0x88], 0x07eb9090
当我们从偏移7处开始执行时,我们的两个NOP和随后的JMP被命中以使注入的代码运行。
07: 90 nop08: 90 nop09: eb07 jmp 0x12...12: 90 nop13: 90 nop14: eb07 jmp 0x1d...1d: 90 nop1e: 90 nop1f: eb07 jmp 0x28
这与HITB-JIT-Spray-Attacks-and-Advanced-Shellcode中所展示的技术相似。最多使用两个字节长的指令。建立一个解析VirtualAlloc,分配RWX的内存,将stage1 shellcode复制到它,并跳转到它的stage0 payload仍然足够。好,现在我们来看看firefox是怎么修复的。
- 代码分配的地址被随机化。
- 32位中每个进程的可执行ASM.JS/WASM代码的数量被减少为128MB(修复后不久,Firefox 51.0.1中128MB的限制增加到160MB)。
但是如果VirtualAlloc在指定的randomAddr上失败,那么我们回到以前的未受保护代码。另外,假设将一个模块的大小保持在64KB以下,那么最多可以分配160MB/64KB=2560个ASM.JS模块。我们的计划如下。
- 使用不相关的不可执行内存占用64KB的对齐地址。使用类型化数组可以轻松完成堆喷射:这减少了可用的64KB基地址的数量。
- 喷射尽可能多的ASM.JS实例。由于AllocateExecutableMemory中的回退代码,可用的64KB对齐地址越少,我们使用JIT代码占用可预测地址的可能性越大。
- 通过触发垃圾回收器来释放1中分配的内存,因为我们不需要它。
这导致了CVE-2017-5400,并在Firefox 52中修复。
参考资料
https://github.com/rh0dev/expdev
https://github.com/nmatt0/CVE-2016-9079
https://rh0dev.github.io/blog/2017/the-return-of-the-jit/
https://rh0dev.github.io/blog/2017/the-return-of-the-jit-part-2/
https://community.rapid7.com/community/metasploit/blog/2016/12/29/a-friendly-fireside-foray-into-a-firefox-fracas
- CVE-2017-5375&CVE-2017-5400&CVE-2016-9079浅析-firefox中的JIT喷射
- CVE
- [漏洞] CVE-2017-1000364/CVE-2017-1000365/CVE-2017-1000366
- CVE-2017-8759
- CVE-2017-0214
- CVE-2017-8759 复现
- CVE-2017-11882复现
- CVE-2017-0214Poc
- 浅析CVE-2012-4220
- 浅析CVE-2009-2692
- 浅析CVE-2012-0056
- 浅析CVE-2014-0196
- 浅析CVE-2013-6282
- 浅析CVE-2015-3636
- CVE-2017-8464 转载poc
- CVE-2017-12617漏洞分析
- CVE-2017-7269浅析-IIS6.0栈溢出漏洞
- CVE-2017-6008浅析-HitmanPro内核池溢出漏洞(Win7)
- 友元类实现人员信息录入与排序
- 多路转接服务器之select
- HBase数据存储
- 3.Jfinal连接Mysql数据库(使用前面第2篇的工程)
- git学习经验分享(二)
- CVE-2017-5375&CVE-2017-5400&CVE-2016-9079浅析-firefox中的JIT喷射
- spark源码阅读一-spark读写hbase代码分析
- 【基础题】--实现二叉树的前序 / 中序 / 后序非递归遍历
- 分享一些自己的学习过程和学习方法
- Tesseract in Java
- 接口测试学习
- UVA 136 & POJ1338 Ugly Numbers
- Retrofit的简单使用
- Unity游戏接入百度移动广告