《SoK: Eternal War in Memory (S&P‘13)》笔记

简介

SoK: Eternal War in Memory (Paper),作者是四位大佬László Szekeres、Mathias Payer、Tao Wei、Dawn Song。

该文基于大量经验试图进行系统性分析,但依然无法保证分析的完备性(很难实现)。

该文针对Memory Corruption系统分析。主要贡献包括:

  1. 针对Memory Corruption攻击构建通用模型,基于模型定义了多种可实施的安全策略。
  2. 对比安全策略设计及漏洞利用各阶段特征,发现部分攻击向量未被现有防御机制保护。
  3. 从性能、兼容性和鲁棒性评估及比较现有防御方案。
  4. 讨论现有防御方案的缺点、未被实际应用的原因,讨论未来新防御方案设计所需满足的要求。

攻击模型(看图说话)

在这里插入图片描述

Memory Safety Corruption(①、②)

Invalid(out-of-bounds、dangling)指针(① )被解引用(读写)(②)导致内存安全破坏(数据破坏、信息泄露),内存安全破坏是各类攻击的根源。

解引用越界指针构成Spatial Error,解引用Dangling指针构成Temporal Error。

依据C/C++标准,越界写数组、解引用Null指针和读取未初始化变量导致未定义的行为。边界检查和内存管理需开发者自行维护。(译注:现有Sanitizer会从边界和内存操作进行检查)

Invalid指针成因(①)

指针越界源于数据指针被修改(③ → \rightarrow ①):

  • 未对allocation failure检查使得null pointer被使用
  • 数组指针递增递减导致buffer overflow/underflow
  • indexing bugs(常由integer overflow、truncation or signedness bug或incorrect pointer casting触发)。

Dangling指针源于:

  • 异常处理句柄中释放了指针但未对此重新初始化
  • 函数返回并且全局指针指向该函数内的局部变量

Invalid指针解引用后果(②)

后果包括内存破坏(代码数据被破坏)和信息泄露。

越界指针解引用(读、写)导致Spatial Error

使用指针从越界/攻击者控制区域读取内容,导致Value Corruption,例如:

  • 未检查从攻击者指定变量(直接/间接)读取的值,导致Data Corruption(进而可能导致控制流劫持)
  • 攻击者指定读指针内容被输出,构成信息泄露

使用指针写内容到越界/攻击者控制区域,导致内存覆写,例如:

  • 缓冲区溢出覆写数组指针使其越界,指针越界导致虚表破坏,被篡改的虚表解引用导致控制流劫持
  • Free攻击者控制的指针导致任意地址写
  • 使用越界指针写操作导致信息泄露

Dangling指针解引用(读、写)导致Temporal Error

使用dangling指针读(dangling指针所指新旧对象不匹配),导致攻击者非法访问内存,例如:

  • 当旧对象dangling指针指向新对象(与dangling指针所指内容被修改略有不同),旧对象虚函数被调用时,会查询虚函数,使用新对象值,导致虚函数corruption。此处temporal read error与spatial write error一样导致vtable破坏。
  • 此外,新对象内容在读取旧对象dangling指针操作时泄露。

使用dangling指针写,如越界指针一样破坏新对象中的指针或数据,例如:

  • Dangling指针指向局部变量或栈,对其写操作可能导致(后续新栈帧)返回地址等敏感数据覆写
  • Double-free是use-after-free的特例,它的use操作是free,dangling指针free时,将所指新对象误解为堆的元数据并被free,导致任意地址写

攻击类型一:Code corruption attack

内存安全破坏(①、②)能直接修改内存代码(③)为攻击者指定代码内容(④),执行此会导致code corruption attack。

代码完整性保护(③)旨在保护代码免于修改。但自修改代码和JIT场景等难以使用代码完整性保护进行完全保护。Instruction Set Randomization(ISR,指令集随机化,使用攻击者未知的密钥异或加密指令,攻击者伪造的代码无法被成功解密并被执行)(④)使攻击者代码无法被正确执行。

攻击类型二:Control-flow hijack attack

内存安全破坏(①、②)能修改代码指针(③)指向(恶意的)shellcode/gadgets(④),并让跳转指令目标为被篡改的代码指针(⑤),成功执行shellcode/gadgets(⑥),从而成功劫持控制流。

代码指针完整性保护旨在保护代码指针不被破坏(③)。地址空间随机化技术让攻击者难以将代码指针指向shellcode/gadgets(④)。控制流完整性保护旨在检测间接跳转处的corruption(⑤)。Non-executable Data(不可执行数据,W⊕X,程序内容要么可写,要么可执行)和指令集随机化拒绝shellcode/gadgets被执行(⑥)。

但是Jump Oriented Programming(包括return oriented programming和return-to-libc)能够绕过W⊕X和ISR的保护(⑥),使用程序自身存在的代码构建代码链并展开攻击。

此外若要让攻击更具意义,攻击者通常需要进行系统调用、提权(例如文件访问)等。权限、强制访问控制、沙箱策略(SFI、 XFI、Native Client)能限制攻击者所攻陷程序的能力,但本文所关注的是如何保护程序不被攻陷。

攻击类型三:Data-only attack

内存安全破坏(①、②)能篡改安全关键数据变量(包括代码、代码指针)(安全关键是语义定义,实则需要保护所有变量完整性)(③),让数据修改为攻击者指定值(④),当该数据被使用时(⑤),data-only attack成功。

数据完整性(包括代码、代码指针完整性保护)保护旨在保护数据不被篡改(③),数据空间随机化(包括地址空间随机化)对数据引入额外的熵值,使攻击者无法使用指定数据(无法正确验证)(④),数据流完整性保护(包括控制流完整性保护)检测数据流是否被破坏。

攻击类型四:Information leak

内存安全破坏(①、②)能输出(③)攻击者指定的内存数据(④)。

信息泄露能被用来绕过ASLR等概率性防御技术。

全面的数据空间随机化旨在防止攻击者成功地将数据泄露(④)。

常用的防御方案及实际的漏洞利用

避免定位跳转目标(④)

ASLR避免攻击者定位恶意代码。

ASLR的代码地址仍可能被攻击者预测,信息泄露能泄露ASLR之后的代码地址。

控制流完整性保护方案(⑤)

Stack Canary/Cookie、SafeSEH、SEHOP在间接跳转时检查代码指针(返回地址和异常处理句柄,此例未涉及保护虚表和函数指针),Canary因开销不足1%且不影响兼容性而广受欢迎。

Canary无法防御与buffer overflow(经由Canary覆写返回地址)略有不同的indexing error(直接定位并篡改返回地址)。

跳转目标权限控制(⑥)

DEP/W⊕X能防御代码注入攻击中注入代码被执行。

DEP无法防御ROP攻击。

研究方法及评价指标

防御方法分类

概率性防御方法 基于随机化和加密技术

Instruction Set Randomization、Address Space Randomization、Data Space Randomization

确定性防御方法

Reference Monitor检查策略包括:

  • High-level,例如在syscall层面施加的文件系统权限检查
  • Low-level,例如内存安全及控制流完整性检查

Reference Monitor实现方式包括:

  • 硬件实现,开销低
  • 动态插桩(插桩指在目标程序嵌入代码)依靠动态二进制插桩技术,在运行过程中插桩检查代码,开销大
  • 静态插桩依靠编译器插桩及静态二进制重写技术,在生成目标程序时插桩检查代码,开销比动态插桩小,比基于硬件实现方案大

Low-level Reference Monitor实用性评价指标

保护能力 Low-level Reference Monitor的有效性取决于具体实施的策略,体现在其所能防御的攻击,其精确度包括假阴率(漏报)和假阳率(误报,实用环境中难以允许误报存在)。

成本 包括:

  • 性能开销。性能开销增幅低于5%或10%的方案容易被广泛采用。性能测试除I/O-bound benchmark外,更应关注(攻击所关注的)CPU-bound client-side benchmark
  • 内存开销。其引入增长的元数据会造成内存开销,某些利用影子内存的防御机制会使内存开销翻倍。

兼容性

  • 源代码级别兼容性。好的防御方案应不需要人工介入来调整源代码(移植或注释已有程序)
  • 二进制级别兼容性。好的防御方案应当能直接兼容已有的二进制程序(已经编译的程序),这有利于推广部署。此外,有的库文件受完整性保护,不允许二进制代码修改。
  • 模块化支持。由于动态库等模块非常重要,好的基于编译器的防御方案应当能独立编译并保护单独的模块,好的二进制重写保护方案应该支持单独加固单个二进制文件。

(注:后续内容过于细节)

Ⅴ. 概率性防御方法(章号与图1内注释对应)

依赖于随机化及加密技术,主要包括:

  • Instruction Set Randomization (ISR)
  • Address Space Randomization (ASR)
    旨在通过随机化(堆、栈、代码段、库)避免间接跳转目标为攻击负载。若非全部代码数据段被随机化,ASR可能被绕过,实际应用中除库以外的模块往往未开启PIE以规避10%的性能损失。【32位系统低熵难抵抗暴力攻击、去随机化(如堆喷射、JIT喷射)攻击和指针覆写,64位系统高熵ASR能被信息泄露绕过。】
    由于W⊕X广泛部署,(可执行的)攻击负载几乎只能存在于代码段。对此,函数级别、指令级别的随机化被用于增加ROP(gadgets获取)难度(对return-to-libc无效)。但信息泄露仍能轻易获取指令、gadget及函数的地址
  • Data Space Randomization (DSR)
    ASR对内存数据的位置进行随机化,而DSR加密内存数据(包括指针)以随机化其表现形式。DSR加密使用多密钥能部分避免信息泄露。

Ⅵ. 内存安全(保护方法)

类型安全语言(编译时避免类型差异导致程序违背预期)通过对象边界检查和自动垃圾回收机制保障空间及时间内存安全。(该文希望讨论在src/ir/bin插桩reference monitor改造不安全程序变得内存安全)

空间内存安全(边界检测)

  • 使用包含元数据的数据结构表示指针并进行边界检测会改变内存布局并使二进制程序难被兼容,而使用哈希表/影子内存记录指针边界元数据的SoftBound方法的程序无法有效兼容不支持该扩展的库(库操作指针后无法更新元数据)(开销67%)。(在指针解引用时检查是否越界②)
  • 将对象边界用元数据表示(只在对象创建销毁时需要更新)并用于越界检测,其关键在于指针运算确保指针指向正确的对象(①)。该方法有效兼容二进制库,可减少误报,但导致漏报。(开销60%以上)

时间内存安全

  • 使用特殊的动态内存分配器让被释放的虚存只能被相同类型(及对齐)的对象使用,能一定程度避免时间内存安全破坏。(彻底拒绝虚存重用虽能避免UAF等攻击,但过于浪费。)
  • 基于对象的方法使用影子内存记录被释放内存能一定程度保障时间内存安全,但无法避免内存重新被分配使用后的时间安全问题。(类似基于对象的边界检查会记录对象元数据。动态转换器Valgrind造成10倍性能开销,编译时插桩方案AddressSanitizer造成73%性能开销)
  • 基于指针的方法如CETS在全局字典存储每个对象的有效性,并为每个对象分配唯一ID,所有指针与对象ID关联。该方法保护时间安全产生的开销为48%,联合SoftBound(保护空间安全)产生开销为116%。CETS与SoftBound难以有效兼容未受保护库。

Ⅶ. 通用防御方法

这类方法的策略(重在阻止攻击)弱于内存安全(重在检测攻击),无法抵抗信息泄露,但性能开销较低。通用体现在其着眼于所有数据(包括代码、指针)。

  • 数据完整性保护如Young’s system能防御(如越界)写操作导致的数据破坏。利用影子内存标记不安全(可能越界)指针所指不安全对象(1代表不安全对象,0代表其它),在不安全指针写操作时基于影子内存中标记验证操作地址的确为不安全对象,避免破坏(写操作)不安全对象外的其它内存。开销50%~100%。
  • **指向集合完整性保护(Integrity of points-to sets)**如Write Integrity Testing(写操作完整性测试),基于源码指针分析,对每个指针及所指对象集合分配ID(对间接跳所用指针分配ID并在影子内存记录有效目标地址集合),杜绝写指针指向其对应集合外的地址。开销5%~25%。BinArmor基于动态跟踪指针指向集合(导致漏报、误报),使用二进制重写实施类似WIT的策略,开销180%。
  • 数据流完整性保护 对(如用于指定间接跳目标地址的)读操作指令backwards分析数据来源(哪些指令会写操作指定跳转地址),为合法指令指定集合并分配ID,并在读操作时检查目标地址由合法集内的指令赋予。开销50%~100%。

前两项指定合法写操作目标集合,能够一并保护影子内存等元数据存储区域由于point-to analysis中ID等元数据依赖于全局性指向集/图等信息构建,这三项均无法有效二进制兼容未保护库等独立模块(但适合宏内核、hypervisor等所有模块静态链接的场景),存在误报(如库未被标记到不安全内存,对其写操作导致误报)。

【注:如何有效确定合法目标集合(point-to analysis)?】

VIII. 控制流劫持防御

代码指针完整性保护代码指针不被破坏(侧重于防篡改),控制流完整性检测被破坏的指针,共同抵御控制流劫持攻击。

  • 代码指针完整性保护(数据完整性子集)非常重要,但缺乏内存安全将无法有效提供保护。GOT、虚表中指针无需重写,设置只读即可有效保护。其它指针则不然,需要可写性,可被篡改用于控制流劫持。此外,恶意值(有别于指针)的加载(读)可劫持控制流(如UAF中加载新对象的虚表中指针)。
  • 控制流完整性保护
    • Dynamic return integrity。Canary检测stack smashing破坏栈帧中返回地址信息(开销不足1%,无兼容性问题)。影子栈使用(未受保护的)影子内存记录栈信息并在return前检查返回地址是否被篡改(开销5%,兼容性问题不大)。RAD保护影子栈安全,10倍开销。
    • 静态控制流图完整性。Abadi CFI构建静态控制流图保护函数调用和函数返回,在跳转目标处(代码完整性保护)标记ID(类似指向集合ID)确保跳转目标合法。W⊕X能助其避免伪造跳转目标(使用恶意数据地址冒充并标记ID)。针对return的静态先验目标集合包含过多目标,弱于影子栈实施动态调用栈。此外将所有指向集合合并的方法,无需指针分析便能记录所有函数地址,支持modular transformation and interchanging of libraries(?),但集合过大,建议采用影子栈保护。开销15%~45%,使用影子栈将额外造成10%开销。无法二进制兼容,W⊕X使其无法用于JIT场景。

讨论与总结

(由于论文是2013年的,其所提供的信息也截至2013年)
在这里插入图片描述
(Dep.指deployment status。Compatibility指compatibility issue,即存在哪些兼容性问题。 Primary attack vectors指对应防御方法无法防御的攻击向量)

如表2所示,只有开销较低且兼容性强的方法才可能被广泛部署。除了基于指针的完全内存安全保护方案,其它方案均没有一个很好的鲁棒性支持。

由于W⊕X的应用,后续研究更关注ROP攻击。随机化是当时(2013年)比较新颖的方法。但JIT的流行使W⊕X难以应用,(攻击者通过JIT提供恶意脚本)随机化方法也容易被轻易打败。更强的策略如DI、CFI需要被深入研究。

作者建议学术界和工业界频繁交流,研究员基于开源平台设计新的防御方案~

The war is not over !

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值