SoK:Eternal War in Memory

论文来源

IEEE Security&Privacy’13:SoK: Eternal War in Memory

摘要

  • 描述了一些当今系统的攻击方式;
  • 并介绍了防御的一些策略;
  • 同时分析了为什么没有部署更严格的防御策略的原因;
  • 在不同的策略之间进行比较,为了找到安全和效率之间的平衡。
  • 提供了一些当今的研究问题,并给了一些建议。

介绍

​ 如今的防御措施有几个方面的原因导致不能大面积部署:

  • performance不够好,overhead太大;
  • 方法和过去的代码或者feature不兼容;
  • 方法不够健壮,提供的保护也不是很完整;
  • 有些保护依赖于修改工具链和编译器,但是很多工具链也并不是开源的。

contribution

  • 总结内存损坏攻击的通用模型,并根据模型确定不同的可执行安全策略;
  • 通过将攻击手段的执行阶段与利用阶段做匹配,总结哪些攻击目前还没被保护的,或者过去提出的方法不适用的;
  • 评估和比较提出的方法的performance,兼容性和鲁棒性。
  • 讨论了为什么很多防御措施没有被大范围接受,然后新的措施的标准是什么。

attack

一张各个阶段的攻击总结:

image-20210826141804129

一些词的解释

dangling pointer:空悬指针,指针指向的内存已经释放。

spatial error:访问越界的指针错误

temporal error:解引用一个dangling指针引起的错误

Return Oriented Programming (ROP):返回现有代码的攻击,组合现有代码完成一些操作。

Jump Oriented Programming (JOP):面向跳转编程,即用直接跳转完成类似ROP的效果

内存corruption(下面的序号对应图中最左边圆形的序号)

指针

1.指针错误类型:

  • 通过指针越界或者访问释放了的内存来使得指针失效,主要有buffer overflow/underflow
  • 通过边界检查错误造成的越界或者整数溢出,主要有integer overflow,截断,符号错误,指针转换错误。
  • 也可能通过内存腐败,修改了指针数据而造成指针error。

2.获取这样的指针:

  • 通过不正确的异常处理bug,比如有些程序释放了一个对象之后没有把指向该对象的指针初始化。这种被叫做use-after-free。可以在堆分配和释放的时候做一些手脚。
  • 一些局部变量指针被全局指针赋值时候也可能会导致问题,因为这样的指针在函数返回时候释放了局部的变量,但是全局的部分称为了悬挂指针。

3.有了这样的指针之后如何利用:

​ 利用之前获得的指针进行读或者写的时候,就可以泄露一些内部的数据。

  • //比如如下这种情况,下一次的控制流跳转是跳转到执行表中的某个函数,但是因为index是用户控制的,就可能产生任意地址的跳转,
    //攻击者可以让jump_table指向攻击者可以控制的地方
    func_ptr jump_table[3] = {fn_0, fn_1, fn_2};
    jump_table[user_input]();
    
    1
    2
    3
    4
    5

    * ```c
    //printf format string bug,利用类似的bug攻击者可以读任意地址内存
    printf(user_input); // input "%3$x" prints the
    // 3rd integer on the stack
  • 如果攻击者控制的是写指针,那么任何指针都可能被覆盖写,甚至任何数据都可能被覆盖写。
    比如buffer overflow/underflow和下标错误,可以写覆盖写敏感错误的数据比如返回地址或者虚表指针。其中重写虚表指针就造成了图中3->1的反向循环。第一轮循环通过前述方式修改虚表指针,然后第二轮循环由于别的code解引用了这个虚表指针,从而造成控制流的劫持。从此循环反复,越来越多的指针就能被利用。

  • 攻击者利用corrupting的指针调用free()也能造成内存破坏。[jp, “Advanced Doug Lea’s malloc exploits,” Phrack, vol. 11, no. 61, Aug 2003.]

  • //打印时候解引用的指针也会造成信息的泄露,控制err_msg指针的指向可以进行任意地址的读
    printf("%s\n", err_msg);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27

    下面是针对temporal error的利用

    * 当解引用悬挂指针的时候,指向的内存可能被内存分配器比如堆分配器分配给了别的object,就可以利用悬挂指针对别的object进行控制读写,甚至double-free也能造成任意读写,即新的object也被错误地释放了。

    ### Code corruption 攻击

    ​ 利用上述bug去重写程序在内存中的code,但是把code设置成read-only。

    ​ 但是他不支持“程序自己修改代码”和“Just-In-Time(JIT)即时编译”。如今大部分浏览器用的都是JIT用以解析Flash和JS脚本,这会给代码部分一个可写的窗口期。

    ### 控制流挟持攻击

    4.通过前述指针的error,可以修改return address来跳转到攻击者可控的地址空间。这可被地址空间随机化缓解(不能根除)

    5.通过直接call或者间接跳转或者函数返回指令ret来造成CFI(Control-flow Integrity)的破坏。

    6.第六步就是运行恶意代码,比如shellcode。这样可以被数据页面不可执行缓解,即W⊕X (Write XOR Execute) 策略,即页面要么可写,要么可执行,但是不能同时可写可执行。同样JIT和self-modifying code的情况不适用。于是提出了(Instruction Set Randomization),即加密代码,但是performance太差了,慢慢被淘汰。同时攻击者为了绕过权限,设计了重用现有代码,实现ROP攻击比如return-to-libc攻击或者通过gadgets小组件来执行恶意操作,还有JOP的攻击。目前这种类型只能缓解,不能根除。比如面向编译器的一些缓解方式,二进制重写以及消除大块。(个人知识,paper里没写:目前Intel和ARM各自有硬件上的缓解措施,比如CET,形成第二个shadow stack记录跳转地址。)

    ### data-only攻击

    3.总的来说攻击者目标就是恶意修改程序逻辑,获得更多的控制权限,甚至修改特权级以及泄露信息,这个目标可以通过不修改显式控制流相关数据比如返回地址,跳转地址这种来完成,可以通过修改变量来改变程序执行逻辑。比如如下代码:

    ```c
    bool isAdmin = false;
    ...
    if (isAdmin) // do privileged operations

可以通过数据空间随机化以及增加数据熵的方式缓解。

信息泄露

任何内存错误都可能造成信息泄露,而利用信息泄露可以绕过ASLR,解决的办法是数据空间随机化。

real-world的保护和利用

用的最多的是stack smashing protection,DEP/W⊕X 和 ASLR。windows还用SafeSEH 和 SEHOP保护堆元数据和异常处理,但是这样的保护很有限,仅仅是在跳转时候check一下,也仅仅是检查了返回地址和异常处理函数等方面,没有覆盖到全部,并且这种可以被绕过,比如利用索引来攻击,而不是覆盖。

  • stack smashing protection:在栈上返回地址和buffer之间放一个随机值(cookie或者canary),然后在返回之前check
  • SafeSEH and SEHOP:在使用异常处理指针时候先用cookie验证一下。

DEP/W⊕X可以保护code注入的攻击,但是不能保护ROP这种code reuse攻击,这种攻击可以用ASLR稍微防护一下,但是可以被去随机化和信息泄露等方式破解ASLR。

现在PDF viewers, 办公应用,web浏览器等运行user可控的脚本,可以用来动态构建payload,并使目标机器运行。

方法和评价标准

  • 主要分为两大类:概率性保护(probabilistic)和确定性保护(deterministic)

    概率性主要是随机化:Instruction Set Randomization, Address Space Randomization, or Data Space Randomization, build on randomization or encryption

    其他的都是确定性的保护。

memory safety

指针边界检查:

  • 胖指针:要修改指针结构和代码结构,让一个指针结构体记录额外的信息
  • SoftBound:将元数据和指针分离开来,用一个熵来记录,数据都存在一个特定的区域,类似哈希的方式(这里为了防止别人破解用的加密方式),在指针解引用的时候去特定区域取数据。

基于对象的指针检查:由于我们不知道指针是不是指向正确的对象,尽管有边界检查,对象指错了也不行。基于对象的指针关注于指针运算,而非解引用。但是这样的过程只关注于对象创建和释放的时候,在对象被修改的时候是注意不到的。

Temporal safety

特殊的分配器:不再使用相同的虚拟地址。或者地址只能被相同类型的对象使用。或者随机分配位置的分配器

根据对象的方法:

根据指针的方法:不仅要维护边界,还要维护分配信息保证安全。

别的资料

What is memory safety