Giter Site home page Giter Site logo

Comments (2)

sjtusemichael avatar sjtusemichael commented on August 15, 2024

本文主要是针对ROP问题进行了进一步研究,提出了3种比较新的ROP攻击的方法:1、Call-Preceded ROP;2、Evasion Attacks; 3、History Flushing。之后作者具体介绍了这三种方法,在面对两个目前处理ROP问题的比较成功的防御方法——kBouncer和ROPecker时的表现,先介绍两种防御的工作原理,再针对这两种方法的工作原理的漏洞,作者具体介绍了他的三种攻击方法中对应的破解方法。
本文旨在说明了modern ROP攻击的危险性。

细节中重要的内容:
文中作者针对KBouncer提出了ROP防御的重要检查的依据:
一、call-preceded原则:就是没有恶意的代码指令执行的时候,一个ret指令回到的地方的上一句指令一定是call。
二、ROP的攻击一般都是由短的gadget组成的长串指令来执行的(gadget指的是少于20个instruction并以ret结尾的片段)

作者具体重点介绍了History Flushing这种方法可以让KBouncer在检查的时候无法判断,通过Large NOP的指令块,来破坏KBouncer检查的原理,从而实现ROP攻击。

对于ROPecker,基本上作者之前所做的工作都可以简单地破解。

作者最终还提出了一个结论或者说是愿景:ROP的防御必须着眼于正常执行和ROP攻击执行的最基本的差别。言下之意,目前众多的ROP防御的方法还是存在漏洞的,或者说这些方法都是治标不治本。

from rop-intro-and-foreland.

sjtusemichael avatar sjtusemichael commented on August 15, 2024

ROP is still dangerous: Breaking modern defenses
ROP简介:
传统的ROP攻击的原理基本上是利用已经存在的代码片段,经过改变指令的执行顺序,重新组合来实现一些危险的操作。针对这一特性,ROP防御的方法一般有两大类方式:一、通过重编译来移除潜在的ROP攻击片段,加强控制流的隔离。二、通过runtime protection来保护二进制指令执行。

几个名词解释:
“gadget”:以ret结尾,指令数<20 的指令串
“long sequence”: 8 个以的连续执行的gadget
本文中三种全新的ROP攻击:
不过作者认为有三种方式可以突破现在主流的ROP防御的方法:
一、 Call-Preceded ROP
原理简介:
通常来说,在一个正常结构(没有ROP漏洞)的程序中,每一个ret指令都必须回到一个紧跟着一个call指令的地方。

从图上可以看出,如果0x2b130处的代码正常执行,那么ret肯定会回到“test rbp,rbp”这一句指令,这句指令肯定紧跟在“call 0x2b130”指令后。

ROP是利用现有代码重新组合从而实现攻击,所以肯定存在不符合Call-Preceded原则的跳转代码片段。所以一般的ROP防御都会检查是否每一条ret指令都紧跟着call指令。
不过,Call-Preceded ROP是作者提出的可以不违反这一原则的情况下仍然可以实现ROP攻击的方法。
具体细节:
只用call-preceded gadgets:
核心的**,我们需要让gadgets变得更加复杂,让gadget变得相对长,包含直接跳转和间接跳转,可以增加可用的gadget,让跳转的位置可以找到的call-preceded的指令。

二、 Evasion Attacks
原理介绍:
ROP防御原理——Classification-based defenses:
这部分的ROP防御是runtime实时监查程序的执行,来区分程序的执行的指令是还是“normal execution”还是“gadget”。那么现在很多的ROP攻击就是通过组合多个gadget形成一个long sequence,一种length-based classifier就可以通过检查执行时指令的长度,如果出现了long sequence,可以把执行流按照间接跳转(ret,间接jmp)拆分成多个只包含基本指令的指令片段,如果这些指令片段都是比较短的,并且在一个窗口周期内,出现了大量的短的片段,那么这类的防御就把他们判断成long sequence of gadgets,算作是ROP攻击。
针对的攻击方法:
Using gadgets that look like benign execution
Evasion Attacks当然是要把把包含ROP攻击的“gadget”伪装成正常的执行指令串,就针对length-based classifier这种防御来说,只需要把ROP攻击的gadget长短结合,就可以避免被检查出来。当然,其他的ROP方式如果也是从normal execution和gadget的区别来防御的,也是可以设计出Evasion Attack的方法的。
细节。

三、 History Flushing
原理介绍:
一部分防御方法是会维持一定量的指令执行的历史,并且会隔一段时间检查执行流。
History Flushing就是通过不断刷掉ROP attack的历史信息,给检查历史执行流的ROP防御程序一个假象,就可以实现ROP攻击。
针对的攻击方法:
给程序中加入很多有效的no-op指令,这样防御者就看不到任何ROP的信息。比如kBouncer使用的就是the Last Branch Record,是一种报告最近16个记录的硬件机制。那么只要加入16个无关的indirect jmp,就可以绕过kBouncer的防御。

Defeating kBouncer:
kBouncer的原理:
kBouncer使用了LBR,检查最近的16个调用system call的分支。同时,kBouncer有两个检查机制:一、它检查LBR中每一个ret指令的返回地址是call-preceded地址;二、它检查了最近的8个间接跳转分支,如果是类似于gadget的,整个进程就会被当做ROP攻击而kill掉。

作者按照以下流程击败了kBouncer:

在传统ROP的基础上,作者使用了history flushing清除了LBR中的ROP攻击的证据,之后使用了evasion attack和一些专门设计的gadgets来调用syscall。

来看一下具体的细节:
1、Initial exploitation:
简单来说,在这个阶段贮备好system call的所有参数并把它们保存在可以非常简单恢复的位置。
2、Hide the history:
两个部分:
第一部分Hiding history through LBR flushing:
1) A short flushing gadget: 简单的call-preceded的以ret结束的gadget,不修改所有的寄存器。
2) A long termination gadget: 超过20个指令,长度长到让kBouncer检查的时候认为不是gadget的call-preceded gadget。
如下两个例子:

具体实现的细节:
首先,我们重复使用flushing gadget来填满LBR里16个记录,从而清除了其他的记录。尽管这个时候LBR里已经没有其他ROP攻击的记录,但这个时候如果直接调用system call,仍然是不行的,因为LBR所有的entry还都是被检测出是gadget的,这样仍然会被认为是一个ROP攻击。
下一步,就需要使用termination gadget。这样,可以把LBR中至少8个entry中的gadget的长度超过了20,可以说,termination gadget是用来让kBouncer无法找到gadget-like的指令串。至于termination gadget之后的寄存器的状态是没有什么要求的,唯一的要求,就是在termination gadget执行后,我们仍然有指令流的控制权,可以说这个要求也是比较简单的。对于termination gadget,具体做的时候,还需要保证紧跟着termination gadget的一个没有问题、不进行攻击的指令集,同时要保证其中的指令操作不会修改memory和register。

第二部分Hiding history through context switching:
由于LBR是在线程间共享的,所有还有一个简单的flush掉LBR的方法就是利用content switch。我们只要找到很多类似于下图的片段就可以轻松地用来flush LBR。

只要另一个线程修改了LBR,那么原来一个线程在LBR里的疑似ROP攻击的证据就会被消除,这个方法非常有效。

3、Restoring Registers with Returns:
这一步就在调用system call之前是把寄存器的值存好,使用的gadget也要是call-preceded的,并且长度比8短就行。这一步也是比较简单的,因为x86的汇编代码一般都会有大量的push和pop寄存器的操作。

4、Restoring Registers without Returns:
当然也有以下四种方法可以存储register。
1、 ROP without return instructions.
2、 Jump Oriented Programming (JOP).
3、 Using Non-Call-Preceded Gadgets
4、 Call Oriented Programming (COP).

5、Issuing the System Call:
最后一步就是调用system call。
在这里我们不能直接让执行return直接到想要去的函数的开始的地方(比如mprotect, VirtualProtect),因为那样不是call-preceded的。
有三种方法可以解决:
1、reflector gadget:这事一个以寄存器的值进行间接跳转的指令结尾的gadget。这样就可以是的寄存器的值指向想要调用的函数。
2、直接找到一个call指令,指向desired function,比如这样的代码:

3、有的时候,可以回到一个desired function的中间,利用偏移量来,这样也就是call-preceded了,比如下图的<execv+18>。

Defeating ROPecker:
ROPecker原理简介:
ROPecker在防御ROP攻击的原理总体来说和kBouncer是相近的,只不过它更加频繁的检测了程序执行的状态。
ROPecker中,只有少量的页可以被执行,我们不妨称之为executable set。每有一份新的页需要被执行,就会触发page fault,那么ROPecker就会检查是否是一个ROP攻击。
ROPecker会发生page fault的时候,检查指令流,如果指令流指向了一个间接跳转,就会被认为是gadget-like的,此时ROPecker就会记录下来并继续去监测,直到指令流的最后不是一个指向间接跳转的短的sequence。这时候ROPecker根据统计出来的LBR中有风险的gadget的数量来判断是否是一个ROP攻击。
数量上,6个及以下的指令片段被记录为gadget,11个gadget-like的sequence就是被认为是危险的。

对ROP的攻击被称为Repeated history hiding attack:

有几个重要的阶段:
1、Initialization:插入一段termination gadget,让ROPecker对LBR中gadget数量的监测失效。
2、Loading Phase:首先把一个useful page载入executable set,这个page上的所有gadget都会立刻被preceded并且跟随一个termination gadget。当page fault发生的时候,ROPecker会检查LBR,如果他往后检查就会发现每个page跟随的termination gadget,如果向前,就会找到最开始加入的那个,所以就限制了ROPecker对于page load gadget的数量的控制。
3、Attack Phase:在useful page被载入进来之后就可以开始部署攻击了。因为之前的工作,所以useful page上的攻击是不会被发现的,并且重复以上三个阶段,就可以在多次攻击中完成攻击者最终想要做的攻击,这样使得每一次攻击的难度没那么大。
History Hiding:最后,像对付kBouncer一样,只需要flush掉LBR里的记录,就可以在下次ROPecker正常工作之后,也不会被发现有ROP攻击。

4、Segmenting the Attack Payload:
由于要分段进行ROP攻击,所以需要合理地在每个page上放置有限数量的gadget。

5、Selecting Pages to Load:
由于executable set只能存有限量的page,最简单实用的方法就是每次从一个页上载入一个gadget,并且只载入一次。
当然,也可以在操作得当的情况下,载入一个有多个useful gadget的页,这样即使ROPecker的executable set只有一两页,也是可以实现攻击的。

最终的结论:
本位提出了三种攻击的思路,并且成功地攻陷了kBouncer和ROPecker。作者发现了前人没有注意到的两个重点,一、ROP攻击是可以只含有很短的gadget的,二、ROP攻击可以都是call-preceded的。
对于未来的ROP防御的研究,作者认为,防御不能只关注程序执行过程中的一部分历史,因为这样是肯定可以被攻击者通过其他方法给消除攻击证据的;ROP的防御必须针对所有ROP的特点,因为特定的防御方法都会有特定的evasion attack。
总之,最终的结论或者说是愿景:ROP的防御必须着眼于正常执行和ROP攻击执行的最基本的差别。

from rop-intro-and-foreland.

Related Issues (4)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.