目录
记录一下早上(20180828)看的这个ppt,免得白看了。ppt来自Linux Security Summit Aug 2018。
本文很多内容是读ppt的时候的一个记录,如果有理解不对的地方,欢迎指出。
此外,也有少部分内容没有完全理解,以后用到了再看。
ppt原文:https://events.linuxfoundation.jp/wp-content/uploads/2017/11/LSS2018.pdf
一、Android内核漏洞概览
Android中的内核漏洞占据所有Android安全漏洞的三分之一。
目前来说,漏洞缓解机制中达到的效果最好的是减小攻击面。访问控制是减小攻击面的一个具体实现。
Mark Brand - Google Project Zero说目前最有效的漏洞缓解措施已经生效了,大大减少了攻击面:加强的selinux的策略和seccomp sandboxing。
访问控制
这要求攻击者需要在更小的攻击面中找到更多的漏洞。增加访问控制也是成本比较低的,因为访问控制不需要有exp编写经验就可以应用在系统中。
攻击面减小之后导致好处就是,很多kernel vulns在userland是可以被触发的,但是不能被unprivileged进程触发。增加了访问控制机制后(selinux、unix 权限模型、Capabilities),这些漏洞都受到了限制。效果如下图所示:
seccomp sandboxing
seccomp sandboxing是内核隔离的一种实现。
参考资料:seccomp sandboxing是linux kernel2.6.23中引入的安全机制。它能使一个进程进入到一种“安全”运行模式,该模式下的进程只能调用4种系统调用(system calls),即read(), write(), exit()和sigreturn(),否则进程便会被终止。受到的限制是:Seccomp模式的进程不能动态分配内存、不能与其它进程使用共享内存、不能使用新的文件描述符、等等。如果要支持具有丰富功能的应用程序,则需要另外的方法来截获并处理其它系统调用。相关关键词:TOC2TOU、ftrace。来源:https://blog.csdn.net/xpylq/article/details/54138181
从Android O 8.0开始,所有的app都在seccomp sandboxing中运行,因此类似CVE-2017-14140这样的漏洞就受到了限制。参考资料:CVE-2017-14140 4.12.9版本之前,在Linux内核中的move_pages系统调用mm / migrate.c并不检查目标进程的有效uid,使本地攻击者可以学习尽可能使用ASLR的setuid可执行文件的内存布局。来源:https://www.deepin.org/2017/09/25/deepin-security-updates-cve-2017-12134-cve-2017-12153-cve-2017-12154-cve-2017-1000252/
访问控制+seccomp sandboxing是内核提供的两种漏洞缓解机制,目前都起到了很好的效果。
不需要权限在userland就可以被触发的bug
但是,仍然有一些不需要权限在userland就可以被触发的bug。按照触发的来源分类,如下图所示:
按照漏洞的类型分类,如下图所示。
因此内核中中增加了对copy_from_user这些函数的运行时检查。
引入了PAN漏洞缓解机制。效果如下:
-
组织kernel 直接访问 userland数据
-
加强的copy from user这些函数的校验
-
Found/fixed multiple instances of kernel directly accessing userspace.(不太明白啥意思)
总之,PAN就是为了让userland过来的数据更加安全的,kernel没权限读userland数据。
不从userland也可以被触发的bug
有了以上的缓解机制还不够(访问控制、seccomp sandboxing、PAN),因为还有一些kernel vulns不从userland也可以被触发。比如,从wifi 蓝牙 usb,个人理解这些属于硬件设备驱动的漏洞,直接运行在kernel land。
总结一下,目前的userland->kernel的防御已经做了以下两点:
- kernel提供的攻击面减少的工具:selinux、seccomp sandboxing
- 加强的usercopy + PAN 来缓解一些需要访问用户态数据时会发生的漏洞
但最后还有1/3的kernel bugs是由其他的vectors攻击向量导致的。因此还需要类似1、2中这样的工具来帮助我们。
内存安全对所有的内核漏洞进行分类
如果换个角度,根据内存安全对所有的内核漏洞进行分类,分为heap 越界读、heap越界写、stack越界写、heap微初始化,全局变量越界读。
二、CFI(Control Flow Integrity)
从这个角度出发,看看Control Flow Integrity的效果。
what:Control Flow Integrity是通过增加runtime checks来保证控制流在预先计算的图表中。
where: llvm>=3.7中包含了向前的(forward-edge)的CFI,有效保护非预期的分支跳转(indirect branches)。
How:工作的原理是,允许跳转 到 正确类型的有效函数的开始位置。(Allows an indirect branch only to the beginning of a valid function with the correct type.)
实际效果如何呢?55%的非直接的函数调用只有<=5个允许的目标函数去跳转,只有7%有多余100个目标函数去跳转。(不是特别明白,理解为开启了CFI之后的效果是增加了这些东西)
llvm链接时的最佳优化实践如下:
- llvm的CFI模块会用LTO来决定所有valid call targets。
- 必须使用llvm的整体的汇编器来进行inline汇编,必须使用LTO-aware的链接器,比如说 GNU gold linker或者是llvm的ld。
- 目前来说,几乎所有的问题都是由于工具链的兼容性导致的。在测试的几个月中,没有发现内核的稳定性问题。
过程如下:
在今年,第一批的android设备已经刷进了LTO+CFI的kenrel。发现了以下问题:
- 由于c编译器中允许类型不符的非直接函数调用(indirect calls),导致一些CFI保护失败,这些问题不大,不影响大局,修复了就完事儿了。
- Cross-DSO CFI support needed for kernel modules.
- CFI对于indirect calls增加了一个很小的开支(a small overhead),不过由于LTO的存在,尽管加入了CFI,总体的你表现还是不错的。
举个函数指针类型不符的例子,类似这些函数都得patch。
再举个例子(example continued):CFI check, slowpath for cross-DSO(不明白啥意思)。如果target address是允许的才返回。
如果target address有问题,那么CFI check失败,kernel panic。如果处于调试模式,permissive mode下会产生一个warning。panic信息如下:
如果你想要开启CFI,kernel 4.9和kernel4.14中开启如下两个选项,如果要调试,开启CONFIG_CFI_PERMISSIVE=y。
最后,未来的工作有2点:
- CFI仅仅保护了 indirect branches. LLVM’s Shadow Call Stack 会保护返回地址。
- GNU的gold linker有很多问题,未来会切换到llvm的 lld linker。
看完的感受是:Days become harder for android kernel pwners. 要学的太多了。。