刘华玉,甘水滔,尹小康,柳晓龙,刘胜利,李宏亮
(1.战略支援部队信息工程大学 网络空间安全学院,郑州 450001;2.数学工程与先进计算国家重点实验室,江苏 无锡 214215;3.清华大学 网络研究院,北京 100084;4.江南计算技术研究所,江苏 无锡 214083)
随着互联网的发展,网络安全问题近年来备受关注。网络协议作为互联网通信的基础,其安全性一直是学术界面临的重大难题。协议在设计和实现时可能存在安全问题,网络通信的易达性和传播性会严重威胁用户的隐私安全以及整个互联网络的安全。
2017 年5 月爆发的WannaCry 勒索软件攻击,利用“永恒之蓝”漏洞广泛传播,短时间内超过150 个国家和30 万用户遭到攻击,给全球大量计算机用户造成数十亿美元的经济损失[1]。2020 年,360CERT监测发现,国外研究团队发布的DNS 协议实现的逻辑错误,攻击者通过发起指向恶意name-server 的DNS 查询请求,可以造成递归服务器和特定域名服务器拒绝服务影响[2]。这些协议级漏洞利用非法用户数据被服务器解析后,服务器产生非预期的执行行为,最终造成拒绝服务攻击或者远程执行攻击等。因此,针对网络协议实现的漏洞挖掘技术研究是当前的主流方向之一。
1990 年,MILLER 等[3]提出模糊测试概念,主要用来检测目标程序的鲁棒性。目前模糊测试技术[4-6]已经成为一种重要的程序脆弱性分析方法,在网络协议安全研究中具有自动化程度高、效率高等优势。现有的针对网络协议的模糊测试技术主要有以Peach[7]为主的 黑盒测 试方法和以AFLNet[8]为 主的灰盒测试方法。Peach 等黑盒测试需要协议的格式信息,依赖于人工知识储备,人工成本高,可扩展性较差。AFLNet 是首次提出针对协议的灰盒测试方法,仅需要少量协议信息即可指导完成测试,但在测试过程中忽略了协议本身的格式信息,变异策略随机性强,导致测试效率低等问题。
为解决当前研究中存在的问题,本文提出一种基于协议逆向工程自动识别协议格式来辅助模糊测试的方法,主要包括:基于对齐聚类的智能化协议格式推断模型和协议格式信息辅助的变异策略优化。该方法在对TinyDTLS、OpenSSL 等程序的模糊测试中不仅能够提高目标程序的边覆盖率,而且在最新版本TinyDTLS 程序中触发一个缓冲区溢出类型崩溃,该崩溃在AFLNet 中无法触发。
近年来,针对网络协议的模糊测试得到了广泛的研究。现有的协议模糊测试研究方法主要分为基于测试用例生成和基于种子变异两类[9]。
目前基于测试用例生成的模糊测试主要以黑盒测试[10-11]为主,针对网络协议的测试,代表工具有Protos[12]以 及Peach。2001 年,KAKSONEN 等[12]提出了Protos,实现了针对网络协议的模糊测试,这也标志着模糊测试技术成为程序脆弱性分析的实用性工具。Protos 为不同的网络协议提供了不同的测试用例集,通过故障注入的方式测试协议软件安全性,测试人员通过指定协议格式中的字段来生成测试用例。Peach 刚开始主要被用于文件的模糊测试中,经过多次升级改进,目前适用于测试网络协议等多种类型程序,而且至今仍然在被使用。
基于种子变异的协议模糊测试方法以灰盒测试为主。近年来,随着AFL[13]的提出,通过程序轻量级插桩实现基于覆盖率引导的灰盒测试方法成为主流模糊测试方式。AFLNet 是针对状态网络协议的灰盒测试工具,AFLNet 以覆盖率为导向,采用插桩的方法监控程序边路径覆盖情况,引导种子变异以探索更多的路径。AFLNet 仅需要少量的协议知识用于提取请求消息和构建协议状态机,自主性高,并且易于扩展,是目前成熟且应用最为广泛的协议模糊工具。此后,针对网络协议的灰盒测试技术得到发展,研究人员提出并实现了许多工具。StateAFL[14]是一种将被测程序内存状态映射为唯一的状态标识符,从而推断目标服务器的当前协议状态,无须手动为协议定制。SPFuzz[15]通过预定义协议规范等来增强测试用例的有效性。SNPSFuzzer[16]提出一种基于快照的测试方法,通过在特定位置存储程序的上下文信息,并在需要模糊相关状态时恢复上下文信息,提升测试速度。这些工具通过协议状态推断、协议分析、种子功率调度[17]等多方面的改进,在实际应用过程中取得一定进展。
协议逆向技术是在没有或少量协议规范知识的条件下,通过对协议实体的网络输入输出和指令执行流程等进行跟踪分析,提取网络协议基本描述和状态机等信息[18-20]。根据现有的研究方法,协议逆向技术主要分为基于Network Trace 和基于Tainted Data 两类。
基于Network Trace 的协议逆向技术,即基于报文的协议逆向技术,依赖于wireshark 等抓包工具捕获协议流量进行逆向分析的技术。该技术针对协议字段的取值变化和特征推断协议的信息,典型的代表是由文献[21]在2004 年启动并发布的PI(Protocol Informatics)项目。该项目引入了多序列对比算法[22],并根据相同类型报文聚类的统计特征对报文格式进行分析。Discover[23]使用报文序列分析方法实现完整的协议格式提取,该技术在推断消息格式时模拟报文解析的过程,能够有效识别依赖于消息本身的字段。
基于Tainted Data 的协议逆向技术,也即基于指令序列的协议逆向技术,主要通过动态污点分析技术跟踪分析服务器程序对报文的解析流程,根据程序对报文的解析来推断协议格式。Polyglot[24]提出利用该技术自动解析协议格式,Polyglot 采用shadowing 方法监视程序对应用数据的处理过程,根据数据处理过程中的二进制信息来提取有关字段边界和关键字的信息。AutoFormat[25]提出基于指令轨迹的协议识别技术,该技术可分为上下文感知的执行监控模块和协议字段识别模块。
基于Tainted Data 的协议逆向技术利用协议实现的详细数据处理流程,可以在逆向工程中实现较高的精度,但存在固件获取、二进制程序混淆等困难。基于Network Trace 的协议逆向技术只需考虑通信过程的数据包,不需要程序源码,具有良好的逆向效果。
针对日益复杂的网络协议,模糊测试技术是目前最为实用的安全分析技术之一,但现有的研究方案依然存在以下问题:
1)人工成本高。现有的网络协议黑盒测试工具需要大量的协议知识辅助测试,依赖人工构建协议模型和数据模型。实验时要求测试人员需要精确掌握协议规范,还要熟悉工具的使用,以此才能够构建耦合效果良好的模型,这不仅给测试人员增加了大量的工作,而且协议测试的效果完全取决于模型的好坏,这使得工具的可扩展性变差。
2)变异策略缺乏协议格式信息指导。代码块1为TinyDTLS 程序中处理收到消息的部分源码。
研究发现,程序在这部分会将收到消息的实际长度msglen 与消息内的长度字段length 进行比较(第15 行)。如果变异得到的测试用例无法通过此校验,将无法探索更深的路径。然而现有的状态协议灰盒测试方法大多是作为AFL 的扩展,直接采用AFL 中的随机变异策略,忽略了协议格式信息,很多测试用例无法通过服务器的初步数据校验(第8、9、15 行),造成大量的无效测试用例,降低了测试的性能。
为解决上述问题,本文提出一种基于对齐聚类的智能化协议格式推断模型ProCluster,用于指导灰盒测试中协议状态机构建和种子的变异。该模型通过自动提取协议关键字和推断相应类型,来辅助种子变异策略生成更符合协议规范的测试用例,以此加速提升模糊测试的代码覆盖能力和脆弱路径发现能力。
为了解决面向通信协议的模糊测试中代码覆盖率低、变异策略缺乏协议信息指导等问题,本文将协议逆向技术应用到灰盒测试中,提出一种基于对齐聚类的智能化协议灰盒测试方法ProCluster。考虑到协议逆向的成本,利用基于Network Trace 的协议逆向技术获取协议格式,然后根据设计的变异策略在协议格式信息的指导下对种子进行变异,得到更多符合预期的测试用例用于对网络协议的测试。
本文所提灰盒测试方法的框架如图1 所示,主要包括协议识别、状态机构造以及种子变异3 个模块。ProCluster 首先利用协议识别模块将前期通过wireshark 等工具抓取的数据包进行自动化解析,然后将得到的协议格式等结果作为模糊测试引擎的输入,来辅助后续模糊测试工作。
图1 基于协议格式智能推断的灰盒测试技术框架Fig.1 Framework of gray-box test technology based on intelligent inference of protocol format
协议识别、状态机构造以及种子变异3 个模块的介绍如下:
1)协议识别模块
该模块基于Network Trace 的协议逆向技术实现。该模块以原始网络数据包为输入,经过预处理得到消息序列作为初始种子,进一步推断解析得到协议关键字和协议格式,用于后续构造状态机和种子变异。
2)状态机构造模块
该模块主要利用前一阶段中识别的关键字作为响应消息的状态识别码,以此在测试过程中自动化构建状态转移图,更进一步指导变异过程。与AFLNet 相比,本文提出的方法省略了人工提取协议状态的过程,无须手动为协议定制,使得测试更为自动化。
3)种子变异模块
针对变异策略缺乏协议格式信息指导问题,该模块以AFLNet 为原型,在原有变异规则基础上设计消息序列变异策略,并根据协议格式信息实现针对协议字段的变异策略,使模糊工具能够更加高效地发现一些常规的安全漏洞。
本文采用基于Network Trace 的协议识别技术。分析此类技术现有的研究方法又可以分为基于对齐聚类方法和基于token 的方法。基于对齐聚类方法,现有的研究主要通过利用各种对齐算法对齐消息并计算消息的相似性分数,得到的结果与阈值进行比较,依据此进行聚类并分析。但是预设置的阈值对协议本身敏感,因此这些方法鲁棒性低。基于token的研究方法将消息拆分为token,然后根据token 值对消息进行分组。这类方法依赖于预定义的token分隔符,而且将字段限制在token 级别,在分析过程中很容易造成冗余,降低正确率。
本文考虑在对齐消息的基础上,利用概率分析的方法提取状态协议中的消息状态,即消息中的关键字,然后依据关键字完成消息聚类,最后对簇内的消息进行格式推断解析。协议格式识别算法如下:
算法1 协议格式识别算法
1)多序列比对算法
现有研究中针对协议序列比对大多使用双序列比对算法[26-27],而考虑到报文序列较多情况下会影响整体的性能,本文使用多序列比对算法对报文序列进行对齐操作。文献[28]针对三类多序列比对算法在报文序列上的性能和效果做出了分析和测试,本文在此基础上考虑使用基于迭代细化策略的渐进比对算法。该方法首先对齐最相似的报文序列,然后将其他序列逐步添加到对齐结果中,随后迭代地重新排列初始全局比对结果的序列子集,以提高精度。
2)生成候选关键字
本节考虑为了使生成的候选关键字尽最大可能包含协议的真实关键字,需要保守地构造候选关键字列表。
利用上文多序列对齐的结果,将报文分割为字段。针对文本类协议主要依据“空格”等分隔符进行字段划分,针对二进制协议,以字节为单位进行字段划分。根据前期初步对齐结果分析所有字段在所有消息中值的变化。对此,可以区分出静态字段(该字段在所有消息中不发生变化,标记为“S”)和动态字段(标记为“D”),对于连续的静态字段可以合并成更大的静态字段。为了构建保守的候选关键字列表,本文不仅将单位字节的动态字段视为候选关键字,而且在单位字节的基础上考虑将连续的动态字段整体作为一个候选关键字。考虑实际的协议设计以及避免造成后续计算浪费,本文设置候选关键字长度的阈值为3。至此,生成了一个保守的候选关键字列表。
3)概率分析
本节对候选关键字进行概率分析,筛选出最为可能的协议关键字。本文主要基于两个直观的观察来对候选关键字进行概率分析,分别是根据候选关键字聚类后簇的相似度得分以及簇规模。
根据前期的对齐结果,计算两两报文序列的相似度得分,并以此构造报文序列的相似度得分矩阵。然后利用相似度矩阵计算聚类后簇内的相似度得分和簇间的相似度得分。直观地认为簇内的相似度得分应该总是大于簇间的相似度得分,然而情况并不理想,两类得分会出现重叠的现象。
现有研究中大多直接根据相似度得分与阈值之间的大小关系来进行判断,但在实际应用中阈值对协议依赖性强,固定的阈值在协议逆向分析中很难得到置信的结果。基于此,本文提出假设:基于阈值的聚类结果中存在同类型消息被聚类到不同的簇中,不同类型的消息被聚类到同一簇中,即错误不匹配(False Non-Match,FNM)和错误匹配(False Match,FM)。根据针对多序列比对的研究[29],本文设置取值从0~1 的阈值t,通过错误匹配率曲线和错误不匹配率曲线得到等错误率(Equal Error Rate,EER)值,该值描述聚类结果总体准确性。分别给出错误匹配率和错误不匹配率的计算公式,如式(1)、式(2)所示:
其中:RFM表示错误匹配率;RFNM表示错误不匹配率;Sinter_i为第i个簇间相似度分数;N为簇间相似度分数的个数,i∊[1,N];Sinner_j为第j个簇内相似度分数;M为簇内相似度分数的个数,j∊[1,M]。
最后根据式(3)得到一个关于当前候选关键字基于相似度分数的置信度值p:
其中:E为等错误率值。
本文在研究过程中发现,对于基于相似度分数的概率,存在一个突出问题。当存在某个候选关键字聚类后有过多的簇时,此时该候选关键字的置信度会比较高,对后续格式分析干扰严重。因此,对该值设置一个基于簇规模的权重。
针对簇规模,本文分析真正的关键字聚类后的结果,得出以下结论:针对非刻意捕获的报文序列,生成的簇数量适中,且很少存在单个消息为独立簇。根据上述结论,给出候选关键字基于簇规模的权重配置w:
其中:Nsingle-message表示仅有一条消息的簇数量;Ncluster表示簇总数。
最后本文利用因子图计算得到候选关键字的后验概率,概率最大的即为协议关键字。假设多序列对比没有正确对齐,因此无法正确识别关键字。尽管如此,根据前面分析得到的关键字进行聚类,簇内消息的结构差异可能会减少。因此,对于每个簇,执行多序列对比和概率关键字分析。随后将新的关键字与原始关键字进行比较,如果新的关键字可以在所有消息获得更好的区分效果,则用新的关键字替换原始关键字。重复该过程,直到无法识别更好的关键字。
4)格式推断
对于最终的聚类结果,每个簇中均为同一类型消息。最后本文利用启发式方法对消息中的长度、cookie 等字段进行恢复。对于长度字段(标记为“L”),本文主要基于消息长度的差值与同一类型消息中对齐字段的差值进行比较确定。针对cookie 字段(标记为“C”),使用RolePlayer[30]中的启发式方法来识别。
利用wireshark 抓取的DTLS(Datagram Transport Layer Security)协议数据报文对本文提出的模型进行测试。如图2 所示,本文提出的模型能够区分协议数据中的静态字段和动态字段,准确识别协议数据的关键字,并在后续推断分析时能够识别到DTLS协议中的长度字段。本文的目标是利用协议逆向技术来辅助模糊测试,不进行精准的协议格式解析工作,因此图2 展示的结果中大多只分析静态字段或动态字段,不进行深入解析。
图2 协议识别结果Fig.2 The results of protocol identification
本模块在AFLNet 原有的变异策略基础上分析其中的不足之处,并提出新的优化变异策略。AFLNet 现有的变异策略包括确定性变异和随机变异阶段。确定性变异阶段主要包括位和字节的变异,这类针对数据报文的变异策略在实际中很难有出色的表现,而且会耗费大量的时间。因此,大多在实际操作过程中禁用确定性变异阶段,直接进入随机变异阶段。在运行过程中,随机变异阶段通常占有较大比例,而且该阶段也通常会覆盖更多的新路径。但是随机变异阶段对种子的变异盲目随机,这会产生大量无效的种子,从而造成模糊效率低下的问题。本文提出消息序列变异策略,并针对原有随机变异策略进行优化。
首先本文设计并实现消息序列变异策略。为了能够变异得到高度切合程序处理的消息,将当前选择的消息完整地插入该消息序列的任意位置,保持了消息的完整结构,也期待能够获得更多的状态转换。
然后优化随机变异阶段对种子的变异。结合前面对网络报文的分析,得到在报文序列中存在一些静态字段,而且可以直观地感受到编程人员在处理这些字段时并不会花费过多的篇幅。也就是在模糊测试过程中变异这类字段,很难会覆盖到新的执行路径,那么在随机变异阶段再对这类字段进行变异,往往没有收获,还降低了模糊测试的效率。因此,为了降低随机变异阶段种子变异的盲目性,本文提出大幅减小甚至不去对种子的静态字段进行变异操作,降低了目标程序的执行次数。
另外,前期协议逆向中除了区分静态字段和动态字段外,也分析得到了长度字段。针对长度字段,本文提出的变异方法是利用一些特殊值替换该字段的原有值,以此来测试该字段能否触发程序崩溃,如表1 所示。
表1 字段变异策略Table 1 Field mutation strategy
对于报文中的其他字段,本文仍以AFLNet 中的随机变异机制为主要变异方法。为降低变异过程中的随机性,本文针对前面提到的静态字段和长度字段进行调整,恢复静态字段的原有值,并将长度字段赋值为变异后报文的长度。
本文的实验是在一台Ubuntu 18.04 系统上完成的。实验中提出两个指标来评价模糊测试工具的性能:一个是触发程序崩溃的数量;另一个是路径覆盖,即在测试过程中被测程序覆盖的路径数。本文针对DTLS 协议和TLS(Transport Layer Security)协议进行了测试评估:在Eclipse 的TinyDTLS 公开库上测试DTLS 协议;在OpenSSL 公开库上测试TLS协议。为了保证实验的公平性,本文选取相同的输入在同一台虚拟机中完成实验。
本文以AFLNet 作为基准程序,利用边覆盖对所提方法进行评估。利用AFLNet 和本文设计的工具ProCluster 分别对TinyDTLS 和OpenSSL 公开库进行24 h 测试,并分析了测试结果。表2 所示为最终实验结果。
表2 在TinyDTLS 和OpenSSL 上的测试结果Table 2 Test results on TinyDTLS and OpenSSL
由于这两类协议均为加密传输协议,测试主要集中在协议握手阶段,很难覆盖到后续的状态转换,因此覆盖率都保持相对较低的水平。可以看到,本文所提方法ProCluster 在两个公开库的测试中均表现优于AFLNet。结果表明,本文所提的方法具有更好地发现更多路径的能力。
本文进一步比较分析了边覆盖数在两个公开库中的实时性能,如图3 所示。
图3 边覆盖数的实时性能比较Fig.3 Real-time performance comparison of the number of edge coverages
AFLNet 在随机变异阶段随机性强,而且变异得到的消息很难保持消息内的依赖关系,这样会产生大量无效测试用例,最终导致性能不佳。根据观察,本文方法ProCluster 能够发现更多的状态转换和路径覆盖,主要依赖于设计的基于消息序列的变异策略,以及在随机变异得到新的消息时会尝试恢复静态字段、长度字段等消息内依赖关系,使得消息更可能符合规范。ProCluster 能够解决代码块1 中所处理的长度字段等消息内依赖关系,变异得到测试用例后,恢复消息内的静态字段(代码块1 中第8 行msg[1]的数据和9 行msg[2]的数据),并根据消息实际长度对消息内长度字段length(代码块1 中第12 行的数据)赋值,使测试用例可以通过目标程序对这些特殊字段的校验(代码块1 第8、9、15 行),更进一步去深入探索被测程序的功能路径。而AFLNet 利用随机变异策略得到的测试用例,几乎无法通过相应的数据校验(代码块1 第8、9、15 行),不能更深入地探索程序路径。因此,本文的方法在实验时能够获得更高的程序覆盖率。
DTLS 即数据包传输层协议。该协议在TLS 协议基础上进行扩展,使之能够支持UDP 协议。TinyDTLS 是DTLS 协议的一个公开实现。在针对TinyDTLS 公开库的模糊测试过程中,ProCluster 多次触发程序崩溃,而基准对比工具AFLNet 在测试时没有触发任何崩溃。
对程序崩溃进行分析,发现当服务器接收数据的长度大于设置的最大长度时,服务器会自动截断多余数据,但在后续处理时只考虑接收数据的大小和消息内长度依赖之间的关系,忽略了实际保存在指针中的数据长度小于该值,最终造成缓冲区溢出。因为服务器在处理过程中会校验消息的长度字段值,ProCluster能够在变异得到测试用例后,根据消息的实际长度自动对消息内长度字段进行赋值,而AFLNet 变异得到的测试用例无法通过代码块1 中的长度字段校验,因此在测试过程中不能触发这类崩溃。
本文结合协议逆向分析技术设计并实现一种新的状态协议灰盒测试方法。ProCluster 基于对齐聚类实现对协议格式的自动推断,能够实现在消息字段级别进行变异操作,以生成更多符合规范的消息,并最大可能减少无效消息,从而获得更好的性能。将该方法与AFLNet 在TinyDTLS 和OpenSSL 两个公开库上进行测试,结果表明,本文所提的方法在路径覆盖以及崩溃触发各方面都优于AFLNet。在针对协议实现的模糊测试研究中,基于字段的变异策略不仅能够产生更符合程序预期的消息,而且能够避免同基于位和字节的变异策略一样产生大量无效的消息。本文方法在字段划分时依然不够精细化,下一步将研究实现像wireshark 一样的消息解析器,以区分出更细粒度的字段。