作者:Yueqi Chen, Zhenpeng Lin, Xinyu Xing
单位:The Pennsylvania State University
出处:CCS 2020
资料:Paper
1. Abstract
本文作者提出了一个绕过KASLR以及泄露关键内核信息的系统性方法,这个方法通过静态/动态分析的方式来找出内核漏洞利用中可利用的Elastic Object,从而绕过KASLR或者泄露关键内核信息。作者实现了一个原型(命名为ELOISE),通过对40个内核漏洞进行实验评估,作者发现ELOISE可以帮助其中的大部分来达到目的。最后作者还提出了一个针对性的防御机制,并且通过实验评估其性能开销,验证了性能开销是可忽略不计的。
2. Introduction & Background
近年来内核的防御机制已经很多,许多以前常见的利用方法已经变得不可行,例如KASLR的设计让攻击者在劫持控制流的情况下无法跳转到一个可靠的可利用函数。对应地,也有很多的利用方法,其中一个比较常见的利用方法是通过一个Overwriting Primitive来操作Elastic Kernel Object,从而绕过KASLR。本文作者解决了如何来自动化地找到可利用的Elastic Object的问题。
Threat Model
攻击者可以通过PoC程序获得Overwriting Primitive。
Elastic Object。如下图所示例子,xfrm_replay_state_esn
是一个Elastic Object,包含了两个关键字段,一个是缓冲区字段bmp
,一个是长度字段bmp_len
,对应了系统调用recvmsg
可以读取的bmp
的长度。调用recvmsg
可以读取bmp
里的数据,并返回给用户空间。进行实际的利用时,通过越界写将bmp_len
改大,就能泄露出f_op
了,从而绕过KASLR。除了绕过KASLR,Elastic Object也可以帮助泄露栈或者堆的Cookie,甚至可以任意地址读。如果缓冲区是在栈上,改大长度就能越界读栈Cookie,如果在堆上,可以越界读堆Cookie。
3. System Approach
3.1 Identifying Elastic Object Candidates
3.1.1 Tracking down elastic structure candidates.
Elastic Object包含一个长度字段(整型),通过检测这个字段来筛选出候选的Elastic Object。
由于Kernel实现的复杂性,在检测长度字段之前,首先将所有的结构体进行平坦化处理,最终将结构体里的成员类型都变为平凡数据类型 (char, int *...)
,或者一维数组。然后就可以比较容易地检测出有整型类型的结构体,这些结构体将被认为是候选的Elastic Object。
3.1.2 Pinpointing elastic object candidates at allocation sites
通过如下策略进一步筛选Elastic Object,并收集信息:
1) 识别代码中所有的内存分配函数调用。
2) 通过分配代码的返回类型判断是否为候选Elastic Object。
3) 检查到达这个内存分配函数调用的代码是否需要root权限。
内存分配函数有两种类型:
(1) kmalloc
,kalloc
和malloc
类型,从general cache/zone里分配。
(2) kmem-cache
, mcache_alloc
, uma_zalloc
,从特定cache/zone里分配。
对于(2)中的分析返回值的类型,是通过分析use-define chain来确定具体类型。对于(3)中的检查权限,检查调用路径中是否含有下列函数调用,对如下函数的调用意味着需要root权限,除此之外还要检查调用路径里是否包含了对一些特权设备的访问。
1) Linux: capable(CAP_SYS_ADMIN)
2) FreeBSD: priv_check
, priv_check_cred
3) XNU: priv_check_cred
3.1.3 Profiling elastic object candidates
获取Elastic Object的一些属性,包括所属的cache/zone的信息。对于 kmem-cache
, mcache_alloc
, uma_zalloc
,可以通过检查参数就能获得这个信息。对于kmalloc
,kalloc
和malloc
类型,通过判断size的大小来确定是哪种类型的cache/zone,例如如果是132字节,则是kmalloc-256,但是如果分配的大小里有常量加变量的形式,采用的是启发式的策略,认为大于常量大小的cache/zone。
3.2 Filtering out Object Candidates
Elastic Object还需要一个信息泄露信道,并且识别出来的长度字段需要代表的是一个内核缓冲区的长度。因此需要进行更进一步的筛选。
作者总结了一系列关键的内核函数,将这些函数的调用视作为Leaking Anchors,这些函数可以将内核缓冲区的数据拷贝到用户空间,或者通过网络发送,如下图所示。通过将数据地址参数和长度参数设置为污点源,分别进行跨过程的后向污点分析。
对长度变量的污点分析,追溯起来源,如果发现是来自栈或者全局变量,则不继续分析。如果是来自堆的,则继续对数据地址参数进行后向污点分析。
数据地址来源有如下形式:
(1) src = &buffer
(2) src = &objA→buffer
(3) src = objA→p
如果数据地址参数指向的是栈上的区域,那攻击者可以通过越界读来绕过KASLR,泄露Cookie。
第(2)种类型的来源代表指向一个堆上的Kernel Object。对于第(3)种类型的来源,作者更进一步检查长度是否在同一个Object里,如果是,则可以做到任意地址读。
到此为止,可以进一步筛选出那些有对应的信息泄露信道的Elastic Object,及其对应的Conclusive Capability。
3.3 Pairing Vulnerabilities with Objects
通过前面的分析,可以得到每一个Elastic Object对应的信息:
1) 对应的cache/zones;
2) 长度字段在结构体内的偏移;
3) 缓冲区指针在结构体内的偏移(如果缓冲区指针与长度字段在同一个object里);
4) 分配Object的代码;
5) Leaking Anchor
6) Conclusive Capability
在给定一个漏洞的情况下,作者通过运行漏洞对应的PoC,人工通过调试器确定漏洞会破坏的cache类型,以及对应的可控位置及对应的值范围。例如图3,kmalloc-128里的Vulnerable Object可以溢出到下一个相邻的spot,并且只能控制 [0, 8) 以及 [16, 24)的区域。然后通过这些信息来从Elastic Object里挑选对应的Object。
4. Evaluation
评估工具识别Elastic Object的能力
ELOISE依赖于静态分析,但是静态分析的精确度依赖于控制流图的准确性,以及编译时的优化选项。
作者评估了假阳性和假阴性。假阳性指错误地将一个非Elastic Object识别为Elastic Object,假阴性没有识别出Elastic Object。
对于假阴性,作者采取人工检查的方式。由于内核代码的复杂性(53775个分配,75100个关键函数调用),作者只通过随机采用分析假阴性。对于假阳性评估,作者通过自动化方式来实现。
对于工具识别出来的97个Kernel Object,作者确认了其中74个为准确识别,因此假阳性比例是可观的。对比作者随机采样分析出来的Elastic Object,都被包含在了这些里面。
评估是否能绕过Mitigations
作者选取的40个漏洞中的27个可以找到合适的Elastic Object,并且能绕过KASLR。
评估ELOISE在漏洞利用中的实用性
邀请了有Linux内核漏洞利用经验、熟悉文章中漏洞利用方法的人进行测试,分为A/B组,分别对5个漏洞进行实验。A组用作者的工具来进行,B组则不用。测试结果如下图所示,实验分三个阶段,1) Vulnerability capability exploration, 2) Elastic object identification, 3) Memory layout manipulation,测试者需要在每个阶段进行汇报。A组对每个漏洞都可以成功绕过KASLR,B组则一个都做不到,并且B组认为寻找Elastic Object是最困难的。
5. Defense Mechanism
将ELOISE识别出来的Elastic Object都用一个Isolated cache/zone进行存储,例如一个原本在kmalloc-16里的Elastic Object,现在将存访在kmalloc-isolated-16里。
这些Shadow cache是在系统启动时创建的,作者修改了内核源码,并加入了flag (__GFP_ISOLATE)来标识Elastic Object。实验评估发现性能开销评价在0.19%,安全性评估发现40个上述漏洞无法在匹配到对应的Elastic Object。