自动化攻防内核安全

bsauce读论文:Unleashing Use-Before-

2019-05-05  本文已影响26人  bsauce

摘要

        目标:内核栈变量未初始化漏洞利用。

        问题:由于很难控制内核栈区域,未初始化漏洞一直被忽视,例如,全内存安全技术(SoftBound+CETS)就没有考虑该漏洞。

        解决:提出自动化定向栈喷射方法,来利用Linux内核栈变量未初始化漏洞。包含两种技术:确定性栈喷射—采用定制的符号执行和引导型fuzzing来识别内核输入,使用户程序能引导内核代码路径并在内核栈布置攻击数据;消耗内存型喷射—使用内存消耗和污染方法控制内核栈。并提出基于编译的漏洞修复技术,将未初始化的变量初始化为0,带来的额外开销很小。

        结果:定向栈喷射能控制超过91%的内核栈(前者能控制32%常用栈区域,后者能控制89%的内核栈空间),结合未初始化使用漏洞就能实现提权。

1.简介

漏洞示例:见Figure1。若cpg->eng_st != ENGINE_IDLE,则会使用未初始化的backlog。攻击者可通过控制backlog指向恶意代码函数,从而劫持控制流。

Fig1-未初始化利用漏洞示例

(1)挑战

难点(内核栈未初始化):

        a.栈空间被其他代码复用,导致布置的数据被覆盖;

        b.内核栈对象很小且固定,很难布置;

        c.栈深度检查很严格。

要求:

        R1—得知道未初始化变量在栈上的相对地址;

        R2—R1未初始化变量的值可控;

        R3—R2上的数据在被使用前不被覆盖。

(2)贡献

        a.提出自动定向栈喷射,能稳定地向内核栈写任意数据;

        b.采用定制符号执行和引导型fuzzing确定性的控制常用的栈区域;提出一个控制动态分配的内核内存的策略,包括内核栈;

        c.验证了内核栈的未初始化内存是可控的,未来的内存安全技术应该重视未初始化使用漏洞;

        d.提出实用的缓解措施,来防止未初始化使用的利用,开销很小。

2.定向栈喷射方法简述

2.1 确定性栈喷射

目标:找到syscall的一条执行路径和相应参数,能在未初始化变量区域(最常用的1KB)布置数据。

组成:符号执行引擎,引导型fuzzer,协作器。

步骤:见Figure3。

         a.SE找到一条能往内核栈上未初始化变量区域布置数据的路径,对于路径爆炸问题,需识别循环和循环条件,交给fuzzer选择性的执行这些循环;

         b.检测可控区域,将SE计算的输入中不影响路径走向的部分填上magic code,由引导型fuzzer执行,使用kprobes拦截执行并检查占内存上magic code区域,该区域即为可控区域。

Fig3-确定性栈喷射步骤

2.2 消耗内存型喷射

目标:引导栈空间分配到已布置好数据的空间。

步骤:

         a.先占用目标机器的大部分空间;

         b.剩余的小部分空间存放污染数据,迫使新分配的栈位于这部分剩余空间。

3.详细设计

3.1 确定性栈喷射

(1)符号执行syscall

        目标:遍历syscall执行路径,生成相应路径的具体化参数。

        平台选择:KLEE—基于LLVM编译器;S2E—基于QEMU,能进行全系统符号执行,支持在真实软件(用户程序、库、内核、驱动)栈中进行in-vivo分析而不需要使用抽象模型,支持选择符号执行能提高效率。所以选择S2E。

aa.自动测试用例生成

        S2E需指定哪些数据要符号化,例如Fig4,将pathname符号化。

        挑战:

                a.一些syscall需按顺序调用才有意义,可参考Linux Test Project(LTP设定了每个syscall的执行条件);

                b.指针类型的参数无法确定size,作者发现路径与指针指向的参数数量无关,所以假定指针类型的参数仅指向一个元素。

bb.路径探索

        目标:生成路径对应的参数,传递给fuzzer执行。

        参数:控制相关—符号执行生成sample value,用于路径约束;控制无关—填充magic code,未用于路径约束。

cc.识别循环

        目标:循环很有可能是能布置内核栈的代码,但SE不能处理判断条件中含符号数据的循环,SE识别loop并收集loop条件(参数和值范围)交给导向型fuzzer。

        识别方法:

                a.跳转目标地址为已执行的指令;

                b.跳转地址小于条件指令地址。

(2)引导型fuzzing

        目标:确定定向栈喷射是否成功,也即内核栈上是否有magic code。

        步骤:

                a.输入构造:sample value +magic code,指针类型的参数仅指向1个元素(内核栈上分配对应size的内存,填充magic code,指针指向该内存);

                b.用kprobes[2]拦截syscall返回,检查栈内存,哪些区域被magic code污染。

(3)协作器

        SE与fuzzer工作于QEMU虚拟机。通讯:协调器给虚拟机发命令;SE输出到fuzzer;fuzzer的确认结果发给协调器。控制:若SE和fuzzer卡住,协调器向他们发送STOP信号。

3.2内存消耗型喷射

优势:确定性喷射很难找到控制内核栈顶部1KB区域的syscall路径;内存消耗型喷射不仅能控制内核栈,也能控制动态分配的内核空间,如内核堆。

步骤:

        a.占用内存,不断创建进程,每个进程往内存页随机写8字节(COW),并保持进程运行;

        b.污染内存,剩余空间(eg,50MB)用mmaps填充恶意数据,再用mumap取消映射。

4.实现

4.1确定性栈喷射器

(1)符号执行引擎

组成:S2E,自动测试用例生成器,2个S2E插件。

测试用例生成器:注意syscall定义来自SYSCALL_DEFINEx,x表示参数个数;注意指针型参数。Eg,SYSCALL_DEFINE3(open, const char *, filename, int, flags, uint16_t, mode)。

S2E插件:

        路径探索插件—探索执行路径,并利用约束求解器生成具体化参数;

        循环探索插件—识别判断条件依赖syscall参数的循环,具体来说,通过捕捉onTranslateBlockEnd信号来hook S2E的每个基本块块结尾,检查块最后一条指令时直接还是间接跳转,以拦截函数调用,同时保存已执行的指令地址以判断是否出现循环,若循环条件是符号化的,则调用求解器的getRange()计算看能的条件值范围。

(2)引导型fuzzer

定制的Trinity fuzzer:主要问题是输入合法性问题,例如文件描述符。

喷射校验器:用到kprobes工具(kprobe拦截syscall入口;jprobe拦截跳转指令;kretprobe拦截syscall入口和返回),使用kretprobe即可,获取stack_pointer和THREAD_SIZE,搜索栈内存,用/proc虚拟文件系统将结果传到用户空间。

(3)协作器:输入需控制的目标栈区域,输出可用的syscall。能布置内核栈的syscall有3类:能设置配置或写数据;含循环;含指针类型的参数。

4.2 内存消耗型喷射器

         Fork进程(使用Trinity),每个占2M,以消耗内存;污染线程往剩余空间写污染数据并mumaps消除映射。为确认新分配的栈使用了污染内存页,用kprobes拦截syscall入口并检查栈内存是否含magic code。

5.评估

栈喷射覆盖:确定性栈喷射能覆盖32%的常用内核栈,前10见Table1;内存消耗型喷射能覆盖89%覆盖率。

Table1-覆盖前10的syscall

覆盖分布:确定性栈喷射中,有些区域被很多syscall控制,这些区域很可能是栈对象区域,未初始化变量很可能在这里;Table2显示了控制唯一内存的syscall排名。

Table2-控制唯一内存的syscall

可靠性:内存消耗型喷射平均1.8次就能控制新分配的内核栈。

效率:确定性栈喷射在30min内能分析完大部分syscall;内存消耗型喷射用时2s内完成控制。

示例:

         CVE-2010-2963,出自Cook’s exploit[12],见Fig7。Karg.vc没有初始化就被copy_from_user()引用。首先用kprobes在do_video_ioctl下断点,得到未初始化指针kp->data的栈偏移396,使用确定性栈喷射技术找到能控制该偏移处8字节的syscall,找到27个syscall,通过在该偏移处构造恶意数据,触发任意地址写。

Fig7-CVE-2010-2963
上一篇 下一篇

猜你喜欢

热点阅读