NSC2015文伟平:软件安全漏洞挖掘技术探讨

文伟平:谢谢主持人,非常高兴,也有这样的机会来参加第三届网络安全大会,今天我看到这个场景非常感动,这么多参会者还坐在地下,外围也那么多参会者站着听着我们的演讲。我们北大团队也参加了第一次的网络安全大会,也派了一个代表,所以我这里有一些内容可能曾经在第一届网络安全大会和第一届互联网网络安全大会讲过,如果有一些参会者听过的话,我可能会说的快一点。

今天和大家探讨的主题叫”软件安全漏洞挖掘技术”。软件安全漏洞挖掘,国内还有叫漏洞分析,漏洞分析和漏洞挖掘业界也是有区分的,有些就是把漏洞挖掘归在漏洞分析,我们这个意义上的漏洞挖掘是指对位置漏洞的挖掘。

当前的技术发展,大家也知道,漏洞挖掘本身是很难的问题。大家发现Windows7发现很多远程管理的漏洞,但Windows7发布之后,大家发现远程管理漏洞,尤其系统级的变得比较少了,实际这和微软SDL开发模式有一定的关系,他们有安全的开发平台。Windows7操作系统里实际它也引入了很多安全机制,上午TK在”内存20年”里也介绍到了一些安全机制,但没有详细展开,StackCookie他可能列了1997年的几篇论文,也就是通常所说的GS,还有SafeSEH,DEP是数据执行保护,和地址空间的随机化,也就是说这些安全机制的涌入,使得我们能找到安全隐患的点或者Bug,如果Bug不能够被利用其实还不能算是漏洞,即使我们确定了可能是个安全隐患,但它却不能够被利用,使得我们在挖掘一些安全漏洞也变得越来越难。

实际漏洞挖掘技术也是在发展的,既然早期我们主要是通过ST、CTOI或者GES、SGANF(音)这样一些漏洞的函数,构建函数模型来进行漏洞挖掘。在后期我们会引入路径模型的概念,包括原代码审计系统系统,二进制代码反馈之后也是一种源码,也就是说我们可以根据代码直接的上下文关系,语义解析构建出一些路径模型来挖掘漏洞。系统的安全对抗也是发展的,既然有了新的技术和安全机制同样也会推出对抗的技术,例如数据执行保护制,可以通过ROP构造出来各种各样的Gaget,调用系统里相关的函数,因为攻击者攻击对方之后实际更多是被攻击机器上的资源完成他攻击的过程。

针对地址空间的随机化,我们知道对喷(音)是最典型的,还有AC3里一些针对漏洞的GsBre(音),还有TK之前提出来的像UZDate数据解构,他们是能构造出来通用Shellcode的一些方法,能够提高漏洞被利用的成功概率。

以Windows系统为例介绍一下它的内存保护机制,当传统内存机制,GS、SafeHEP、SafeSEH、DEP和SLF,Windows8里也引入了新的能够检测到ROP的方法。对堆进行了更多的改进,比如随机化的LFH,对页面属性的保护,对其他内存方面比如虚指针也提供了相应的安全机制,对于空页面、内存分配的随机化,(PPT)大家看右边这个图,GS方面它也进行了积极的增强,也就是StackCookie,Windows7和Windows8最大的一个区别,就是在Cookie生成算法里采用了Intel的SQL-key这样的算法和技术,使得Cookie更难以进行猜测。在SL方面,他又提出了高算地址的随机化,我想研究过Win8或调试过Win8的系统安全人员应该都很清楚它的机制。DEP里它也提出检测ROP的机制。

软件、漏洞挖掘技术,三个标红的可能是我们在一些大会上面跟大家交流过的,漏洞挖掘技术只是一些思路,并没有很深的技术,大家一听可能就很明白。第一种技术应该是很多厂商广泛使用的一种漏洞挖掘技术,基于Crash信息的,这里的Crash信息可能是基于单机的,实际还有很多网络上的Crash信息是可以用的。如果微软Crash信息收集服务器没有采用加密形式进行传递,这些信息对我们是非常有用的,所以对于微软的Windows系统我们更多的是采用本地的,对于应用软件,就是对网络上传输的Crash信息是很有分析价值的。

以微软Windows为例,Windows出现蓝屏事件时会有系统转存,转存模式有三种:一是完全的淡化,把当前物理内存里的所有信息都存下来;二是核心内容的存储,除了进程之外的信息我们把它Download下来,三是小内存的存储,以16位为单位进行转存。得到这些信息之后,就可以通过微软自身提供的Win DBG进行分析,每一个出错的信息都可以形成陷阱针,我们这个陷阱针我们可以通过bing看到它当时出错的场景,比如堆栈是什么情况,在一条什么指令上出错,可能大家从这条指令就能够看出,它在读一些不应该读的地址,就是读出一个错误。

回溯之后,我们可以把整个函数调用顺序清晰地展示出来。最后我们能看到,可能这个函数是wingzk!SfnINSTRING出现的问题,下一步我们可以借助强大的反馈链平台,像IDA来进一步判断它是不是存在逻辑的缺陷,判断它这个漏洞有没有可能被进行利用。

我们判断的一个依据,看看用户态里的参数或数据能不能够传递到内核态里,所以,我们一步步进行回溯,最后发现从用户态空间传递过来的参数没有做任何判断,而”esi+8″这个地址可以被我们指定,这种情况下面就意味着我们能够发现内核里读的错误,这也是我们MS11-054发现的过程,也是我们团队发现的一个漏洞,这个漏洞也已经提交给微软了。

第二种方法也比较简单,基于这样的假设,也就是假设我们给厂商给自己产品打补丁时,打的补丁存在一些安全隐患,或者打的不完备,因为软件厂商发现发现自己的产品出来漏洞之后一定希望用最小的代价去修复当前这个漏洞,所以,它一般比较注重当前漏洞点的修补,而很少考虑漏洞上下文的环境,尤其Windows这种大型逻辑上它是很严谨的,但对很多的逻辑条件在别的地方可能也存在相应的改变,也就是它不能考虑到整个系统或者第三方代码会给我们变量和逻辑条件带来影响,这样我们可以形成了参考,安全补丁比对逻辑挖掘的思路。这种技术我们在清华大学学报上面已经形成论文,大家可以去下载。其实思路是很简单的,也就是说我们可以通过路径查找能够找出来所有可能能够执行的路径。

进一步对这个逻辑条件进行相应判断或者进行约束,最后进行求解,能不能够找到绕过这个逻辑条件去执行到漏洞点的路径,如果能找到的话,就意味者这个漏洞修补可能是不完全的。

这里有个典型的漏洞,这个漏洞应该说是已经公开的。漏洞之前和之后实际在前面就是加了一个逻辑条件和框段,同时Windows系统和其他开发者在其他地方又有这个地方进行了修改,如果我们采用某一种方法,能够把它的条件先进行重置,这种情况我们就可以把以前的漏洞进行重现。

举例,MS10-010,是我们团队成员在分析MS10-011这个漏洞和补丁分析出来的一个漏洞,当时这个漏洞的D点和P点实际是在不同的函数里,也就是说实际增加了运行到漏洞点的逻辑条件,但这个逻辑条件它是可以通过普通用户构筑一些特定函数后来进行修改的,既然我们自己也有编程的基础,代码方面的技术,自己可以通过编程直接改变这个条件,从而触发原来的一个漏洞,所以这很巧一个是MS10-011,一个是MS10-010,实际他们是一样的漏洞,是属于客户端子系统的漏洞。

第三种方法,系统内核函数的无序调用,这里面不仅仅是内核函数,也可以API函数,Win7、Win8出现之后,系统漏洞的确很难继续挖掘,我们需要改变一些新的思路进行漏洞挖掘,这种方法实际是很好的能够去挖掘本地学习提升漏洞的方法,它可以面向Windows的内核模块,也可以面向一些驱动程序进行漏洞挖掘。

它的思路很简单,一个普通用户能够驱动打印机,就意味着一个普通用户能够调用打印机驱动里面的函数,既然普通用户能够驱动我们驱动里的函数,完全可以通过自己的代码编程把这些函数剥离出来,剥离出来的目的是什么呢?就是为了进行无序的调用,所以,我们可以有正常的顺序序列,也可以有打乱顺序的调用,譬如顺序里的函数经过多次调用之后看看在内存里是不是会打架。

也可以对调用的顺序看能不能改变,或者调用一些条件或参数,或者没有公开过的API函数。这也是MS10-010的特点,它是客户端子系统的漏洞,在微软里它应该属于重要级的漏洞。MS10-010,也就是客户端子系统主要是完成这样的功能,它可以让用户登录进来之后开启的每一个进程可以在SDRSD里留下一个PLD,就是进程列表。我们有个方法可以让用户退出操作系统,他曾经开启过的进程依然留在这个系统里,这应该是存在安全隐患的,这和操作系统设计原则是违背的。

这种方式我们怎么样让它重现呢?就是打乱它的调用数据,这里列出的一段代码,我们可以改变它的逻辑条件,绕过它的安全补丁。我们怎么做呢?这里面每一步都可以通过安全函数实现。正常情况下会先连接ApiPort–退出–关闭,我们的标志就结束了。如果打乱这个顺序的调用,先连接,接着关闭,最后调用退出函数,这样它就会寄存器里变成0,进程却依然留在系统里。

基于协议握手的漏洞管理技术,这也没有什么太多的新意,主要是针对一些网络软件,这个测试空间是很大的。尤其是针对网络的应用软件,原来我们在进行Fuzzing测试时比较关注的是它的第一步,现在我们可以关注到它的第二步或第三步,我们首先可以把它整个网络通信流程模拟出来,再结合我们的立项工程,立项主要用在C端(Server端),也就是说我们去解析传递过去的数据包最后在Client端或Server端是怎么解析的,我们能够去定制更加自动化的Fuzzing测试用例,从而缩小我们测试用例的空间。

这是个典型的例子,我们往往关注的是第一步,实际往往很我多漏洞会出现在第二步或第三步,在连接创建之后或者用户已经登陆进去之后,后续数据包的传递可能会导致出来一些安全漏洞,最近我们团队也在研究URL的漏洞,主要是针对浏览器和Flash的一些对象,比如SWF。一提到UAF可能大家都知道,就是User After Free。(PPT)这张图上应该展现的就是比较通用的漏洞模型,也就是说我们在解析HTML时,HTML里可以通过Create Edident去创建我们的对象,同时通过JS完成事件的回调,事件回调中可以销毁对象或者对对象属性进行修改,修改完了之后我们后面还会有继续的引用,这样就会导致UAF的漏洞。

业界也有很多Fuzz的工具,比如Grade(音)也是用开源的工具,这些工具有相应的特点,比如针对节点的,针对JS的,还有跨浏览器的,比如CloseFuzz针对IE、Chrome或者火狐。一般UAF漏洞,第一步,既然我们做Fuzz工具应该怎么做?首先是创建我们的对象,第二步是修改创建对象的属性或者进行释放,最后在别的地方再进行引用,同时我们需要有一个能够监控到浏览器运行状态的工具,进行Fuzzing时完全可以打开创建浏览器为子线程调试的参数,这样我们可以捕获到当前浏览器运行的一些异常信息,最后来进行判断。

如果我们要实现一个浏览器的Fuzzing工具有很多资料可以进行参考,典型的是有W3C的官方文档,文档里面给我们提供了大量关键字,可以构造出我们的字节,告诉我们dom树的一些结构。

W3C也提供了针对浏览器基本功能的测试用例,它其实有很多测试样本,这些测试样本对于我们写浏览器Fuzzing测试工具是很有帮助的,我们可以对它进行修改和定制。还有一些方法,例如可以利用跨引擎,大家知道,IE浏览器发展到今天有很多的版本,在IE8之前的都是通过JScript.dll来进行解析的,IE9或IE9以上的版本都是通过JS和Jscript9.dll版本解析的。

这样我们可以用早期JScript.dll来构建我们的dom树,用新版本来进行解析,也就是说我们利用他们构建方式和解析形式上的差异,看能不能够发现相应的一些漏洞。为dom树节点添加事件的回调。还有一种方法,我们也可以充分地利用一个对象,这个对象也是dom树里可能会有的一个对象,像elementRange,这个对象可以存储dom树当前需要Fuzz的节点和应用,在别的地方处理完之后最后再通过对象查询它引用变化的情况,对于回调函数的构造可能有一定的技巧,需要我们能够有效地打乱dom的关系树,并且它构造的方法可能会直接影响到我们Fuzzing工具的一些效率。

最后的方法是针对Flash对象UAF的挖掘,SWF工具里S3为了提高SWF处理效率,有个新的线程跟踪机制,存在主线程和工作线程。这里有共享内存的特性,如果主线程和工作线程根据共享对象的处理出现一些不同步的现象就有可能出现UAF的漏洞,最近有一些案例,1月份出现2015的0311和0333,这两个都是因为ByteArray这个对象的一些问题,也就是我们把ByteArray设成并享之后出现的问题。

第一个漏洞,如果说An Cooperis(音)失败的情况下有可能会造成主线程里对它错误的引用;第二个是工作线程里直接对ByteArray进行clean清楚时没有告诉主线程,这时候我们dom memory可能导致一些引用,导致UAF漏洞,这是针对ByteArray对象的根本原因。

m-buffer是指向实际的对象,m-subscriber是个引用的列表,也就是说我们把ByteArray设成共享时再进行复制,它复制了m-buffer的相关内容,忽略了对象引用的信息,当我们对这个新对象进行操作时,就会使得老对象还不知道一些信息,从而在后续引用时造成UAF。

对于Flash对象,我们一般认为,它支持三种线程之间的共享方式,只有最后一种它是以共享内存的形式进行同步的,也就是说第一种和第二种都是直接进行复制,但不引用,只有第三种他们的内存可以进行共享和操作的。通过S3脚本设置共享对象,并对共享对象进行处理,把它取出来使用。这是个操作的过程,也就是说我们把它取出来之后,可以写入自己的Fuzz程序,Fuzz程序可以对当前这个对象进行Clear,也可以复制成null,也可以把当前属性修改成位我们想要修改的属性,后续我们再进行进一步引入来看看它是不是出错,从而发现UAF的漏洞。

对于回调事件的设置也是很灵活的,也就是说我们可以对这个对象进行很多处理,进行相应组合,最后发现一些漏洞。

今天这个机会很难得,后面还有点时间,我再把北大的小组也简单介绍一下,我们这个团队在2008年时就一直存在,主要在做软件安全方面的研究,我们主要有一个方向:立项工程,漏洞分析,漏洞挖掘,恶意代码和安全评估。(PPT)这是我们在漏洞方面发现的成果,相比前面大牛们所做的工作,我们做的工作是很少的,我们给微软提供的是3个漏洞,没有公开的有3个漏洞。

对应用软件上发现的漏洞是本地学习提升的漏洞,主要是驱动层面发现的漏洞。

今天的演讲就到这里!谢谢大家!

上一篇:NSC2015马坤:基于众筹插件模式的分布式安全扫描平台

下一篇:NSC2015公安部李明:云环境下的信息系统安全等级保护要求