ROP与Intel PT
fuzzification的论文阅读内牵涉到了基于ROP的保护措施,而ROP常用于攻击,并使用Intel Process Trace进行保护,所以接下来阅读了几篇文章,在此整合一下这几篇的内容。
链接:
1.http://www.cnki.com.cn/Article/CJFDTotal-RJXB201805012.htm
2.https://bsidescordoba.org/slides/Control_Flow_Hijacking_detection_through_Intel_PT.pdf
3.http://www.doc88.com/p-9992302206413.htm
ROP攻击
本部分请结合链接3内PPT阅读
技术简介
- 使用存在的应用逻辑gadgets伪造shellcode,注入恶意执行代码/重定向到非预期部分
- 攻击需要的条件:
- 可利用的弱点vulnerability exploitation
- 地址已知的gadgets(一种语义等效的工具)
- 非随机代码
1.disassemble
linear-sweep 线性扫描
- 解码指令 decode instruction
- 增加指令长度 advance EIP by len
disassemble from any address
- 从任意地址与全字节序列开始反汇编有唯一的反汇编
recursive descent 递归下降
- 跟随跳转并且返回(ret结尾)
2.代码序列标志为gadgets
- 执行语义动作的指令集,基本以ret指令结尾
- compiler-created gadget:编译器插入ret结尾的指令序列
- unintended gadget:非编译器产生的指令集(如非对齐方式开始反汇编)
3.组装gadgets为shellcode
下以Q-ROP为特例对上面的三个步骤进行拆分对应(但是关于这个语法的东西好像很少,所以仅供理解流程使用,出处也在链接3的PPT内)
Q-ROP
流程
1.1.executable code
1.2.linear sweep @ all offsets
-对应ROP反汇编部分(disassemble)
1.3.randomized testing of semantics
- find a gadget that implements a Q-Op
1.4.prove semantics
- 等效于ROP的代码序列标志为gadgets
- assembly
- weakest Precondition:逻辑上简化程序为语句的算法
- semantic gadget
- prover gadget and preconditions
- SMT 可满足模理论:靠定理证明或SAT寻找合取范式证明
1.5.gadget database
- 等效于ROP的组装shellcode
2.1.QooL program
- 详细内容查看PPT
2.2.Q-Op arrangement
- 详细内容查看PPT
- 一系列Q-Op program上编译QooL(使用不同的Q-Ops描述同级别指令),最终选择最小的具有目标程序相应最小部件的Q-Op program
- 类比:C: a = a*2; – Assembly: a = a *2;
a = a << 1;
a = a + a; - 优化:Q使用懒惰评估使得程序按需求生成
3.gadget assignment
- 任务选择单个Q-Op程序使用真实gadget与寄存器(命令与寄存器一一对应),可类比编译器内的寄存器任务
4.ROP shellcode
probability
- 给定程序大小是否可以生成payload:测试user/bin内所有程序
- 依赖于对象计算:以GOT与非GOT方式调用libc函数
Limitations
- gadgets 非图灵完整
- 没有条件gadgets
- 不会最小化ROP负载大小
motivation
- return-to-libc重写转换控制来实现逻辑片
简单的几种ROP:
- 被保护模块内寻找gadgets,修改寄存器与栈上值,再返回到正常地址
- 模块内部跳转修改关键内存与寄存器
- 内存模块漏洞修改内核核心的栈,使得返回到正确的地址后运行修改过的栈
- 解决方案-换栈:内核模块单独准备栈,切换时扩展页表错误处理函数映射虚拟机栈指针到新页防止修改内核栈
Intel PT防御ROP
CFI 控制流完整性检查
- 间接转移指令:间接函数调用,间接跳转,返回指令
粗粒度
- 正确计算间接跳转指令的合法跳转目标地址的集合
- 间接跳转指令前加上检查目标地址是否合法的指令
- 二进制覆写
- 保护透明性
- 破坏二进制程序完整性,导致不兼容
- 合法目标地址加上合适标签
细粒度
- ret只能返回到调用他的函数
- 间接函数调用只能跳转到事先定义的有限的函数集中
实现
- 准确性:系统保护足够精细可减少ROP攻击
- 高效性:额外的性能开销不大
- 透明性:易部署,兼容,系统需要对程序不可见
局限性
- 基于源代码
- 更精细保护策略
- 无法拿到共享库源码,不支持已部署好的应用程序
- 不基于源代码
- 集合大,牺牲安全性
Intel Processor Trace
- 提供软件运行信息,有效使用硬件,过滤
- 输出缓冲区大小不受特殊寄存器限制,只受主存大小限制。若输出目标重复且被及时清空,可创建任意长度追踪
- 依赖于给定处理器配置多个范围进行指令指针过滤IP过滤,CR3值与配置过滤器匹配时生成追踪数据
1.记录程序控制流包到提前设置好的内存区域内
-
流指令更改CoFI执行产生高度压缩包,且直接写入物理内存不经过TLB和缓存
-
只在执行目标地址非静态确定的控制流跳转指令时记录,不记录非条件直接转移
TNT
- 条件跳转指令(jg,je)
- 1bit
- 条件跳转是否被采用,1生效0无效
TIP Target IP
- 间接转移指令(callq*,jmpq*,ret)
- 记录跳转的目标地址
FUP Flow UPdate
- 长跳转指令(中断,虚拟机下陷)
- 上情况下随TIP一同生成,但先于TIP
PIP page information packet
- CR3切换(上下文切换,中断)
- 记录切换后的CR3
- non-root位标记切换后CR3是否位于Root下以区别是否在虚拟机内运行
mode
- current CPU mode
PSB
- heartbeats generated at regular intervals定时间隔
2.区域填满后硬件中断通知软件处理
3.软件解码器结合包与其他信息(程序二进制文件)还原程序原本控制流
- 解码步骤繁琐且耗时,需要将IPT包与程序二进制结合解码还原完整控制流才能与传统CFG比对
优化系统设计
1.内核模块基于二进制静态分析得到CFG
- 程序控制流图 CFG
- 基本块(不包含控制流跳转指令与其目标的基本代码,跳转为最后一条指令,目标为第一条指令)为基本单位
- 控制流跳转指令为边
- 包含所有执行到的控制流跳转指令,可组合出所有控制流
- 合法地址目标集合多于实际跳转目标以保证不误报
- 对象:被保护的程序二进制(无需源码)与依赖的所有库(内核模块无)
跳转指令处理
直接转移指令
- 目标地址固定的直接函数调用(call)直接跳转(jmp)
- 内核中,目标地址编译时未知,模块加载到内核时查找符号表,替换0占位符为目标地址,所以分析加载后的程序
间接函数调用指令
- 目标地址依赖于寄存器的call指令,由函数指针的函数调用编译得到,依赖于运行值
- 合法的地址
- 出现在.text段,地址被直接赋值给一个指针,则其出现在.text段
- 出现在.data段,指针从数据结构或变量内读取地址,则其出现在.data段
- 调用位置
- 该模块内部
- 某个内核模块依赖的内核模块内部
- 某个依赖该内核模块的内核模块内部
- 内核核心
返回指令
- 一对一,返回指令与下条指令call相连
- 尾调用优化破坏 tail-call:a调用b,b调用c变为a直接跳转到c,简化一次调用(b到c)与一次返回(c直接到a),导致无法静态分析(a实际上没有调用c)
- 处理
- 分析当前函数a时计算可能的控制流,找到可被函数执行的,在另一个函数b中的,且目标为第三个函数c首地址的跳转指令
- 目标函数所有返回指令的地址与a中调用b的call指令的地址在CFG中相连,表示c返回到a中调用b的地方
间接跳转指令
- 目标地址依赖于寄存器的jmp指令
- 应用场景
- 间接函数调用与尾调用优化结合
- switch-case构成的跳转表,存储在.rodata,使用间接跳转的相对偏移计算出跳转表位置,然后跳转至表包含的目标地址,再将其在CFG内与指令相连
2.重构CFG得到ITC-CFG
- 匹配函数调用时参数最多使用数量限制目标地址集合TyoeArmor
- 寄存器读推断消耗参数数量
- 寄存器写推断准备参数数量
- 删去直接转移指令的边,连接间接转移指令的目标基本块,得到符合IPT记录包的样式 FlowGuard
- 存储在控制流检查器
3.开启IPT
- 虚拟机管理器
- IA32_RTIT_CTL MSR控制记录包的条件
- TraceEn与BranchEn设为1开启控制流记录
- OS设为1User设为0记录内核控制流
- FabricEn设为0将直接记录包发至内存
- 扩展页表内将内核模块设为不可执行
- 记录被保护的虚拟机的控制流,并将记录包存入内存
4.运行时动态检查控制流合法性
- 时机:被保护的内核模块切换至内核核心进行控制流检查,减少频繁检查造成的开销
扩展页表错误
控制流到达被保护内核模块调用扩展页表错误函数
- 权限切换:内核模块设为可执行,内核核心设为不可执行,然后交还控制流给内核模块
控制流从被保护内核模块转移至内核核心
- 调用扩展页表错误函数:满足控制流触发条件?
- 调用控制流检查器
- 条件:触发本次错误的地址不在被保护的内核模块内,上一次触发错误的地址在被保护模块内(当前控制流跳出内核模块到内核核心的瞬间)
- 被保护内核模块内控制流
- 内存内取出IPT记录包(挑选出控制流在虚拟机中)
- 比对IPT包与ITC-CFG:逐个读出tip地址查找对应节点,找到了就依次顺序查找,失败报错
- 内核模块返回到内核核心的返回指令目标地址合法
- 影子堆栈检查返回值:虚拟机管理器内维护函数调用栈,控制流从内核核心到被保护的内核模块时若目标地址为某个函数开头,则内核核心调用了内核模块的函数,将存在内核的栈顶值即返回地址读出,存入维护的调用栈
- 内核模块返回指令且目标地址在内核核心中,比对地址与栈上最新地址,相同则合法
- 控制流在被保护模块内接收到中断时,内核核心需要处理中断从而暂停内核模块执行,造成了未知的控制流挑战
权限切换
- 内核模块设为不可执行,内核核心设为可执行,然后交还控制流给虚拟机运行,恢复开启保护的状态
局限性
- 单用户空间进程
- 未实现内核代码监控
- 开销上升
- 未成功可视化
控制流劫持保护
Test Tool for validation
- 得到目标PID与测试文件
- 注入TestToolAgent.dll到目标内存区域
- 验证测试用例文件
- 使用keystone安装ROP gadgets
- 目标进程请求运行内存
- 将gadgets写入内存块
- 建立每个gadgets的地址的payload
- 缓冲区溢出发送payload
控制流检查 intel CET
- shadow stack
- indirect branch tracking
- IDLE接受指令进入WAIT_FOR_ENDBRANCH,若ENDBRANCH则回到IDLE,否则产生一个#CP
CFI-Analyzer
- 进程管理员开始进程
- 进程管理员对每个入口指针设置BP
- 事件管理员接收到入口指针信号并将进程管理员排序来设置断点在call与ret指令,然后开始追踪运行
- 触发任意断点事件管理员停止追踪,分析数据,生成追踪而后继续运行
- 跳转与调用CFI,多线程,内核代码,单代码与单进程支持