Vulnerability mitigation
Published:
基础概念
漏洞缓解包括事前的安全编程、漏洞挖掘,事后进行补丁,对软件执行状态进行安全检查。
通用原理
防御思路:主动部署安全检查,感知攻击过程或消除攻击危害
解决方案:基于引用监控器的安全不变量检查
- 在关键的代码/数据引用位置部署引用监控器,实行内生安全检查
- 在软件执行过程中监测安全不变量,检测、缓解、阻断未知漏洞攻击
部署方式
- 基于漏洞条件加固(打补丁):运行时代价小,漏洞条件难以准确提取,漏洞无法提前预知、无法保证挖完
- 基于安全策略加固(安全检查):主动、通用、防御一类漏洞,性能开销大
漏洞缓解策略
完整性保护
阻断:代价大,安全强度较高
软件:Bound check + tag check=完全存储安全性
- bound check(分配meta->传播metadata->检查metadata)
- fat pointer: 使用两个指针标记buffer的开头和结束为止,修改buffer内容时首先检查是否超出buffer内容
- 可以检查子对象的溢出
- 使用内联的元数据,会改变存储布局,因此资源兼容性低
- object-based: 将对象插入object table,在修改时在object table中查找是否越界
- 不关联的metadata,不会改变存储布局,因此资源兼容性高
- 无法检测子对象的溢出,查找object table的成本不一定
- SoftBound: 使用不关联的metadata表代替fat pointer,在不改变存储布局的情况下对任意投射安全
- fat pointer: 使用两个指针标记buffer的开头和结束为止,修改buffer内容时首先检查是否超出buffer内容
- CPI(code pointer integrity): 将存储分为安全存储和普通存储,之间使用基于硬件的设施级别隔离
- 安全存储保存敏感指针和元数据,仅对敏感指针进行检查(代码指针和指向敏感指针的指针)
- 创建元数据时显式获取静态分配的内存对象或函数的地址,在堆上分配新对象,或获取子对象的地址
- 安全性检查:敏感指针的每次取消引用都会在运行时进行检查,普通指针的取消引用不用进行安全性检查
- tag check:对存储段打标记,在每次进行修改时对标记进行匹配,如果不匹配则不能进行修改 硬件
- ARM MTE
- Intel MPX:intel内存保护扩展
- 检查所有指针的读取和写入,以确保它们在已声明的内存范围内。这个技术可以检测缓存区的溢出,并且停止正在运行的程序以避免危及系统。
检测:代价小,安全强度相对较低
软件
- Stack canary:因为大部分buffer溢出都序列性出现
- 在返回地址附近放置一个随机的canary,在函数退出时检查canary的值来判断是否存在溢出
- 随机布置,因此攻击者难以猜到正确的cookie,很容易通过编译器支持实现
- 难以保护无法重编译的老代码,且可以绕过
- 通过信息泄露漏洞获取cookies/找其他不顺序重写的漏洞/重写返回地址之外的敏感数据/逐字节暴力攻击
- Shadow stack
- 在函数开头将返回地址RET和SFP复制到安全栈,在返回时检查RET和SFP是否和备份一致
- 保护性强,难以重写返回地址
- 需要保护安全栈,运行成本高,有兼容性问题 硬件
- ARM PAC:通过keyed-MAC检查是否存在溢出
- ROLoad:编译时根据方案生成指令,处理器内核检查内存页的权限,被访问的内存页是否只读,内存页标签是否与指令匹配
隐藏漏洞
软件
- 随机化代码地址(ASLR)
- 对地址空间的布局进行随机化,破坏攻击的指向
- 对代码段整体做随机,代码内部的指针相对位置不变,是对整体进行偏移
- 性能好(只用在加载时做一次随机化),需要内核支持,不需要重编译,对安全应用透明,安全性保证在32位系统上不佳,但在64位系统上较好,有效防御注入攻击
- 地址无关代码(PIC):可在存储器中任意位置正确运行,不受绝对地址影响的机器码
- 使用程序连接表(PLT:procedure linkage table)和全局偏移表(GOT:global offset table)的惰性链接方式,首次调用前全局偏移表调用程序连接表获得函数真实地址,全局偏移表随后将保存的程序连接表地址更新为函数真实地址。
- 地址独立可执行程序(PIE):ASLR必须,将所有的绝对地址引用替换为相对地址,与PIC相似,可将程序装载在存储器任意的地址
- 仅可执行存储
- 程序部署在仅可执行存储分段内,不能进行复制和修改,保护程序避免复制和干预
- 堆随机化 硬件
- 仅可执行存储
- 在普通存储中的程序可以调用在仅可执行存储中的函数(API),但看不到存储中的内容
- ARM 使用HPROT,ARPROT/AWPROT 信号的总线系统
- RISC-V 物理存储保护(PMP)
隔离资源
软件
- 软件错误隔离
- 目的:将错误隔离在不信任的扩展中,允许有效的跨域调用
- 主要思路:将进程地址空间按模块划分成不同的段,按不同的id标记
- 段匹配:按段id查找匹配的存储,提供专用的段寄存器和数据寄存器
- 段匹配代码必须运行以保证安全性
- 专用寄存器必须不能被模块修改
- 修改较多,可以在段id不匹配时精确定位错误
- 沙箱:强制要求顶层位字匹配段id,不对段id进行查找比较
- 修改较少,仅保证存储访问停留在区域内
- CPS
- 堆/全局隔离:程序存储通过硬件强制的设施级别隔离划分为安全存储和常规存储,安全存储仅保存代码指针,常规存储保存非代码指针,不修改存储布局。接触代码指针的修改使用基于类型的静态分析进行识别。
- 沙箱 硬件
- Intel MPK:
- 引入PKRU寄存器,指明内存页读写权限,需要同时通过页表和PKRU的权限检查
- 在页表中加入PKEY索引(59-62四个比特),指明在PKRU中的domain,domain指明具体权限
- PKRU寄存器存储16组权限,可以给不同线程分配不同domain,然后更改PKRU的domain给线程分配不同的权限,实现进程内隔离
- ARM DACR,Intel SMEP, SMAP
进行访问控制
软件
- 控制流完整性(CFI)
- 特点
- 保护性强:拥有对于全部数据存储的完全控制
- 应用范围广:语言无关,只需要二进制文件
- 可信/正确性可证明:有正式语义,容易验证
- 有效:实验中减少0-45%的攻击成功率?
- CFI模型:
- 可以在任意时间修改任意数据存储,可重写当前context的寄存器,但不能删除数据,修改代码,向%ip写入,重写其他context的寄存器
- 执行必须遵循在运行之前创建的控制流图中的路径执行
- 方法
- 在编译时静态创建控制流图
- 指定函数间的直接调用和非直接调用关系
- 在安装时调整二进制文件(增加ID和ID check/维护ID唯一性)
- 为控制流图中的每个调用目的添加唯一的标签,如果控制流图中同一个源节点有指向两个目的的边,则这两个目的等价(标签一样)
- 标签指明控制可转移的目标
- 在加载时验证CFI调整(直接跳转目标/ID和ID check是否存在/ID是否唯一)
- ID Check:跳转前检查目标的label,目标label匹配时才跳转
- 在运行时执行ID检查(不直接跳转要有匹配的ID)
- 在编译时静态创建控制流图
- 特点
- 数据流完整性
- 编译时计算变量的合法写操作
- 运行时追踪每个变量最后一次写操作
- 确保变量使用时写操作是合法的 硬件
- Intel CET
- 非直接分支追踪:追踪分支是否结束,防止跳转到任意地址,阻止面向跳转的编程攻击
- shadow stack:使用shadow stack提供返回地址保护,阻止面向返回的编程攻击
- Stop Code injection/exec.(NX/DEP):
- 将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令
- 有硬件支持时对性能无影响,其他情况小于1%,部署需要内核支持和模块内部算子支持(windows),可以深入合法的程序内部,安全性保证为在NX页的代码永不执行