ROP攻与防

diting0x

ROP是一种代码复用攻击方式(Code Reuse Attack)。 ROP攻击劫持控制流后,复用内存中可执行的指令(gadget),这些gadgets以ret指令返回,攻击者利用这些gadgets以不同方式拼接并执行,完成攻击过程。

下面以几个图来展示ROP攻击(图来源于Blackhat 2013 Kevin Z.Snow, Luca Davi)

metaphor:




攻击过程:













代码复用攻击历史:




再次声明,以上六张图全部来源于(Blackhat 2013 Kevin Z.Snow, Luca Davi)

上图可以大致了解2010年前ROP的发展历程,而下文大多数介绍的是近三五年ROP的攻与防。

Ret2libc可以说是ROP的前身,有点接近ROP。其主要是复用libc的函数地址(而不是含有shellcode代码的栈地址)覆盖被利用栈的返回地址。如果攻击者想触发一个shell, 他会利用system()地址来覆盖返回地址并设置好system()在栈中需要的必要参数,以便能成功调用system()。

随着ASLR的出现,将程序的数据段、堆、栈以及共享库在内存中的位置随机化,使得Ret2libc和ROP变得更加困难。
道高一尺魔高一丈,一攻一防,近些年对ROP的研究越来越热,不断有研究者设计更高级的ROP来绕过系统中现有的防御机制(包括DEP,ASLR),又不断有研究者设计更安全的ASLR或其它安全机制来抵御ROP。

攻:JIT-ROP(IEEE S&P 2013),其主要思想是即时扫描有效内存,即时反汇编搜寻rop gadgets。具体过程是,利用内存泄漏(memory disclousure)获取一个运行时的代码指针,泄漏当前4K的内存页,再利用页中指向其它页的分支获取更多的页面。之后将当前页反汇编获取所需的gadgets,构造ROP链。

slides

防:ROPecker (NDSS 2014)

ROPecker是一个利用Intel LBR特性记录代码执行流来检测和防御ROP的工具,是第一个可以针对所有形式ROP攻击的一种general的,不需要源代码,二进制代码重写。

ROPecker分析了现有ROP攻击的特点,发现ROP的gadgets一般利用jmp与分支判断指令跳转,在代码段中会进行大幅度的跳转,且其调用链很长。基于这两个特点,ROPecker先对要检测的程序进行离线gadgets分析,利用LBR记录代码执行流的分析信息,将不在代码当前执行片段周围的代码(sliding window)设置为不可执行状态,执行sliding window以外的代码就会触发ROPecker对攻击的分析。基于离线gadgets分析,再加上LBR的分析信息(当前程序的过去和未来的执行情况),一旦发现程序的执行流不符则认为程序受到了ROP攻击,则强制终止程序。以上检测和防御机制假设DEP已经打开。
pdf

防:Kbouncer(Security’13) 也是利用LBR记录程序的执行分支信息,识别程序的控制流转移信息,在关键点监控程序的间接指令来检测并防御ROP,其在Windows 7中实现。Kbouncer基于两个发现,1) ROP攻击会返回到non-call-preceded地址,其相对应的防御机制是Call-Preceded原则,没有恶意的代码指令执行的时候,ret指令回到的地址的上一条指令一定是call;2)ROP是由许多短gadgets组成的长链序列,其相对应的防御机制是不允许许多短gadgets组成的长链序列
slides

攻: ROP is Still Dangerous:
Breaking Modern Defenses (Security’14 -Berkeley)
Abstract: 提出三种方法(Call-Preceded ROP, Evasion Attacks, History Flushing)攻破现有的ROP防御方法,包括kbouncer,ROPecker。
并对未来防御ROP的方式提出两点要求:1)将代码分类成gadgets与non-gadgets并非易事;2)防御机制需要集中关注正常执行与ROP攻击的基本不同点。

slides
pdf video

防:ASLR-Guard(ccs’15 Byoungyoung)
要实施code reuse attack,要满足两个条件:1.知道现有gadgets的地址,2.用这个地址覆盖被控制的数据。对2的防御有:stackguard,cfi,code pointer integrity,对1的防御有:ASLR,但是ASLR有个缺陷,信息泄露,比如代码指针的泄露导致可验获取代码地址(JIT-ROP,Blind ROP, “Missing the point”)
本文目标:阻止代码指针的泄露。方法:系统化的发现代码指针,两种技术来阻止代码指针的泄露,隔离与加密。将代码指针存储在隔离的内存区域,隔离还不够,还需进行加密。实现:gcc,gas,ld,ld.so 3000sloc,eglibc,glibc。
slides pdf

防:XnR (CCS’15)
里面关于rop,aslr,jit-rop等介绍的很详细
该文从代码泄漏的角度进行了防护:当代码在被执行期间,不允许对代码的读操作,从而能有效抵御JIT code reuse攻击,简称XnR。类似W^X(W⨁X)策略。 这一思路在无硬件支撑的条件下,通过软件MMU实现,
基本思路:要实现XnR策略,在硬件不支持的情况下,可通过修改MMU中处理过程,实现软体MMU。 目前架构中:mmu可以检测到写,但是无法检测到读;读只能通过内存页的non-present实现;但是一旦non-present的话,代码也将无法运行。解决方法:修改page fault handler,在handler中区分page fault产生的原因,并决定是否继续正常执行,还是发现代码内存读取行为,中止执行。 具体的,在代码运行期间,仅允许极少的代码可读,文中的实验数据表明,采用3页的sliding windows是一个比较好的选择,即当前代码页面的相邻两页。是否可读性的实现方式是通过设置页面的present位实现,非法的读取将陷入中断,在中断中判断中断原因是缺页还是非法读。
PDF 来自liangyu blog

防:No-Execute-After-Read:
Preventing Code Disclosure in Commodity Software (AsiaCCS’16)
文章提出最新的XnR技术不能防御just-in-time的代码复用攻击,并设计了一种No-Execute-After-Read(NEAR),针对just-it-time攻击进行强安全保证。NEAR允许所有代码被披露,但是阻止被披露的代码继续执行。
pdf


待续