文/Yue CHEN
会找漏洞的时光机:Pinpointing Vulnerabilities
文/Yue CHEN
当我们的系统遭受攻击时,即便我们已经检测到攻击,如何知道被利用的漏洞在哪里呢?
先让我们来看一个简单的例子(图1):假如用户输入的argv[ ]是个很长的字符串,由于strcpy不限制拷贝的字符数,buf会被溢出,那么在第6行函数return时候,很可能会控制流异常。假如使用攻击检测,很可能检测到第6行的return 0,但是真正的漏洞在第4行。症状(Symptom)和根本原因(Root Cause)在不同的地方。对于复杂的大程序,想要定位到Root Cause还是非常困难的。
图1
目前基于memory instrumentation的漏洞检测,由于要instrument所有的内存访问,其开销特别大,因此不适合用于程序运行时使用。所以这篇论文提出个系统叫Ravel (Root-cause Analysis of Vulnerabilities from Exploitation Log), 它包含一个online模块和一个offline模块. Online的模块把运行的不确定事件都记录(Record)下来,并且提供攻击检测功能;Offline的模块可以重放(Replay)程序的运行,并且可以instrument memory并且定位漏洞。
这样做有以下优势:
1. 可以把真实世界里的攻击,在实验室环境进行重放(Replay)并分析。
2. 运行时候性能开销很小,开销大的漏洞定位阶段被放在了Offline的实验室里。
3. 可扩展。可以自由添加新的攻击检测和漏洞定位方法。
对于攻击检测,Ravel用了现有的方法,比如程序崩溃,异常系统调用,CFI等。 对于Record amp; Replay, Ravel主要记录了不确定事件,比如系统调用(syscall)结果,内核到用户空间的数据拷贝,同步原语等。
对于Replay阶段的Memory Instrumentation,Ravel集成了二进制翻译引擎(Binary Translation Engine,简称BT)。其中很重要的功能 (也是一项挑战) , 是可以把BT发出的syscall和目标程序本身的syscall区分开来,以确保replay和record的一致性。
对于漏洞定位,Ravel设计了一系列功能。首先是数据流分析(Data-flow Analysis)。写入一块内存叫做define(简称def),把从这块内存里读出数据叫use。假如指令A写入这块内存,指令B接着读了出来,它们就构成了一组def-use关系。一堆这种关系可以构成一张图,叫DFG (Data-flow Graph)。
我们可以预先把这些关系找出来,做一张DFG。假如运行时候有之前没见过的def-use关系出现,可以视为发现 违规(violation),意味着很可能有漏洞。那么如何知道出问题的地方是在def,还是在use呢?
图2
图3
Ravel用了一些启发式搜索(heuristics)。这里用buffer overflow举个例子。
比如我们本来有三个use (也就是read from memory),如图2所示。然后有一个大的def (write to memory) 把这三个use都覆盖了,如图3所示。假如发现这些def-use关系是violation,那么很有可能,漏洞在def的指令及其周围。并且很有可能是buffer overflow, memory overwriting之类的。Violation关系如图4所示。别的漏洞,如information leakage,也可以用类似的方法来定位。
图4
对于整数错误(Integer Errors), Ravel关注的是一些用整数计数(比如放在RCX/ECX寄存器里)的指令,比如MOVS, STOS 等; 和有整数参数的函数,比如memcpy, recvfrom 等。从这些参数开始,Ravel进行backwards search来定位漏洞。
可以定位的整数错误漏洞包括:
Assignment Truncation (比 如0x12345678 → 0x5678)
检测方法:从longer type赋值到shorter type
Integer Overflow/underflow (比如0xFFFFFFFF + 1)
检测方法:检测RFLAGS/EFLAGS寄存器
Signedness Error (比如 unsigned_int_var = signed_int_var)
检测方法:收集Hint。比如一些指令或者函数会指定特定的signed或者unsigned参数。详情参见slides或者paper.
那么有些整数错误是程序员/编译器故意设置的,如何区分呢?
由于这些错误已经和reported的漏洞相关,所以非常大可能是漏洞,而不是正常的整数操作。
假如Race Condition存在,replay的execution trace会和record下来的不一样,所以可以用该方法来检测。
一旦检测到,Ravel继续用happens-before relation来进一步尝试定位漏洞。
图5
Ravel还能检测以下一系列漏洞,此处不再赘述:
Use-after-free and Double-free
Buffer Overflow
Integer Errors
Information Leakage
Format String Vulnerabilities
Ravel的Record amp; Replay功能基于FreeBSD 10.2实现,漏洞定位基于Valgrind实现。
图5用Nginx的CVE-2013-2028 漏洞举了一个Ravel如何从攻击定位到漏洞的例子, 具体描述参见论文(论文链接: http://ww2.cs.fsu.edu/~ychen/paper/ravel.pdf)。
更多的Evaluation实验, 比如Heartbleed,以及别的漏洞类型比如Null Pointer Dereference,Heap Overflow,Out-of-bounds Read,Untrusted Pointer Dereference,也可以在论文里找到。
图6
图6 是Online Performance Overhead的Evaluation, 实验是在流行的Web服务器和SPEC CPU 2006上做的,可以看出性能开销非常小。
(责编:杨洁)
(作者单位为佛罗里达州立大学)