会找漏洞的时光机: Pinpointing Vulnerabilities

作者: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并且定位漏洞

这样做有若干优势:

  1. 可以把真实世界里的攻击,在实验室环境进行重放(Replay)并分析。
  2. 运行时候性能开销很小,开销大的漏洞定位阶段被放在了Offline的实验室里。
  3. 可扩展。可以自由添加新的攻击检测漏洞定位方法。

对于攻击检测,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


为您推荐了相关的技术文章:

  1. 定时炸弹 - MQ 代理中危险的序列化数据
  2. 构造PPSX钓鱼文件 - Evi1cg's blog
  3. 360安全报告-2017年上半年网络诈骗趋势研究报告
  4. Rasp 技术介绍与实现
  5. 通过Netflow进行攻击AS溯源

原文链接: www.inforsec.org