前身——return-to-libc
目的:
绕过DEP
主要思想:
代码重用攻击。利用进程空间中已经存在的代码,通过注入的异常数据控制其为攻击者服务。
新的利用点——复杂指令集CISC
CISC的问题:
代码中的一个字节的含义取决于上下文,原则上,任何一个字节都可能是一条指令的起始。
思想:
通过劫持控制流,攻击者可以令CPU按照其意愿重新解读已经存在的代码。
需求:
使用非常规的方式将短小指令片段串联成完整的代码流。
Galileo算法可为返回导向编程搜索可用的指令资源。
升级版——精简指令集RISC
对SPARC构造返回导向编程面临的问题:
无法利用意外的指令序列;X86下返回导向编程gadget的所有构造特点在RISC中都不存在。
新思路:
将函数的后缀作为gadget使用(利用结尾的ret-restore指令序列);利用结构化数据流使得gadget与SPARC的函数调用惯例相吻合;构造内存-内存gadget(寄存器仅在gadget内部使用)。
新防御——Smashing the gadgets:
核心思路:
阻止x86代码中出现以外的gadget。
手段一:抹掉0xC3字节(ret)
手段二:指令重新排序
手段三:寄存器压栈顺序随机化
手段四:寄存器重新分配
颠覆版——ROP without return
核心:
update-load-branch指令序列“pop x; jmp *x”
蹦床trampoline
函数调用步骤:
在esi中载入call-jmp序列的地址
在ebp中载入leave-jmp序列的地址
在eax中载入n+偏移量
将call-jmp序列的地址存储至地址n
改写esi,使其存储“返回地址”
esi值写入result位置后,再读出至edi
交换使返回值存入ebp,leave指令换入edi
在esi中载入pop-jmp序列的地址
在ecx中载入函数入口地址
在eax中载入地址n
交换esp和eax,栈指针指向n(函数地址)
edi处的leave指令将使函数“返回”
防御新思路——从结构上阻止程序控制流被劫持:
- 程序控制流完整性保护(CFI)
- 实用粗粒度CFI
Springboard段--使得间接控制转移可以通过位校验进行检查,Springboard段内地址的第27位总是为0,且仅有Springboard段满足该条件。间接控制转移只能以Springboard段内的适当存根作为目标。
缺陷:
需要改写程序的二进制代码。很难识别出程序中所有合法控制转移目标,对二进制代码的修改困难且易错,兼容性问题在一定程度上依然存在。 - 基于动态优化的CFI
在程序执行过程中加以监视和约束。
缺陷:
效率太低。 - 利用硬件特性和虚拟化技术的CFI
利用虚拟化技术。
内核模式下作为操作系统的一部分,性能开销低;提供了动态优化工具可能提供的CFI优势。
监视目标:执行流由足够长的gadget序列组成。
完整性检查的触发:
设置滑动窗口,所有窗口外内存均设为不可执行。控制转移指向窗口外时,将触发异常并陷入内核,引起完整性检查;敏感系统调用也会引起完整性检查。
返回导向编程的基本流程:
始于对程序控制流的篡改(控制流异常)
各gadget由ret指令或pop-jump组合代替eip加以链接(大量控制流异常)
原始栈结构遭到破坏,ROP过程中栈指针单项移动(栈的行为异常)
有时,栈指针可能被篡改并指向不属于栈区的内存(栈的行为异常)
CFI基本思想:
通过预设的运行时校验,确保程序执行与预先定义的控制流图严格吻合;
通过对二进制码的静态分析来获取CFI所需保证的控制流图;
借由静态的二进制代码改写为程序添加运行时的自我校验。
CFI的基本安全性假设:
控制转移目标的标示符(ID)具有唯一性;
程序代码不可写;
程序数据不可执行。
CFI的搭建:
-
内存访问控制 SMAC
通过与运算,确保控制转移目标的最高位字节内容位40h;
使得间接控制转移总是指向合法的代码区段;
由此在一定程度上实现软件故障隔离(SFI)。 -
受保护的影子栈
专门划定内存区域用于检查ret指令是否响应了正确的call指令;
为影子栈预设地址前缀,结合SMAC,可确保只有CFI的校验指令可以修改该区域。
CFI的弱点:
即使在静态分析中,间接控制转移的合法跳转目标也不唯一,不是排他的;
受到性能的制约,粗粒度化的CFI对控制流的约束更加松散。
改进型ROP——针对CFI
- 利用以函数入口和函数调用点为起始的gadget
- 图灵完全的改进型gadget集对抗现存最严格的CFI
改进型gadget的类型(主要来自kernel32.dll):
具有call指令前缀的gadget(主要);
call-ret配对的gadget组合;
“长nop"gadget。
绕过CFI不会导致返回导向编程的功能性有所缺损。
ROP变种“数据导向编程”——一切为了绕过CFI
特点:
通过污染数据(特别是指针)改变被重用代码的实际语义。借助循环不断注入新的攻击载荷,使得被重用代码的实际执行效果随之改变。
数据导向编程所重用的代码资源主要来自串操作。
交互式攻击 - 允许攻击者在每轮循环中输入不同的载荷以激活不同的gadget。
非交互式攻击 - 攻击者必须一次性输入整个攻击载荷,需要额外的指令元素以支持虚拟的跳转操作。
新防御——地址空间随机化ASLR
思想:
使gadget的位置变得无法确定。
粗粒度ASLR:
mmap offset、stack offset、brk offset随机化。
粗粒度的ASLR
粗粒度ASLR的改进:偏移量+内存区段的位置置换。
细粒度ASLR:
区段间----->区段内
改变代码文本的具体内容
实施
程序在加载时自我随机化;
通过虚拟机进行动态随机化;
操作系统的随机化。
ASLR的无效化:
场景1:攻击者无法披露目标程序的内存空间;
场景2:攻击者可以实施内存空间披露,但只能获得一个代码指针。
限制:对于任何ASLR,程序在执行时不在改变其内存空间结构。
“页面收割”最终暴露进程中的全部代码、API接口等可利用资源。
Just-In-Time代码重用
网友评论