CSE_lecture22:ROP and CFI
ROP and CFI
security principles
- least privilege: 只提供完成事件的最小权限
- least trust: 给组件分配任务时只提供最小的信任,如zero trust,内部也视为外网,使用U盘鉴权
- users make mistakes: 安全要考虑用户犯的诸多错误
- cost of security: 安全的成本不能太高
stack buffer overflow
攻击者往往从控制流或数据的角度来攻击
stack buffer overflow通过栈溢出的手段修改return跳转的地址,从而返回到攻击者设计的恶意代码部分
防御手段为DEP(data execution prevention),即数据段不可执行,即设置为non-executable,需要修改NX bit
此时攻击者使用ROP(return-oriented programming),寻找原有的代码碎片(gadget),这些碎片都以return结尾,在栈上注入地址指向这些碎片,从而将这些碎片代码合并在一起
防御手段有:
- 隐藏binary file,从而无法获取gadget
- ASLR将代码放在随机的地址
- 在return addr前面放置canary,覆盖addr时也会覆盖canary,通过检查canary来判断是否被攻击
CFI: control flow integrity
设置合法的控制流,防止控制流被劫持,这需要描述control flow graph,将其和可执行的二进制文件合并后得到一个self-checking program,加入了多个检查点
攻击者往往从跳转部分劫持,大部分都是direct branches,少部分的indirect branches绝大多数也是跳到那一两个地址:
- direct branches: call或jump后跟着静态的地址
- indirect branches: call或jump后跟着寄存器,代码运行后才能确定跳转的地址,以及return
CFI通过二进制重写的方法将正确的跳转思路内嵌到代码中做检查
但这需要保证所有的代码都被patch,对于库代码可能出现问题。因此使用prefetchnta,访问内存地址,如果不合法就等价于nop,比较4字节的prefetchnta后的数字即可
假设A可以到C,B可以到C和D,此时C和D的tag一样,这就会导致A可以非法访问D,如果加入判断,会导致性能下降
因此引入shadow call stack,即单独维护一个存放return address的栈,这是由软件实现的,从而通过比较软件和硬件栈中的地址来判断stack over flow是否出现。为了提升性能,直接使用Intel CET维护硬件层面的shadow call stack
CFI保证了stack over flow的防御,但不能防御数据修改,如函数指针,或权限位
blind ROP
流程为:
- 禁止ASLR,因为在接受socket时会folk,此时地址一样,并没有重启并让地址随机偏移
- 窃取binary:注入数据测试什么时候触发stack over flow,再继续通过是否崩溃一位一位猜测地址(这是因为canary不变)
- 寻找gadget:测试获取的地址附近的地址,判断是crash还是hang,有用的gadget时而crash,时而hang,说明结尾是return,只是return后跳转的第二个地址是否有效罢了
- 设置write的参数,使用BROP和PLT来设置寄存器的值和函数地址