Gadget构造:从JIT-ROP到对抗XnR

写在前面

简单地说,本文介绍了两种构造gadget的思路。(在浏览器支持JIT的情况下)

演进:关于JIT-ROP

我们知道,最初DEP的出现是为了对抗的栈溢出、堆溢出等这类劫持程序执行流的攻击手法。在这之后,攻击者为了绕过DEP,开始利用libc.so中的函数获取shell,也就是我们所说的Ret2libc攻击。更有效的防御手段还有ASLR地址随机化,使得libc.so加载的基址每次都会发生变化。当然,如果存在地址泄露等,ASLR仍可以被绕过。

于是乎,学术界前几年开始研究细粒度的ASLR,即使基址被泄露,攻击者也得不到准确的地址。

2013年s&p上发表了一篇文章提出JIT-ROP,这项工作极具创新性。其意义在于它提出了在运行时寻找gadget同时构造ROP链的概念。详细的过程是这样的:在存在一个内存泄露漏洞的情况下,得到一个运行时代码指针,它泄露当前4k内存页,因为当前页可能会有分支指向其他页,所以通过这个指针可以得到尽可能多的页。将当前页运行时反汇编后,获得所需要的gadget并构造ROP链。

也就是说,JIT-ROP是通过即时扫描有效内存,即时反汇编搜寻rop gadgets,即时拼接shellcode,从而绕过dep/aslr的,它在程序运行过程中动态执行,所依赖的资源在进程地址空间中获取。所以它和传统rop最大的不同在于:它可以使用任何代码指针初始化攻击环境,而传统的rop需要泄露函数的具体地址。

这种攻击手段可以绕过学术界过去提出的任意粒度的ASLR。

演进:关于XnR

为了针对JIT-ROP,14年ccs有一篇文章提出了XnR,利用软件模拟的方式实现了页的不可读。

既然页不可读,那么JIT-ROP所提出的运行时读内存页然后搜寻可用gadget这一思想就行不通了。

那么如何对抗不可读,提出使JIT-ROP攻击不需要“可读”这一条件,也可达成呢?这就是下面要讨论的内容了。

相关:关于JIT

在进入正题之前,先简单说说JIT引擎是什么。

2008年,浏览器的JavaScript引擎引入JIT技术。JIT是一种即时编译技术,是一种解决浏览器效率低下的方案。例如Javascript,它是典型的解释型语言。程序员写了一个for循环,解释器是不知道这是一个循环的,所以它将会一句一句的去循环执行这个循环。这也是为什么解释型语言的效率特别低,因为它不会预先编译。

JIT引擎的出现就是为了改善这一情况。它通过在运行时将部分语句编译为机器码,于是下次执行不需要再翻译,从而省去了解释开销,使得浏览器性能大幅度改善。

JIT与攻击

早期利用JIT新特性进行攻击的方案是在2010年blackhat大会上提出的Pointer Inference and JIT Spraying,将 ActionScript代码中进行大量的XOR操作。然后编译成字节码,并且多次更新到Flash VM中,这样它会建立很多带有恶意Xor操作的内存块。

但一方面这样的方法比较旧,另一方面,学术界中早已提出防御方法JITDefender。

准备工作

攻击模型

(1)存在内存泄露漏洞 (2)不考虑CFI (3)JavaScript环境

之所以如此,因为这篇文章的中心思想是,攻击者可以通过自己注入gadget实现绕过其他限制手段如XnR。关注点并非实现真实过程的控制流转移。

防御环境

(1)DEP:NX,可写不可执行 (2)ASLR: fine-grained,XnR应用于可执行文件,库,JIT编译而成的代码 (3)Non-Readable Code: 所有代码段不可读 (4)Hidden Code Pointers: 除了JIT-compiled的指针均已匿名 (5)JIT Hardening: JIT上的防御手段,比如randomized JIT pages, constant blinding,guard pages

攻击方法

方法一:利用条件跳转语句

原理:构造if/else,for/while,编译成包含可预知的条件跳转指令。

举例说明一下,如果我们需要的gadget是int 0x80;ret ,翻译成机器码也就是0xcd80c3

那么,则往if中写入0xc380cd字节大小的JS代码。

如上右图,攻击者是可以确定,编译结果中肯定包含‘je 0xc380cd’这一句的(x86/x64 架构采用小端存储)。所以说,通过变化if中js代码的大小,我们可以修改代码长度,改变跳转的距离。而每句JS代码编译后的大小是已知固定的。所以攻击者可以精确的控制所生成的代码长度。即,je后面的值,可以随意控制。

举个更具体的例子,在Chrome 33 (32-bit)/Chrome 51(64-bit)上,如果需要一个gadget:0xcd80c3。

S1: v=v1+v2, compiling to 0x10 bytes(64位 v=v)

S2: v=0x01010101, compiling to 0xd bytes.

使用S1 0c380c次(得到结果0xc380c0),

再使用S2 一次,求和即得到0xc380cd。

以上步骤可以注入自己想要的gadget,接下来就是要找到gadget所在的位置。

(1)首先通过内存泄露漏洞,得到JIT所编译的JavaScript代码的函数地址

(2)将这个函数的作为参数传给另一个函数,这样它就被push到stack中了(javascript的特性)。

(3)得到栈的信息,得到函数对象的指针,它包含了指向实际地址的指针。

方法二:利用直接调用语句

上一种方法对IE基本是无效的。IE部署了JIT-hardening策略,会随机插入NOP,改变条件跳转语句的值。这样,编译后的语句就不可控了。于是作者又提出使用直接调用指令实现攻击。如:call 0x1234560

原理:任何两个直接调用指令即时调用同一个函数,机器码是不一样的。其实和条件跳转的原理差不多。之前控制的是je指令后边的值,那它控制的是什么呢。其实利用的是call后面的这个值是一个相对地址,第一次调用产生的机器码是被调用者和下一次所调用的差。如下图。

假设FUN函数在2000的位置。那么在图中,连续CALL FUN_1,所以这个常数代表FUN1和第二条CALL FUN1(即0x5)的距离。即0x2000-0x5=0x1ffb。(0x5是一条call指令的大小)。这个例子里,攻击者得到的是0xe8fb1f0000。

嗯,以上是大概思路。具体攻击流程有三步:

(1)找到call指令后那个被调用函数的地址。

再列一遍这个式子,0x2000-0x5=0x1ffb。在这里0x2000就是被调用函数的地址。

总之,一般来说,在javascript环境下,可以利用当然是地址已知的helper函数或built-in函数(比如Math.random,String.substring)。

另外不得不说到IE,它的built-in函数在library中,而且采用了细粒度的ASLR。但是它们所产生的JS对象,未启用随机化,而且包含指向这个函数的指针。所以对于IE也是可行的。

(2)把call指令放到指定位置。

再列一遍这个式子,0x2000-0x5=0x1ffb

还是假设我们需要的gadget是int 0x80;ret (即0xcd80c3)。

0xcd80c3,包含三个字节。也就是说,上面那个减法式子的结果必须保证位数满足要求,要是你的距离才两位是怎么也构造不出三位的gadget的。于是为保证三字节距离,需要创建一个由直接调用语句组成的JS函数,使它在编译后覆盖0x100 00 00字节。即我们要求第一次和最后一次CALL之间的距离,至少为0x1 00 00 00字节指令。

(3)检查机制

还是要说到IE,它在编译时会随机插入NOP,所以基本上不能知道函数会被编译到哪个地址。而且,前提条件存在XnR,我们是不可以通过读取代码段来确定那个位置是否是所需要的gadget的。

那么,只能通过取得足够大小的代码空间,全部填满call指令,从而确保计算结果在距离范围内。

Javascript中的check address函数会读栈中数据,直到找到call指令放置的ret,可知是否放置正确。如果没有,重新编译,重复之前的步骤。

其他

这攻击主要针对的是x86 (32- or 64-bit)架构。在RISC架构中,如ARM,MIPS,由于硬件层次的原因,利用上文所述的方法不能成功攻击。除非在ARM架构下,攻击者强制切换为16-bit THUMB模式。

另外,虽然论文中作者是以浏览器为例,但其实采用jit引擎的东西应该都适用(?)。比如pdf阅读器是可以的。

总结

实际上本文的核心思想很简单:

(1)攻击者通过构造JS代码产生一个jit-compile gadget

(2)由于所产生的gadget是攻击者控制的,所以不需要搜索,不需要读页,即可被攻击者用来构造ROP链

(3)以上过程发生在运行时

参考文献

[1] What Cannot Be Read, Cannot Be Leveraged? Revisiting Assumptions of JIT-ROP Defenses. USENIX 2016

[2] Just-In-Time Code Reuse: On the Effectiveness of Fine-Grained Address Space Layout Randomization. IEEE S&P 2013

本文分享自微信公众号 - FreeBuf(freebuf)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-07-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Crossin的编程教室

【每周一坑】注册表单验证

长假过后,大家都缓过神来了吗?在这里祝大家上班快乐~ 今天来个应用题。在使用各种网站和应用时,少不了要注册账号,这种注册表单大家应该屡见不鲜了吧。 ? 一般这些...

41250
来自专栏用户2442861的专栏

分页和分段的联系和区别

    用户程序的地址空间被划分成若干固定大小的区域,称为“页”,相应地,内存空间分成若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实...

93910
来自专栏用户2442861的专栏

从零开始山寨Caffe·陆:IO系统(一)

http://www.cnblogs.com/neopenx/p/5248102.html

10520
来自专栏开发 & 算法杂谈

基于Lockset的数据竞争检测方法汇总(二)

前一篇文章提到的是使用Lockset最经典的方法,但是存在很多误报,针对这些误报产生的原因,有很多分析并改进了原始的Lockset方法,今天主要和大家谈的就是有...

18770
来自专栏逆向技术

逆向知识第十讲,循环在汇编中的表现形式,以及代码还原

        逆向知识第十讲,循环在汇编中的表现形式,以及代码还原 一丶do While在汇编中的表现形式 1.1高级代码: #include "stdafx...

20780
来自专栏DannyHoo的专栏

定位1

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

11920
来自专栏技术之路

wpf 解决 编码解码器无法使用提供的流类型 The codec cannot use the type of stream provided

之前做的ListBox里列大图https://cloud.tencent.com/developer/article/1032668 图片转换的时候这段代码 ?...

21690
来自专栏大数据钻研

一个基于Java的开源URL嗅探器

这是一个可以检测并规范化文本中的URL地址的Java库。 ? 今天,我们很高兴做一个分享,因为我所在的 Linkedin 公司 开源了我们做的一个ULR探测工具...

442110
来自专栏大数据

何为正则表达式?要他有何用?

文章不好,作为学习,差不多就行了。 平常大家都会使用word,记事本之类的文字编辑软件,也会经常使用复制粘贴,把从知网下载下来的论文粘贴到自己的文章里面,一篇课...

23950
来自专栏linux驱动个人学习

处理器并行设计

17420

扫码关注云+社区

领取腾讯云代金券