文/黄震
程序补丁是用于修复程序漏洞的通用方法。然而,用程序补丁来修复漏洞有一个经常被忽略的缺陷——漏洞尚未修复窗口(pre-patch window),即从一个漏洞被发现到其相应的补丁发布之间的时间窗。在漏洞尚未修复窗口,攻击者可以针对漏洞发起攻击。漏洞尚未修复窗口的存在是因为程序员手工分析漏洞、编制和测试程序补丁需要一定的时间。尽管有大量研究致力于程序补丁的自动生成技术,目前此技术仍未被实际使用。文章作者通过对131 个最近公开的程序漏洞和相应的补丁进行分析,发现漏洞尚未修复窗口平均长达52 天。而且由于分析漏洞和编制程序补丁有一定的复杂性,总体来说漏洞尚未修复窗口很难人工缩短。即使软件开发商可以对其已知但尚未修复的程序漏洞进行保密,来降低这些漏洞被恶意利用(exploit)的风险,呈出不穷的零日漏洞仍然可以被攻击者使用[1][2]。
为了解决使用程序补丁来修复漏洞的这一个缺陷,软件用户经常使用“程序漏洞临时解决方案”(configuration workaround)来暂时防止程序漏洞在修复前被恶意利用。程序漏洞临时解决方案通过直接让用户修改与漏洞代码相关的程序配置参数来禁止执行含有漏洞的代码。举例而言,具有严重危害的Android Stagefright漏洞在2015 年被发现,有超过十亿的智能移动设备受到其影响。虽然这个漏洞在当年4 月就被发现,相应的补丁直到8 月仍未发布。幸运的是,通过修改Android MMS 客户端的配置来禁止自动下载短信图片,用户可以防止此漏洞被恶意短信攻击。使用程序漏洞临时解决方案,用户可以牺牲少许的程序功能来换取对严重漏洞的保护。所以一些知名软件开发商如Microsoft 会在发布补丁前公开程序漏洞临时解决方案[3][4],如果相应的漏洞有对应的程序漏洞临时解决方案,以保护程序用户不受针对漏洞的攻击。然而程序配置参数是设计用于让用户灵活调整程序的功能行为,而非提供安全保护,所以绝大多数漏洞并无与之对应的程序漏洞临时解决方案。
受到程序漏洞临时解决方案的启发,作者提出快速漏洞屏蔽方案SWRR(Security Workarounds for Rapid Response)来缩小或消除漏洞尚未修复窗口[2]。SWRR 可以被自动生成为大部分漏洞提供临时解决方案。设计SWRR 的一个主要难题是怎样让SWRR 适用于还未发现的漏洞。因为事先并不知道漏洞在何处,SWRR 必须适用于任何潜在的漏洞。另外一个难题是漏洞可能和任何类型的程序功能相关 ,出现在程序代码里的任何一个位置。而每个SWRR 应该确保程序在应用这个SWRR 之后仍然能够继续运行。因此,作者在设计SWRR 时着重考虑简单性、通用性、和非介入性(unobtrusiveness),而不依赖于任何关于目标程序本身和漏洞的假设。其中非介入性是指不影响目标程序的主要功能。
让SWRR 通用性强和具有非介入性的关键点是利用目标程序自身的出错处理代码(error-handling code),因为出错处理代码的设计目的就是让程序在意外错误发生之后能恢复正常、继续运行。基于此关键点,作者设计并开发了Talos 解决方案[2]。Talos 使用静态程序分析来识别目标程序已有的出错处理代码,自动生成SWRR 并添加SWRR 到目标程序。一旦某个漏洞即将被利用,与此漏洞相应的SWRR 就改变程序执行流程去调用出错处理代码,来防止漏洞被利用,从而屏蔽这个漏洞。使用Talos,软件开发人员可以将SWRR 在软件开发阶段直接植入程序,或以补丁形式针对特定漏洞来发布。如图 1 所示,这种植入式的SWRR 可以让用户修改配置参数来动态启动或关闭用户指定的SWRR。
图1 植入式SWRR
Talos 使用创新的启发式规则(heuristics),通过静态程序分析来识别出错处理代码。这些启发式规则有以下四个。
第一,错误记录函数(error-logging function):当应用程序遭遇错误时,经常会调用错误记录函数来记录与错误相关的信息。通过识别程序对错误记录函数的调用(如图1 所示),Talos 可以发现出错处理代码。
第二, NULL 返回值:如果没能发现对出错记录函数的调用,Talos 会查看目标函数的返回值类型是否是指针类型。通常一个返回值为指针类型的函数会返回NULL来向其调用者指明有错误发生。
图2 出错处理代码调用出错记录函数
第三,错误传递:很多时候一个函数会使用其调用另一函数的返回值作为自己的返回值,这就相当于是把错误沿着调用链(call chain)向上传递。这种出错传递有三种方式:直接使用、转换后上传、和转换后下传。Talos 可以识别所有这三种错误传递方式。
第四,间接保护:如果一个函数的所有调用者都可以被SWRR 保护,Talos 可以通过植入在这些调用者里的SWRR 来保护这个函数。
Talos 是基于LLVM[5]开发的,可以自动识别用C/C++编写的目标程序中的出错处理代码,生成SWRR,以及将SWRR 植入目标程序。作者团队评估了SWRR 的安全性、有效覆盖率、和性能开销。
第一,为了评估SWRR 的安全性,作者测试了SWRR 是否能成功保护五个流行应用软件中的11 个实际的程序漏洞。这五个应用软件包括web server,web cache/proxy,ftp server,和database engine,平均含有95176 行程序源代码和1349 个函数。通过详细分析这11 个漏洞和Talos 为它们生成和植入的SWRR,作者发现这些SWRR 能够成功屏蔽这些漏洞,而且绝大多数SWRR 具有非介入性。
第二,为了评估SWRR 的有效覆盖率,作者测试Talos 能在这5 个应用软件中的多少个函数里识别出错处理代码,和可以用SWRR 来保护其中多少个函数。结果显示平均而言Talos 生成的SWRR 能为75%的函数提供保护。而在这些SWRR 中,71%具有非介入性。总体而言,SWRR 能够比现有的程序漏洞临时解决方案提供多达两倍的有效覆盖率。
第三,作者测试了植入的SWRR 带来的性能开销。平均而言,SWRR 增加了1.3%的运行开销。
在此项目中,作者提出了SWRR 的概念来缩短或消除漏洞尚未修复窗口 ,并设计实现了Talos,一个可以自动生成和植入SWRR到应用软件源代码的工具。作者已经开源了Talos的代码[6]。Talos 使用新颖的启发式规则,通过静态程序分析来识别目标程序中已有的错误处理代码,以用于生成SWRR。作者将Talos 应用于5 个流行应用软件,结果发现SWRR 能够屏蔽75%的潜在程序漏洞。这表明SWRR 的有效覆盖率是用户常用的程序漏洞临时解决方案的两倍。通过用这五个软件中的11 个实际程序漏洞来测试SWRR,作者发现所有这些漏洞都能被SWRR 成功屏蔽,其中8 个SWRR 不影响除与漏洞直接相关的代码外的软件全部功能或主要功能。