KEPLER


paper

摘要

本论文的主要工作是根据用户提供的一个RIP劫持的poc来生成ROP链;

主要面临以下三个挑战:

  1. 缓解措施;
  2. 有些攻击措施可能会导致kernel panic;
  3. 由于缺少栈迁移等,导致无法完成最终的攻击;

亮点如下:

  1. 减少对漏洞和栈迁移的需要,single-shot、代码重用?就是ROP;
  2. 基于内核IO函数去布置栈;

具体步骤:

  1. 搜集5类ROPgadget;
  2. DFS搜索算法;

保护措施:

  1. SMEP+SMAP+canary+kaslr+kpti;
  2. 能够阻止对重要结构(cred、PT)的篡改;
  3. 启动虚拟化保护,防止对cr4寄存器的篡改;
  4. 线性映射区NX;
  5. STATIC_USERMODEHELPER,针对所有call_usermodehelper(),(是不是就是userfualtfd这类功能)
  6. 但是没有开启CFI检查;

漏洞原语:

  1. 假设已经有了一个RIP劫持的poc;
  2. 假设已经有了一个粗略的内存泄漏,(泄露了内核基地址了,就全有了。。。)
  3. 假设没有任意内存读、写的原语;
  4. 且当前不知道栈地址和任意线程的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的地方,且满足以下三个条件:

  1. dst是内核的栈;
  2. src是用户可控地址;
  3. length要足够大导致栈溢出;

大多数的copy_from_user的dst都是基于栈地址的(这里应该就是基于rbp或者rsp间接寻址);

选择short return path

copy_from_user存在三种快速返回的情况:

  1. src+length发生了整数溢出;
  2. src或者src+length处于用户态地址;
  3. 发生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;

几个亮点如下:

  1. 从一个RIP劫持触发构造栈溢出;
  2. 基于IO方法;
  3. 寻找短返回点(主要是copy_frm_user)减少内存错误;
  4. canary的泄露,利用copy_to_user,长度的不一致;辅助函数:需要有间接调用,保证canary的配对;
  5. blooming gadget:用于控制更多的寄存器,需要有间接调用的参数寻找;
  6. bridge gadget:用于多个功能的拼接,需要有多次间接调用;(这样总结下来,其实是寻找具有多个间接调用、且从rdi解引用构造参数的函数)

思考如下:

  1. 为什么这么钟情于canary,明明能够找到大量的ROPgadget,根本不依赖于canary值;
  2. 多次函数调用这种事情也不是没干过,prepare_kernel_cred+commit_cred,就是可能保存返回值略微有点麻烦,但是也是可以做的;
  3. 这个方法和pt_regs相比,有什么优势?pt_regs会存在不适用的情况?

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