JIT-ROP发展历程

过去在最早的这些目标中的许多都是通过所谓的运行时攻击(利用流行应用程序(例如浏览器或文档阅读器)中的漏洞)实现的。也就是通过正常的控制流获得高级权限。尽管在风格和实现上存在差异,但它们都有一个共同的目标:在脆弱的应用程序中重定向程序逻辑的能力。遗憾的是,使这些攻击成为可能的安全漏洞的历史现在已经延续到第三个十年,而且仍然如此。对现代系统构成严重威胁。事实上,尽管已经实现了许多防御措施来限制这些攻击的范围,比如地址空间布局随机化(ASLR)和数据执行预防(DEP),但猫捉老鼠的游戏还在继续

1.早期破坏结构体

在开发的早期,缺乏适当的边界检查被错误地用于覆盖堆栈上的信息(例如,函数的返回地址)和将脆弱应用程序的逻辑流重定向到任意代码(称为shellcode),这是一种攻击策略,后来被称为粉碎堆栈。为了减少堆栈崩溃,一个所谓的金丝雀(即在返回值之前的堆栈上引入了一个验证例程,编译器向函数尾声添加了一个验证例程,当金丝雀被修改为时,函数尾声将终止程序。正如预期的那样,攻击者通过覆盖可选的控制流结构(如结构化异常处理程序(SEH))来迅速适应他们的防御策略。作为回应,x86体系结构的分页方案中引入了一个no-execute (NX)位,该位允许将任何内存页标记为非可执行的。DEP利用NX位将堆栈和堆标记为不可执行的,并在将控制流重定向到注入的代码时终止正在运行的应用程序。为了不被超越,攻击者将代码重用攻击添加到他们的剧本中。这种新策略利用内存中已有的代码,而不是依赖于代码注入。典型的例子是returnto -libc,其中攻击将重新直接执行现有的共享库函数。

2.中期

最近,Shacham扩展了这个概念,将以ret指令结尾的短指令序列连接在一起(称为gadget)实现任意程序逻辑。这种方法被称为面向返回的编程。到目前为止,面向返回的编程已经得到了广泛的应用架构范围(包括Intel x86、SPARC、Atmel AVR、ARM、PowerPC)。
然而,这种早期形式的代码重用依赖于位于内存中已知地址的gadget。因此,将数据和代码区域的位置随机化的addressspace布局随机化为这些攻击提供了一种合理的防御策略。代码区域布局的随机性阻碍了利用中的代码重用;数据的随机性使得很难猜测注入代码的位置,从而阻碍了控制流的重定向。

3.最新代码重用攻击

不甘示弱的是,攻击者很快就与经常被忽视的一类漏洞达成了和解:内存泄露。实际上,公开一个地址违反了ASLR中的基本假设,并且有效地揭示了单个库中每段代码的位置,从而重新启用了代码重用攻击策略。
根据这种新的代码重用范例(无论是返回规范化的、面向跳转的,还是其他某种形式的“借来的代码”),技术娴熟的对手一直在积极地寻找更巧妙的方法来利用内存公开与此同时,防御者一直忙于通过设计“增强的”随机化策略来加强防御范围,以击退下一代狡猾的黑客。直到最近,Kevin Z. Snow团队质疑这种特定的思路(关于细粒度代码随机化)是否在长期内提供了一种可行的替代方法。特别地,他们研究了最近利用缓解技术的不足之处,并表明内存溢出的信息要多得多比之前认为的更具破坏性。正如SEH覆盖的引入打破了堆栈canaries提供保护的幻想一样,代码重用破坏了DEP,内存公开违背了ASLR的基本前提,我们抨击了细粒度ASLR所包含的假设。
Kevin Z. Snow团队的主要贡献是展示了用于减少利用的细粒度ASLR,即使考虑到一个理想的实现,可能也不会比已经被绕过的传统ASLR实现更有效——简而言之,更改越多,它们就越保持不变。我们通过实现一个框架为这一点提供了有力的证据,在这个框架中,我们自动调整任意内存公开,使之适合多次使用要可靠地映射易受攻击的应用程序的内存布局,然后及时编译攻击者的程序,重用(精细随机化)代码。我们框架的工作流完全发生在一个脚本中(例如,浏览器使用的脚本),用于对抗应用程序级随机化的远程攻击,或用于对抗内核级随机化的本地特权升级攻击的单个二进制文件中。

4代码重用攻击详解

为了简单起见,我们使用一系列单指令小工具强调了对堆的ROP攻击,其中涉及到对手将程序执行重定向到现有库函数。更一般地说,攻击者引入了面向返回的编程(ROP),这表明攻击可以结合来自函数(称为gadget)的短指令序列,从而允许对手诱导任意的程序行为。最近,这一概念通过消除依赖而得到了推广实际返回指令。但是,为了简单起见,我们在图中强调了使用ROP重用代码的基本思想。
在这里插入图片描述
首先,被攻击者写所谓的ROP加载到应用程序的内存空间,加载内容主要是由很多的指针(返回地址)和任何其他所需的数据运行攻击(步骤①)。特别是,有效加载内容被放置在一个可以被对手控制的内存区域。比如该区域是可写的,被攻击者知道它的起始地址。下一步是利用目标程序的漏洞劫持的执行流程(步骤②)。在图1所示的示例中,被攻击者使用有漏洞的堆,用指向所谓堆栈主序列的地址覆盖函数指针的地址。一旦覆盖函数指针被应用程序执行后,执行流重定向完成(步骤③)。
简单地说,堆栈主序列(stack pivot)将堆栈指针(%esp)的值更改为存储在另一个寄存器中的值。因此,通过控制register2,攻击者可以任意更改堆栈指针。通常,堆栈主指示堆栈指针的开始有效载荷(步骤④)。堆栈主序列的一个具体例子是x86汇编程序代码序列mov %esp,%eax;序列将堆栈指针的值更改为存储在寄存器%eax中的值,然后调用return (ret)指令。x86 ret指令只是将%esp指向的地址加载到下一条指令指针,并将%esp增加一个字节。因此,执行持续在第一个gadget(存储)指针,返回地址1(步骤⑤)。此外,堆栈指针增加,现在指向返回地址2。
gadget表示原子操作,例如加载、添加或存储,后面跟着ret指令。正是终止ret指令,使产品通过加载地址的链接执行堆栈指针指向指令指针(地址2)和更新堆栈指针,指向下一个地址的有效载荷(返回地址3)。重复步骤⑤⑦,直到对手达到她的目标。总之,不同gadget的组合允许对手诱导任意的程序行为。

5常用的解决办法

针对代码重用攻击的一种广为接受的对策是随机化应用程序的内存布局。地址空间布局随机化(ASLR)的基本思想可以追溯到Forrest等人的,其中引入了一个新的堆栈内存分配器,为大于16字节的堆栈对象添加一个随机pad。今天,ASLR几乎在所有现代操作系统上都是可用的,比如Windows、Linux、iOS或Android。在大多数情况下,当前的ASLR方案会随机化段的基本(开始)地址,比如堆栈、堆、库和可执行文件本身。下图描述了这种基本方法,其中可执行文件的开始地址在应用程序的连续运行之间重新定位。因此,对手必须猜测所需函数和指令序列的位置为了成功部署她的代码重用攻击。
在这里插入图片描述
不幸的是,目前的ASLR实现存在两个主要问题:首先,32位系统上的熵过低,因此可以通过暴力攻击绕过ASLR[37,53]。其次,所有的ASLR解决方案都容易受到内存泄露攻击,在这种攻击中,对手只知道一个运行时地址,并再次使用该信息在其场景中重新启用代码重用。当今许多最复杂的攻击使用JavaScript或ActionScript(以下称为脚本)和内存公开漏洞来显示加载在内存中的单个代码模块(例如动态加载的库)的位置。由于目前的ASLR实现只在每个模块级别上随机化,在模块中公开一个地址可以有效地揭示模块中每段代码的位置。因此,来自公开模块的任何gadget都可以由攻击者在部署攻击之前脱机手动确定。一旦先决条件信息已经收集到,利用脚本只需根据模块在运行时公开的位置调整偏移量,从预先确定的模板构建有效负载。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值