动态污点分析(Dynamic Taint Analysis)是近几年刚刚被提出的一种新的有效检测各种蠕虫攻击和自动提取特征码用于IDS和IPS的一系列解决方案。其原理主要分为两大部分:动态污点标记和非法操作检测、以及更精确的提取特征码的方法。
其 主要原理是将来自于网络等不被信任的渠道的数据都会被标记为“被污染”的,由此产生的一系列算术和逻辑操作新生成的数据也会继承源数据的“是否被 污染”的属性,这样,一旦检测到被污染的数据作为跳转(jmp族指令),调用( call, ret )以及作为数据移动的目的地址,或者是其他使EIP寄存器被填充为被污染数据的操作,都会被视为非法操作,系统会报警并产生当前的相关内存、寄存器和一段 时间内网络数据流的快照并传递给特征码生成服务器作为生成相应的特征码的原始资料。同时服务会立刻终止或者在蜜罐环境中继续捕获进一步的入侵数据。
这个过程主要的实现是利用虚拟机技术,对特定的指令进行特殊处理,比如更新记录相应内存是否被污染的位图或者检查跳转是否安全。
上 面的步骤中提取的特征码原始资料,由于是在溢出(或者其他方式的攻击)发生时的快照,而且只提取被污染的数据(甚至只是被污染的EIP附近的数 据),而不是溢出成功后执行的shellcode ,因而具有很大的固定性和准确性,非常便于特征码提取子系统从中提取出比较通用和准确的特征码,降低了误报的几率。
采 用上面的原理实现的系统,比较有代表性的有TaintCheck[/ref{bib:taint}]和 Argos[/ref{bib:argos}],下面几节就分别讨论了两个实现的细节和优缺点,由于Argos[/ref{bib:argos}]更加详 细地描述了整个过程,我们就先从Argos 开始。
Argos分为三部分:动态污点跟踪和报警部分、特征码提取部分和特征码进一步综合部分。
第 一部分,即动态污点跟踪和报警部分建立在著名的开源虚拟机 Qemu 的基础之上,是对他的一个扩展。Argos用一个一一对应的位图映射来标识相应的物理内存和寄存器是否被污染,每个byte的内存和每个寄存器用一个 bit或者一个byte(两种实现都可)来标志其是否干净。Qemu会将各种处理器的指令集统一翻译成本地(即当前主机)处理器的指令,Argos的工作 就是针对不同的指令处理污点标记的继承关系(如/en{mov dst,src},dst 会继承 src 的污点属性),并且在出现 jmp, call 等指令的时候检查 EIP 是否将会被修改成污点标记的数据,或者被污染的数据是否作为系统调用的参数,若是,则认为是服务器被攻击了,产生一个报警并进而产生当前内存的快照和网络 流的快照,这就是下一步骤特征码提取需要做的工作。
第二部分是特征码提取部分,首先完成的是将当前的环境保存为一个快照作为特征码提取 和日后重现攻击的原始资料这个快照包括当前各寄存器的值,当前进 程信息,相关内存的镜像,以及近一段时间传输的网络数据流。其中寄存器直接由虚拟机软件中获得;进程信息是通过向当前进程中注入一段dump 进程和端口信息的shellcode并执行来实现的;内存快照是利用动态污点跟踪在污染数据侵入EIP 的那一刻触发的特点,直接找到EIP 指向的敏感内存区,以及由此判断的当前进程是处于ring0 或者 Ring3等级,只dump相应等级的标记为被污染的数据两部分组合而来的,体积上比起以前的方法会缩小很多;至于网络流,则是有另外一个程序(比如 TCPDUMP )专门来保存一段时间特定端口的网络数据流。
有了原始资料, Argos利用另外一个程序,利用LCS(最长公共子序列)法和他们自己开发的CREST法来产生特征码。CREST方法的原理是通过匹配在内存快照中原 始EIP指向的数据和网络流中出现篡改后的EIP 地址的区域的相同数据段作为特征码,并结合端口和使用的协议来产生一个snort格式的规则。
第三部分是特征码的进一步综合部分,这部分是通过一款名叫SweetBait的软件来实现的,即将多次检测到的攻击产生的特征码通过LCS进行进一步的提取,除去其中的目标IP地址等部分,产生通用的检测规则提交给各IDS/IPS使用。
Argos 的优点是准确,源代码无关、能够自动产生低误报率的特征码。以往的系统大多需要被保护的程序源代码是可以获 得的,通过静态的检查其中的不安全代码来保证服务器的安全性,这种方式对于商业软件是不可接受的,而且以往的静态检查误报率非常高。Argos 的准确性体现在两个方面:准确检测出攻击和产生低误报率的特征码。原因在上文中Argos 的实现的叙述中已经说明了。
Argos的缺点 也是显而易见的:低效。据[/ref{bib:argos}]作者实验,比起直接运行在实际的主机上,运行在Argos上的程序速 度将会减慢10-30倍,但作者同时又强调,Argos 的设计目的是作为一个蜜罐,因而服务的速度并不是第一位的同时由于网络环境的复杂性如网络延迟等,这方面的速度的牺牲不一定会成为系统的速度瓶颈,况且 Argos 还有很多可以优化的地方。
缺点之二是在特征码提取部分,由于我知识有限,不能很好的理解作者是怎样将“相关的内存”同“无关 的内存”很好的识别出来,并且跟网络流匹配出特征 码的,我觉得这样dump出的内存还是会很大,同时如果网络流进入程序后还有一个解码的过程的话,不就不会匹配出相同的一段数据了么?
另 外,我认为 Argos 的另一个缺点是检测的攻击种类太单一,尽管作者称其他种类的攻击“are beyond the scope of our work”,但是牺牲这么大的速度,却只能检测出堆、栈溢出以及格式化字符串攻击,确实是比较有局限性的,而且[/ref{bib:other}]表明将 动态污点分析运用在诸如SQL Injection、 XSS 跨站攻击等方面也不是不可能的。
TaintCheck 同样是基于虚拟机的,他们所基于的是另外一个开源X86 模拟器 Valgrind. Valgrind 会将机器指令翻译成自己内部的统一指令集UCode ,然后将 UCode 传递给 TaintCheck 由 TaintCheck 根据指令的类型执行一些与 Argos 原理类似的操作,或者报警。
TaintCheck 由四部分构成,分别是 TaintSeed、 TaintTracker、 TaintAssert 和 Exploit Analyzer。
TaintSeed 的负责将一切来自于不受信任的源的数据标记为“污染的”,每一个被污染的byte 都会有一个对应的指针指向存储该字节的污染信息的结构(若该字节没有被污染,则是空指针)。作者说他们利用了一种类似于页表的技术使得实际这些标记占用很 少的空间,我不知道他是怎样实现的。TaintSeed 会检查每一个系统调用确定哪些内存会因为这个系统调用而被“污染”,然后他为这些内存分派一块空间来记录系统调用号,当前栈的快照,以及被写入的数据等信 息(好浪费内存啊~~)。然后上文提到的那个指针会指向这个数据结构。同时,TaintCheck 也可以不这么详细地记录,而只是向Argos 那样记录改内存是否被污染。从其功能上看,我觉得可以把TaintSeed 看作是系统调用级的污点标记器。
TaintTracker 则是指令级的污点标记器,他可以被实现为将由指令产生的新污染内存区域的指针指向源污点内存对应的指针,也可以实现为指向一块新的Taint Structure 数据结构,记录了指令内容以及栈的快照等。但是显然后者将会相当耗内存!
TaintAssert 则负责检查各种危险的对于污染数据的操作,或者说是影响EIP 并将EIP 改为污染数据的指令,还有调用printf 族函数中参数出现/%n的操作。我觉得这里似乎需要某些源代码,否则怎样实现文章中所说的 warpper? 而且这里作者指出了 TaintCheck 的一个不足:若程序自己实现了类似printf 的函数,就没法写wrapper 了,但是我觉得这里对 printf 族的处理完全是多此一举的,因为格式化字符串攻击最终也是要用污染的数据覆盖 EIP 的(或者我记错了?)。
当 TaintAssert 产生一个警报时, Exploit Analyzer就会根据前面记录的 Taint Structure 和相关的污染内存等信息自动产生一个比较精确的特征码,提供给 IDS等。作者在这里只是笼统地提出了一个研究方向,并没有具体的实现方案
由于在原理上与 Argos 的一致性,TaintCheck 的主要优缺点与 Argos 是相同的。
但是,由于 Taint Check 利用了一个Taint Structure 数据结构记录污点的相关信息,在攻击重现和特征码提取方面,相信会有更高的准确度,但是与此对应的,Taint Structure 也带来了更高的内存消耗和时间代价。
如果像作者所希望的那样,将 TaintCheck 不仅仅用于蜜罐的话,TaintCheck 的效率缺点比 Argos 更加明显。
我通过网上数据库和 Google的论文搜索,找到了一篇关于优化 TaintCheck 的计划书[/ref{bib:optim}]但他并不详细,更进一步的资料我现在还没有发现。
同 时,我觉得 Dynamc Taint Analysis 应该还能将 SQL Injection 等攻击检测出来,于是我就到网上寻找相关的论文,最终找到了[/ref{bib:other}]。该文作者的思路是改变PHP 解析器、bash 等用于解析脚本语言的解释器的源代码,使其加入动态污点跟踪的功能,具体实现同样是建立一个内存污染标记的位图(不过没有用虚拟机),对于解释器源文件的 每一个赋值语句前后加入更新其标记的语句,同时为函数调用另外实现两个栈用来传递参数污染信息和返回值污染信息,对于外部函数,则是针对其函数的功能人工 编写一个warpper 用于修改函数返回值的污染信息。
这种实现方法目前来看是非常低效的,他还是要依赖源代码的开放,而且会产生很多误报,加入人工操作也会降低生成效率。而且这种方法并不能很好得识别各种隐式的函数赋值,且会误报一些借助输入跳转的语句。
对于以上三种实现的看法,实际上在其优缺点分析中已经叙述的很多了,在此不再赘言。下面我只是想谈谈我对于这方面的研究方向的一点看法。
就我目前对这个领域的浅薄理解,我觉得一下三个方面将会有许多可以探索的地方: