retspill


paper : https://dl.acm.org/doi/pdf/10.1145/3576915.3623220

paper

摘要

提出了一种部署用户态数据到内核栈的方法辅助CFHP走ROP提权的方法;

对栈迁移的批判理由:

他们依赖特定的内核内存布局,以及某些gadget的精准存在或者一些额外的原语(例如对寄存器控制)的存在;

本工作能够绕过现有的内核保护措施,包括最新的FGKASLR,不需要任何其他的条件(例如寄存器的控制和特殊的内存配置)

当前内存保护:

SMEP, SMAP, KPTI,

NX-physmap:标记线性映射区为不可知性;

CR Pinning 阻止对CR4寄存器的非授权修改;

STATIC_USERMODE_HELPER:应该就是组织userfaultfd这些;

RKP:不许与直接修改进程的cred;

pt-rand:使直接修改内核页表的方法变得无效;

RANDSTACK:堆栈偏移,pt_regs不好用了;

威胁模型:

拥有KEPLER的所有保护措施,此外还有FG-KASLR;

对于原语,需要控制流劫持和内核地址泄露;

design

本方法完全不打算在堆中部署ROP,考虑到有些情况下,系统调用栈中最多部署7个ROP,因此就想每次执行一点点功能,然后退出去再重新进来;

数据存储

直接存储,像poll这个系统调用一样,copy_from_user将数据复制到内核栈中:

间接存储:通过多重syscall将数据加载到内核栈中,例如open+readlink两个系统调用;

在本工作中,要系统的分析直接存储,首先分析用户态的数据在系统调用期间是如何流向内核栈的——主要就是用户的寄存器和用户的内存地址空间,在用户态空间进行污点分析,然后手工分析污点的结果,

直接存储:copy_from_user;

pt_regs

调用约定:保存相关寄存器的值到栈上,分两种情况,一种是内核真的调用了syscall处理函数,另一种是系统调用处理函数调用其他的函数;

未初始化内存:之前系统调用用过的数据还残留在内核栈上;

武器化

startingROP:add rsp, x; ret

SmallChains:可用gadget通常很短,不同的syscal有不通过的情况;很CFHP需要特定的输入,这又减少了布置gadget的数量;很多用户态留到内核栈的数据可能不是连续的,也不好用;

独立的ROP:考虑到CFHP往往是覆盖写了内核堆上的一个系统资源,因此在exploit进程中的所有线程都可以访问到这个系统资源,所以攻击者可以创建一个独立的线程去执行ROP(意思是新开一个线程不影响劫持控制流),使得ROP的终止不会影响到主线程,这样多个独立的线程去触发,可以避免多次堆布局?

不受限制的任意地址rwx:

整合:

保护绕过

SMAP、SMEP、KPTI:根本就没直接访问用户态数据;

RANDSTACK:用合法数据和上下文;由于只随机了5位,搞一堆ret增大成功率;另外如果没有开启panic_on_oops,如果没有开启pamic_on_oops,那么一个任务的指令是不会引起内核崩溃,所以可以用多个线程反复尝试,

STACKLEAK/STRUCTLEAK/INITSTACK:这些保护阻止用户访问未初始化的内存,

FG-KASLR:其他代码片段,如程序集存根,也可以编译为在内核中具有固定偏移量的可执行部分。

KCFI/IBT:它们都旨在保护Linux内核免受前沿控制流劫持攻击,用KCFI编译,__efi_call函数不验证其调用目标。即使有了完美的实现,攻击者仍然可以通过劫持后边控制流来获得PC控制。

Shadow Stack.

CFI+Shadow Stack

半自动化实现

只考虑用commit_creds(init_cred)的方式提权,不考虑多种方法。

输出用于抬栈的gadget的地址,触发函数(能够数据传递、调用触发系统调用)

本工作基于虚拟机快照,之后分析了所有能够流向内核空间的数据,最后使用符号执行判断gadget;


文章作者: q1ming
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 q1ming !
  目录