导语:本文旨在描述我们是如何解决这一项目中所遇到的技术局限以及时间限制等问题,并通过基于变异的Fuzzing来成功测试和查找应用程序中的问题。

介绍

作为一名安全顾问,我们常常需要对我们客户的应用程序进行黑盒安全测试。而在通常情况下,我们所评估的对象都是那些使用自己的私有方案来进行通信的应用程序,而不是依赖于那些常规协议(如HTTP)的。

最近,我们参与了一个短期的安全测试项目,测试的对象就是一个具有自己的通信协议的定制应用程序,该通信协议包裹在SSL / TLS隧道中,因此我们需要完整的去了解其安全性。

本文旨在描述我们是如何解决这一项目中所遇到的技术局限以及时间限制等问题,并通过基于变异的Fuzzing来成功测试和查找应用程序中的问题。 

什么是私有协议?

大致来说,私有协议是一种未被开放标准定义或由个人或公司控制的协议。在许多情况下,没有关于其内部工作的官方文档,并且其难以与使用封闭协议的应用程序通信。

简单来说就是协议的复杂程度会有所不同,有些比其他的更复杂:例如,通常情况下给定的协议可能非常简单 ,他们基于ASCII字符,易于理解,并且可以容易地与其他元素相关的其他字段的消息。但也可能存在更复杂的协议。他们可能涉及不明显的不同数据表示,二进制或序列化数据,校验和和值字段等。

当然尽管是私有的,有些协议还是有自己的规范文件或者由供应商提供的官方文档(如金融交易所所使用的FIX,其被广泛用于股票交易)或已经被技术爱好者和开源倡导者工程进行逆向的协议——这方面比较经典的例子就是AOL的即时通讯OSCAR协议。

虽然网络协议的逆向工程是完全可能的,但这毫无疑问是一个麻烦的任务,这其中涉及非常多的耐心以及高超的技能,当然最重要的还是时间。并且拥有所有这些要素也不一定就是可行的,所以我们需要一个替代方案来解决我们手头上的问题。

自动软件安全测试与fuzzing

fuzzing是过去几年一直很受重视的一个领域,学术界和行业里都出现了很多先进的方法。

早期的 fuzzing技术可以大致分为变异和基于生成,尽管存在更先进和现代的方法,如进化fuzzing,利用代码覆盖的反馈,并借助遗传算法产生更好的投入,以及其他技术的依赖开约束求解器,concolic和象征性执行等

基于变异的fuzzing通常被称为“哑巴fuzzing”,因为它的作用是执行输入的随机变异并且吐出被破坏的数据作为结果。然而,不要被它的名字所欺骗:可能看起来很笨的fuzzing可以非常有效,并且其在流行的软件中发现过许多错误。

另一种流行的策略被称为基于生成的fuzzing。这种技术涉及协议或文件格式的先前知识是fuzzed的,并且它可以生成大多数符合协议的测试用例,但是也会包含一些已知的可能导致意外行为的数据的字段,如大字符串,包含shell元字符的恶意输入,负数,非常长或非常正常的数字等等。

优缺点对比

基于变异的fuzzing具有快速建立的优点,因此在实际应用中可以获得良好的效果; 然而,在实现高代码覆盖率(您的测试样本将在此处发挥关键作用),或者在将校验和发送到应用程序进行处理之前需要调整校验和的情况下,可能无法实现高应用程序覆盖率 ,因为应用程序很可能会拒绝其余部分作为校验和的输入将失败,没有达到代码的潜在易受攻击的区域。

而基于生成的代码更有可能具有更大的代码覆盖率,当然有所牺牲的是创建时间更长。此外,fuzzing的神奇的创造力以及变异和数据破坏启发式的巧妙之处将决定其在查找错误方面的成功。

我们的经验

很幸运的是我们处理的协议虽然是私有的,但却是基于ASCII的,似乎并没有太复杂。然而,考虑到时间限制,我们毫无疑问没有办法去选择花费时间来了解它并创建一个基于生成的fuzzer?而且,为什么要花费宝贵的时间呢?我们完全可以写出我们自己的变异引擎,这不是更好吗?

当然更为重要的一点是与协议本身有关:由于无需计算校验和或符合其他严格的协议结构,因此我们选择使用基于变异的方法进行模糊。

在这个项目中,我们拿到的都是客户端和我们想要测试的应用程序之间的明文流量的数据包捕获(PCAP)。

我们注意来确保我们将获得不同功能和用途的几个样本PCAPs; 这样我们可以保证我们的fuzzer会更有压力,即在测试过程中,使得针对应用程序的可用攻击面的不同区域,增加代码覆盖率以及增加我们发现错误的机会。

在这种情况下,客户端和应用程序之间的所有流量都被包裹在TLS / SSL中,请求捕获非加密流量非常重要,因为我们需要应用程序级通信,而不是传输或上层层流量。

另外,我们必须模糊的协议是某种状态机:在握手消息(也可能被fuzzing)之后,它会按某种顺序预期一些其他消息进行进一步处理。

这意味着如果我们发送了握手信息,并在其期望发送一个格式错误的消息(例如登录消息)时,我们将无法fuzzing任何登录信息,因为它会被拒绝。

为了解决这个问题,我们为我们的Fuzzer增加了一些更多的随机性:PCAP中只有一定比例的有效载荷将被变异。这将给我们更多的排列和发送干净的握手和N-1消息的能力,但只有最后(N个消息)模糊; 或者模糊的握手和干净的消息,或清理握手和格式错误的登录,并清除其他消息等。 

工具

为了组合一个可以执行上述任务的Python脚本,我们使用了下面这些工具:

Scapy:一个库,相当于是与网络相关的所有事物的瑞士军刀,可以从PCAP解析,读取,写入和重写数据。

radamsa:一种流行的基于变异的模糊工具,许多安全研究人员都会选择的武器。

步骤分解

在概述了我们想要做的内容、哪种模糊策略最适合我们的案例以及该任务所需的工具后,现在是时候来粗略地分解我们用来执行应用程序的fuzzing的步骤:

步骤0:定义一个 fuzz因子 - 例如,只有20%的数据包被变异
步骤1:解析PCAP寻找客户端 - >服务器通信
步骤2:在捕获的内容中,找到包含实际有效载荷的那些被人,因为PCAP有时会包含与我们目的无关的通信,例如TCP 3次握手和其他信令
步骤3:输入无限循环:
步骤3.1:提取payload,随机决定是否应根据fuzz因素进行模糊; 如果是这样,将其插入变异引擎
步骤3.2:获取变异数据
步骤3.3:向目标应用程序发送受损的payload(或清洁的payload,取决于是否fuzzed)
步骤3.4:洗涤,冲洗,重复,这样子持续一个晚上。
步骤4:观察崩溃并执行崩溃分类(如果适用)

事实上,第四步并不在我们的范围内:由于从黑盒的角度出发,没有任何手段,甚至没有观察到我们所需要验证的事故。客户端很好地验证了自身的崩溃,我们的fuzzing脚本在发送每个受损测试用例后检查了与目标的连接。虽然缓慢,但是给目标应用程序是否崩溃了一些可见度。

后来在整个的项目过程中,我们被客户通知,Fuzzer触发了四次碰撞; 其中三个是独一无二的。我们没有足够的时间进行任何类型的崩溃分析,但是fuzzing的测试用例会触发未处理的异常,并可能严重影响预计始终处于联机状态的服务的可用性。

上述步骤的开源实现可以在我们的Github中找到。这篇文章更为重要的是去说明我们的一些想法。另外示例代码几乎不能应用于实际的应用程序中,但通过一些修改可能适合不同的需求。

结论

即使是在最简单的形式中,fuzzing也可能是发现漏洞的非常有用的工具,并且其应该在每个信息安全工程师的技能包中。因为其对于使用私有或非文档化协议的应用程序,目标应用程序的不同使用形式的样本PCAP对于测试更大的攻击面并提高查找错误的可能性非常有用。另外,具有创造性和非常规启发式的好的fuzzing引擎也是必要的,比如在我们所说的例子中其正是以变异的可能来触发微妙错误并覆盖深入到了代码逻辑的角落。

源链接

Hacking more

...