摘要
本论文的主要工作是根据用户提供的一个RIP劫持的poc来生成ROP链;
主要面临以下三个挑战:
- 缓解措施;
- 有些攻击措施可能会导致kernel panic;
- 由于缺少栈迁移等,导致无法完成最终的攻击;
亮点如下:
- 减少对漏洞和栈迁移的需要,single-shot、代码重用?就是ROP;
- 基于内核IO函数去布置栈;
具体步骤:
- 搜集5类ROPgadget;
- DFS搜索算法;
保护措施:
- SMEP+SMAP+canary+kaslr+kpti;
- 能够阻止对重要结构(cred、PT)的篡改;
- 启动虚拟化保护,防止对cr4寄存器的篡改;
- 线性映射区NX;
- STATIC_USERMODEHELPER,针对所有call_usermodehelper(),(是不是就是userfualtfd这类功能)
- 但是没有开启CFI检查;
漏洞原语:
- 假设已经有了一个RIP劫持的poc;
- 假设已经有了一个粗略的内存泄漏,(泄露了内核基地址了,就全有了。。。)
- 假设没有任意内存读、写的原语;
- 且当前不知道栈地址和任意线程的canary;
例子
CVE-2017-8890,ip_mc_socklist结构体的UAF;
使用堆喷技术导致无法控制第一个成员;
inet->mc_list的uaf;
overview
single-shot
类似于用户态中的ogg,但是没那么厉害,不可能一步到位直接提取;
避免多次系统调用而导致的内核崩溃,能够自己创造出栈溢出;
design
两大原则:
利用IO打破隔离(copy_to_user、copy_fron_user)
单一系统调用;
构建栈溢出
stack-smashing相关gadget
有一些通过缺页异常触发的 固有短返回路径(intinsic short return path)
“Intrinsic short return path” 是一种在程序执行过程中出现的优化术语,尤其是在编译器优化或处理器执行路径中被讨论。它指的是在某些特定情况下,程序为了执行效率而选择了更短的、内在的(intrinsic)返回路径。
劫持RIP到即将调用copy_from_user的地方,且满足以下三个条件:
- dst是内核的栈;
- src是用户可控地址;
- length要足够大导致栈溢出;
大多数的copy_from_user的dst都是基于栈地址的(这里应该就是基于rbp或者rsp间接寻址);
选择short return path
copy_from_user存在三种快速返回的情况:
- src+length发生了整数溢出;
- src或者src+length处于用户态地址;
- 发生PF;
前两种情况不做任何copy,第三种情况则实际发生了copy;
触发缺页
绕过canary
辅助函数:
function prologue:
辅助函数需要有canary,并且在其prologue执行完之后会有一个间接调用,这个间接调用会建立新的栈帧,它的栈帧布局会和一个栈信息泄露的gadget配对,形成一个完整的栈帧;
这里前提是控制RIP+控制参数rdi;所以才有间接调用;
看不懂为什么如此重视canary,canary不影响在ROPgadget中其他函数的调用啊,并且内核中的ROP完全可以从canary检查之后截断。。。
ROP缝合
blooming gadget
基于间接调用中,往往会对第一个结构体进行解引用,来作为后续函数指针以及参数;如果找不到潜在的可控寄存器(这里其条件定义是,==至少存在一个潜在的可以控制的寄存器,或者这个寄存器指向一个可控的堆空间==)(那么这个条件是否可以对比pt_regs方法中栈迁移呢?此外我觉得栈迁移的ROPgadget其实非常好找),那么就想办法寻找可控的连续栈,add rsp, 0x68; pop rdi; ret; 感觉这还不如pt_regs。。。
bridge gadget
基于多重间接调用;
这里还提到了用线性映射区的方式来伪造结构;
总结
本论文就是开启了除了CFI之外的所有保护的前提下,仅有一个RIP劫持的poc,不能栈溢出直接ROP,然后已经泄露了内核代码段和堆区地址,可以控制rdi寄存器,(且存在可控内核地址、或者线性映射区);
本论文是自动固件ROP chain;
几个亮点如下:
- 从一个RIP劫持触发构造栈溢出;
- 基于IO方法;
- 寻找短返回点(主要是copy_frm_user)减少内存错误;
- canary的泄露,利用copy_to_user,长度的不一致;辅助函数:需要有间接调用,保证canary的配对;
- blooming gadget:用于控制更多的寄存器,需要有间接调用的参数寻找;
- bridge gadget:用于多个功能的拼接,需要有多次间接调用;(这样总结下来,其实是寻找具有多个间接调用、且从rdi解引用构造参数的函数)
思考如下:
- 为什么这么钟情于canary,明明能够找到大量的ROPgadget,根本不依赖于canary值;
- 多次函数调用这种事情也不是没干过,prepare_kernel_cred+commit_cred,就是可能保存返回值略微有点麻烦,但是也是可以做的;
- 这个方法和pt_regs相比,有什么优势?pt_regs会存在不适用的情况?