作者:Yue CHEN, Florida State University, 博士在读
当我们的系统遭受攻击时,即便我们已经检测到攻击,如何知道被利用的漏洞在哪里呢?
先让我们来看一个简单的例子(见上图):假如用户输入的argv[1]是个巨长无比的字符串,由于strcpy不限制拷贝的字符数,buf会被溢出,那么在第6行函数return时候,很可能会控制流异常。假如使用攻击检测,很可能检测到第6行的return 0,但是真正的漏洞在第4行。症状(Symptom)和根本原因(Root Cause)在不同的地方。对于复杂的大程序,想要定位到Root Cause还是非常困难的。
目前基于memory instrumentation的漏洞检测,由于要instrument所有的内存访问,其开销特别大,因此不适合用于程序运行时使用。所以这篇论文提出个系统叫Ravel (Root-cause Analysis of Vulnerabilities from Exploitation Log), 它包含一个online模块和一个offline模块. Online的模块把运行的不确定事件都记录(Record)下来,并且提供攻击检测功能;Offline的模块可以重放(Replay)程序的运行,并且可以instrument memory并且定位漏洞。
这样做有若干优势:
- 可以把真实世界里的攻击,在实验室环境进行重放(Replay)并分析。
- 运行时候性能开销很小,开销大的漏洞定位阶段被放在了Offline的实验室里。
- 可扩展。可以自由添加新的攻击检测和漏洞定位方法。
对于攻击检测,Ravel用了现有的方法,比如程序崩溃,异常系统调用,CFI等。 对于Record & 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呢?
Ravel用了一些启发式搜索(heuristics)。这里用buffer overflow举个例子。
比如我们本来有三个use (也就是read from memory):
然后有一个大的def (write to memory) 把这仨use都覆盖了:
假如发现这些def-use关系是violation,那么很有可能,漏洞在def的指令及其周围。
并且很有可能是buffer overflow, memory overwriting之类的。Violation关系如下图:
别的漏洞,比如information leakage,也可以用类似的方法来定位。
对于整数错误(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或者
那么有些整数错误是程序员/编译器故意设置的,如何区分呢?
- 由于这些错误已经和reported的漏洞相关,所以非常大可能是漏洞,而不是正常的整数操作。
假如Race Condition存在,replay的execution trace会和record下来的不一样,所以可以用该方法来检测。
一旦检测到,Ravel继续用happens-before relation来进一步尝试定位漏洞。
Ravel还能检测以下一系列漏洞,具体参见paper,此处不再赘述:
Use-after-free and Double-free
Buffer Overflow
Integer Errors
Information Leakage
Format String Vulnerabilities
Ravel的Record & Replay功能基于FreeBSD 10.2实现,漏洞定位基于Valgrind实现。
下图用Nginx的CVE-2013-2028 漏洞举了一个Ravel如何从攻击定位到漏洞的例子, 具体描述参见论文。
更多的Evaluation实验, 比如Heartbleed,以及别的漏洞类型比如
Null Pointer Dereference,
Heap Overflow,
Out-of-bounds Read,
Untrusted Pointer Dereference,
也可以在论文里找到。
最后是Online Performance Overhead的Evaluation, 实验是在流行的web服务器和SPEC CPU 2006上做的, 可以看出性能开销非常小。
论文链接: http://ww2.cs.fsu.edu/~ychen/paper/ravel.pdf
Slides链接(pptx): http://ww2.cs.fsu.edu/~ychen/paper/ravel.pptx
Slides链接(pdf): http://ww2.cs.fsu.edu/~ychen/paper/ravel_slides.pdf
作者简介:
Yue CHEN, Florida State University, 博士在读。期间曽有幸在 百度美研X-Lab, 华为美研大数据组 等地实习。
研究兴趣为系统安全,移动安全。个人主页: http://YueChen.me