前言
从计算机体系结构的角度来说,内存安全违规(memory safety violation)一般分为两大类:
- 时间违规(temporal violation):典型代表为Use-After-Free(UAF)漏洞,即在释放内存后,再次对该内存进行访问。
- 空间违规(spatial violation):典型代表为栈溢出(stack overflow)漏洞,即在栈上分配的内存空间不足以存放当前的数据。
而面向返回编程(Return-Oriented Programming,ROP)、面向跳转编程(Jump-Oriented Programming,JOP)和面向调用编程(Call-Oriented Programming,COP)都是基于空间违规的攻击手段,本文将从攻击基本原理、现有芯片级对策和思考三个方面来介绍ROP/JOP/COP。
而ROP攻击产生的一大原因是因为,现代操作系统为了防止攻击者在栈上发起代码注入漏洞,采用了W^X的内存保护机制,即栈上的内存空间只能执行,不能写入。因此,攻击者在栈上发起代码注入漏洞时,只能通过覆盖返回地址来控制程序的执行流程,而这种攻击手段就是ROP。
以下是一个较为详细的ppt:memory-safety.pdf
ROP/JOP/COP
ROP
在x86中,ret指令指令的执行可以看做:
(1)pop:从栈中弹出一个返回地址;
(2)jmp:跳转到该返回地址处执行。
此外,函数调用的参数传递是通过寄存器(eax、ebx、ecx、edx等)来完成的,比如:
1 | execve("/bin/sh", NULL, NULL); |
其中,系统中断的值为0x80,而execve调用的值为0xb(存放在eax寄存器中),剩余三个参数分别为:/bin/sh(存放在ebx寄存器中)、NULL(存放在ecx寄存器中)、NULL(存放在edx寄存器中)。因此,如果攻击者能够将返回地址覆盖为0x80指令所在的地址,并且将四个寄存器的值分别设置为0xb、/bin/sh、NULL、NULL,那么就可以实现execve(“/bin/sh”, NULL, NULL)的调用。ROP代码段是一串以ret指令结尾的代码段,因此攻击者希望填充的恶意数据为:
(0)数据与返回地址的偏移量;
(1)pop eax; ret;
所在的地址;
(2)eax寄存器的目标值0xb;
(3)pop ebx; ret;
所在的地址;
(4)ebx寄存器的目标值/bin/sh;
(5)pop ecx; ret;
所在的地址;
(6)ecx寄存器的目标值NULL;
(7)pop edx; ret;
所在的地址;
(8)edx寄存器的目标值NULL;
(9)0x80指令所在的地址。
指令序列的寻找可以通过JonathanSalwan开发的ROPgadget工具[1]来完成,以下面这段代码为例:
1 | ... |
由于gets函数存在栈溢出漏洞,因此攻击者可以通过恶意输入var1覆盖返回地址,并完成恶意代码执行:
(1)计算var1与返回地址的偏移量;
(2)用ROPgadget工具生成ROP链;
(3)将ROP链写入var1,从而控制程序的执行流程。
JOP/COP
在JOP攻击中,攻击者控制的恶意数据覆盖jmp指令的跳转地址实现控制流劫持,JOP代码段是一串以jmp指令结尾的代码段。而在COP攻击中,攻击者控制的恶意数据覆盖call指令的跳转地址实现控制流劫持,COP代码段是一串以call指令结尾的代码段。对于JOP/COP攻击而言,攻击者要找寻的代码段中,除了要修改目标寄存器外,还要包含一个用于修改后续jmp/call跳转地址的pop指令(在ROP攻击中,由ret指令的特点,不需要攻击者额外控制)。
值得一提的是,在实际应用中,攻击者为了达成劫持控制流的目的,会将三种代码段通过组合的方式发起混合攻击。
现有硬件级对策
Intel CET
Intel CET(Control-Flow Enforcement Technology)是Intel新推出的一项新的硬件级对策,其主要目的是防止攻击者通过ROP/JOP/COP等攻击手段来劫持控制流。
对于ROP攻击的防护,其基本思想与影子栈(shadow stack)类似,即由操作系统在内存中复制一份程序的内存栈或者是仅仅保留控制流跳转地址。然后,这个影子栈无法由正常的store、load指令进行控制,只能通过专门的指令来进行控制。因此,即使攻击者覆盖了软件栈上的返回地址,但是由于影子栈中仍然保存着原始的跳转地址,因此检查失败后会通过抛出异常来终止程序的执行。
对于JOP/COP攻击的防护,Intel提出一种叫做IBT(Indirect Branch Tracking)的技术,其基本思想是通过编译器在合理的间接跳转中⽤新的指令做标记,然后程序执行时会检查下一条指令是否为新添加的指令(endbr),如果不是则会抛出#CP异常。
ARM PAC
ARM PAC(Pointer Authentication)是ARMv8.3引入的一项新的硬件级对策,其主要通过对指针进行鉴权来防止攻击者通过ROP/JOP/COP等攻击手段来劫持控制流。
这种防护方法的基本原理是,利用64位地址空间中暂时空闲的高16位来存放指针的鉴权结果(MAC码),然后在每次指针使用前,都会对指针进行鉴权,如果鉴权失败,则会抛出异常来终止程序的执行。
相关论文(体系结构级别)
论文:PACMAN(ISCA 2022)
利用预测执行机制,通过隐蔽信道泄漏PAC机制的鉴权结果。
论文:ZeRØ(ISCA 2021)
ZeRØ提出了独特的内存指令和新颖的元数据编码方案来保护代码和数据指针,仅仅只需要微小的微架构变化。ZeRØ在SPEC CPU2017基准上的性能开销为零,VLSI测量显示了低功率和面积开销。
论文:No-FAT(ISCA 2021)
No-FAT将内存分配大小(例如malloc大小)作为一个架构特征,来克服传统内存安全方法的许多棘手问题,例如与不安全软件的兼容性和显著的性能下降。No-FAT在SPEC CPU2017基准测试中产生了8%的开销,VLSI测量显示了低功率和面积开销。
参考文献
[1] https://github.com/JonathanSalwan/ROPgadget
[2] Ravichandran, J., Na, W. T., Lang, J., & Yan, M. (2022, June). PACMAN: attacking ARM pointer authentication with speculative execution. In Proceedings of the 49th Annual International Symposium on Computer Architecture (pp. 685-698).
[3] Ziad, M. T. I., Arroyo, M. A., Manzhosov, E., & Sethumadhavan, S. (2021, June). ZeRØ: Zero-overhead resilient operation under pointer integrity attacks. In 2021 ACM/IEEE 48th Annual International Symposium on Computer Architecture (ISCA) (pp. 999-1012). IEEE.
[4] Ziad, M. T. I., Arroyo, M. A., Manzhosov, E., Piersma, R., & Sethumadhavan, S. (2021, June). No-FAT: Architectural support for low overhead memory safety checks. In 2021 ACM/IEEE 48th Annual International Symposium on Computer Architecture (ISCA) (pp. 916-929). IEEE.