`
MyEyeOfJava
  • 浏览: 1152285 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
7af2d6ca-4fe1-3e9a-be85-3f65f7120bd0
测试开发
浏览量:71174
533896eb-dd7b-3cde-b4d3-cc1ce02c1c14
晨记
浏览量:0
社区版块
存档分类
最新评论

[Fuzz]模糊测试

阅读更多
文章来自IBM技术社区: 下一篇文章将按照此理论探讨一下android内的Fuzz测试
在他人攻击您的程序前,先自己攻击
模糊测试(Fuzz testing )是一项对代码质量有着深远影响的简单技术。在本文中,Elliotte Rusty Harold 故意将随机的坏数据插入应用程序,以观察发生的结果。他也解释了如何使用如校验和、XML 数据存储及代码验证等防护性编码技术,来加固您的程序以抵制随机数据。他以一个练习进行总结,在练习中他以一个代码破坏者的角度进行思考 —— 这是一种用于防护代码的至关重要的技术。

多年来,我惊叹于有如此大量能够使 Microsoft Word 崩溃的坏文件。少数字节错位,会使整个应用程序毁于一旦。在旧式的、无内存保护的操作系统中,整个计算机通常就这样宕掉了。Word 为什么不能意识到它接收到了坏的数据,并发出一条错误信息呢?为什么它会仅仅因为少数字节被损坏就破坏自己的栈、堆呢?当然,Word 并不是惟一一个面对畸形文件时表现得如此糟糕的程序。
本文介绍了一种试图避免这种灾难的技术。在模糊测试中,用随机坏数据(也称做 fuzz)攻击一个程序,然后等着观察哪里遭到了破坏。模糊测试的技巧在于,它是不符合逻辑的:自动模糊测试不去猜测哪个数据会导致破坏(就像人工测试员那样),而是将尽可能多的杂乱数据投入程序中。由这个测试验证过的失败模式通常对程序员来说是个彻底的震憾,因为任何按逻辑思考的人都不会想到这种失败。
模糊测试是一项简单的技术,但它却能揭示出程序中的重要 bug。它能够验证出现实世界中的错误模式并在您的软件发货前对潜在的应当被堵塞的攻击渠道进行提示。


模糊测试如何运行
模糊测试的实现是一个非常简单的过程:
准备一份插入程序中的正确的文件。
用随机数据替换该文件的某些部分。
用程序打开文件。
观察破坏了什么。
可以用任意多种方式改变该随机数据。例如,可以将整个文件打乱,而不是仅替换其中的一部分,也可以将该文件限制为 ASCII 文本或非零字节。不管用什么方式进行分割,关键是将大量随机数据放入应用程序并观察出故障的是什么。
测试基于 C 的应用程序
当字符串包含额外的零时,许多用 C 编写的程序都会出问题 —— 这类问题太过频繁以至于额外的零能够彻底隐藏代码中其他的问题。一旦验证出程序存在零字节问题,就可以移除它们,从而让其他的问题浮现出来。
可以手动进行初始化测试,但要想达到最佳的效果则确实需要采用自动化模糊测试。在这种情况下,当面临破坏输入时首先需要为应用程序定义适当的错误行为。(如果当输入数据被破坏时,您发现程序正常运行,且未定义发生的事件,那么这就是第一个 bug。)随后将随机数据传递到程序中直到找到了一个文件,该文件不会触发适当的错误对话框、消息、异常,等等。存储并记录该文件,这样就能在稍后重现该问题。如此重复。
尽管模糊测试通常需要一些手动编码,但还有一些工具能提供帮助。例如,清单 1 显示了一个简单的 Java™ 类,该类随机更改文件的特定长度。我常愿意在开始的几个字节后面启动模糊测试,因为程序似乎更可能注意到早期的错误而不是后面的错误。(您的目的是想找到程序未检测到的错误,而不是寻找已经检测到的。)


清单 1. 用随机数据替换文件部分的类
import java.io.*;
import java.security.SecureRandom;
import java.util.Random;

public class Fuzzer {

     private Random random = new SecureRandom();
     private int count = 1;

     public File fuzz(File in, int start, int length) throws IOException  
{

         byte[] data = new byte[(int) in.length()];
         DataInputStream din = new DataInputStream(new FileInputStream(in));
         din.readFully(data);
         fuzz(data, start, length);
         String name = "fuzz_" + count + "_" + in.getName();
         File fout = new File(name);
         FileOutputStream  out = new FileOutputStream(fout);
         out.write(data);
         out.close();
         din.close();
         count++;
         return fout;
     }


     // Modifies byte array in place
     public void fuzz(byte[] in, int start, int length) {

         byte[] fuzz = new byte[length];
         random.nextBytes(fuzz);
         System.arraycopy(fuzz, 0, in, start, fuzz.length);

     }

}


模糊测试文件很简单。将其传至应用程序通常不那么困难。如 AppleScript 或 Perl 脚本语言通常是编写模糊测试的最佳选择。对于 GUI 程序,最困难的部分是辨认出应用程序是否检测出正确的故障模式。有时,最简单的方法是让一个人坐在程序前将每一个测试通过或失败的结果都标记下来。一定要将所有生成的随机测试用例单独地命名并保存下来,这样就能够重现这个过程中检测到的任何故障。

防护性编码


可靠的编码遵循了这样的基本原则:绝不会让程序中插入未经过一致性及合理性验证的外部数据。
如果从文件中读入一个数字并期望其为正数,那么,在使用其进行进一步处理前对其先验证一下。如果期望字符串只包含 ASCII 字母,请确定它确实是这样。如果认为文件包含一个四字节的整数倍的数据,请验证一下。一定不要假设任何外部提供的数据中的字符都会如您所料。
最常见的错误是做出这样的假设:因为程序将该数据写出,该程序就能不用验证再一次将该数据读回去。这是很危险的!因为该数据很可能已经被另一个程序在磁盘上复写过了。它也可能已经被一个故障磁盘或坏的网络传输所破坏了或已经被另一个带 bug 的程序更改过了。它甚至可能已经被故意更改过以破坏程序的安全性。所以不要假设任何事,要进行验证。
当然,错误处理及验证十分令人生厌,也很不方便,并被全世界程序员们所轻视。计算机的诞生已进入了六十个年头,我们仍旧没有检查基本的东西,如成功打开一个文件及内存分配是否成功。让程序员们在阅读一个文件时测试每一个字节和每一个不变量似乎是无望的 —— 但不这样做就会使程序易被模糊攻击。幸运的是,可以寻求帮助。恰当使用现代工具和技术能够显著减轻加固应用程序的痛苦,特别是如下三种技术更为突出:
校验和
基于语法的格式,如 XML
验证过的代码如 Java

用校验和进行的模糊试验
能够保护程序抵御模糊攻击的最简单的方法是将一个检验和添加到数据中。例如,可以将文件中所有的字节都累加起来,然后取其除以 256 的余数。将得到的值存储到文件尾部的一个额外字节中。然后,在输入数据前,先验证检验和是否匹配。这项简单模式将未被发现的意外故障的风险降低到约 1/256 。
健壮的校验和算法如 MD5 和 SHA 并不仅仅取其除以 256 的余数,它完成的要多得多。在 Java 语言中,java.security.DigestInputStream 和 java.security.DigestOutputStream 类为将一个校验和附属到数据中提供了便捷的方式。使用这些校验和算法中的一种可以将程序遭受意外破坏的机率降低到少于十亿分之一(尽管故意攻击仍有可能)。

XML 存储及验证
将数据以 XML 形式存储是一种避免数据损坏的好方法。XML 最初即着力于 Web 页面、书籍、诗歌、文章及相似文档,它几乎在每个领域都获取了巨大的成功,从金融数据到矢量图形到序列化对象等等。
不切实际的限定
如果真想要破坏一个 XML 解析器,有几种方法可以试试。例如,大多数 XML 解析器服从于特定的最大尺寸。如果一个元素名长度超过 22 亿字符(Java String 的最大尺寸),SAX 解析器将会失败。尽管如此,在实践中这些极限值如此之高,以至于在达到之前内存就已经耗尽。
使 XML 格式抵制模糊攻击的关键特征是一个对输入不做任何 假设的解析器。这就是真正想在一个健壮的文件格式中所获得的。设计 XML 解析器是为了让任何输入(格式良好的或无格式的,有效的或无效的)都以定义好的形式处理。XML 解析器能够处理任何 字节流。如果数据首先通过了 XML 解析器,则仅需要准备好接受解析器所能提供的东西。例如,不需要检查数据是否包含空字符,因为 XML 解析器绝不会传送一个空值。如果 XML 解析器在其输入中看到一个空字符,它就会发出异常并停止处理。当然还需要处理这个异常,但编写一个 catch 块来处理检测到的错误比起编写代码来检测所有可能的错误来说要简单得多。
为使程序更加安全,可以用 DTD 和/或模式来验证文档。这不仅检查了 XML 是否格式良好,而且至少与所预期更加接近。验证并不会告知关于文档所需了解的一切,但它却使编写大量简单检查变得很简单。用 XML,很明显能够将所接受的文档严格地限定为能够处理的格式。
尽管如此,还有多段代码不能用 DTD 或模式进行验证。例如,不能测试发票上商品的价格是否和数据库中库存商品的价格一致。当从客户接收到一份包含价格的订单文档时,不论其是 XML 格式或是其他格式,在提交前通常都会检查一下,以确保客户并未修改价格。可以用定制代码实现这些最后的检查。

基于语法的格式
使 XML 能够对模糊攻击具有如此的抵御能力的是其使用巴科斯-诺尔范式(Backus-Naur Form,BNF)语法仔细且标准地定义的格式。许多解析器都是使用如 JavaCC 或 Bison 等解析器-生成器工具直接从此语法中构建的。这种工具的实质是阅读一个任意的输入流并确定其是否符合此语法。
如果 XML 并不适合于您的文件格式,您仍可以从基于解析器的解决方案的健壮性中获益。您必须为文件格式自行编写语法,随后开发自己的解析器来阅读它。相比使用唾手可得的 XML 解析器,开发自己的解析器需要更多的工作。然而它是一个更为健壮的解决方案,而不是不根据语法正式地进行验证就将数据简单地装载到内存中。


Java 代码验证
由模糊测试导致的许多故障都是内存分配错误及缓冲器溢出的结果。用一种安全的垃圾收集语言(在如 Java 或 managed C# 等虚拟机上执行的)来编写应用程序避免了许多潜在问题。即使用 C 或 C++ 来编写代码,还是需要使用一个可靠的垃圾收集库。在 2006 年,台式机程序员或服务器程序员不应该还需要管理内存。
Java 运行时对其自身的代码起到了额外保护层的作用。在将一个 .class 文件装载到虚拟机之前,该文件要由一个字节符验证器或一个可选的 SecurityManager 进行验证。Java 并不假设创建 .class 文件的编译器没有 bug 且运转正常。设计 Java 语言之初就是为了允许在一个安全沙箱中运行不信任的、潜在恶意的代码。它甚至不信任其自身编译过的代码。毕竟,也许有人已经用十六进制编辑器手工修改了字节符,试图触发缓冲器溢出。我们大家都应该对我们的程序也有对输入这样的偏执。


以敌人的角度思考
之前介绍的每项技术都在阻止意外破坏方面造诣颇深。将它们综合起来恰当地实现,会将未被发现的非蓄意破坏发生的可能性几乎减少到零。(当然,并不会减少到零,但其发生的可能性就如同一束偏离轨道的宇宙射线将您 CPU 运算 1+1 的结果变为 3 的可能性一样微乎其微。)但不是所有的数据损坏都是非蓄意的。如果有人故意引入坏数据来破坏程序的安全性又该如何呢?以一个攻击者的角度进行思考是防护代码的下一个步骤。
转回到一个攻击者的角度进行思考,假设要攻击的应用程序是用 Java 编程语言编写的、使用非本地代码且将所有额外数据都以 XML(在接受前经过彻底验证)形式存储,还能成功攻击吗?是的,能。但用随机改变文件字节的低级方法显然不行。需要一种更为复杂的方法来说明程序自身的错误检测机制及路径。
当测试一个抵御模糊攻击的应用程序时,不可能做纯黑盒测试,但通过一些明显的修改,基本的想法还是可以应用的。例如,考虑校验和,如果文件格式包含一个校验和,在将文件传至应用程序前仅仅修改此校验和就可以使其同随机数据相匹配。
对于 XML,试着模糊单独的元素内容和属性值,而不是从文档中挑选一部分随机的字节进行替换。一定要用合法的 XML 字符替换数据,而不要用随机字节,因为即使一百字节的随机数据也一定是畸形的。也可以改变元素名称和属性名称,只要细心地确保得到的文档格式仍是正确的就可以了。如果该 XML 文档是由一个限制非常严格的模式进行检查的,还需要计算出该模式没有 检查什么,以决定在哪里进行有效的模糊。
一个结合了对剩余数据进行代码级验证的真正严格的模式也许不会留下可操纵的空间。这就是作为一个开发人员所需要追求的。应用程序应能够处理所发送的任何有意义的字节流,而不会因权利上( de jure ) 无效而拒绝。

结束语
模糊测试能够说明 bug 在程序中的出现。并不证明不存在这样的 bug。而且,通过模糊测试会极大地提高您对应用程序的健壮性及抵御意外输入的安全性的自信心。如果您用 24 小时对程序进行模糊测试而其依然无事,那么随后同种类型的攻击就不大可能再危及到它。(并不是不可能,提醒您,只是可能性很小。)如果模糊测试揭示出程序中的 bug,就应该进行修正,而不是当 bug 随机出现时再对付它们。模糊测试通过明智地使用校验和、XML、垃圾收集和/或基于语法的文件格式,更有效地从根本上加固了文件格式。
模糊测试是一项用于验证程序中真实错误的重要工具,也是所有意识到安全性问题且着力于程序健壮性的程序员们的工具箱中所必备的工具。
分享到:
评论

相关推荐

    burpfuzz插件模糊测试专用

    【描述】"支持各种参数字典,fuzz模糊测试专用,让挖漏洞更加方便"表明burpfuzz插件不仅具备基础的模糊测试功能,还能够处理各种参数字典。这意味着用户可以自定义不同的输入数据集,这些数据集可能包含特殊字符、...

    2019.11-使用peach进行模糊测试从入门到放弃1

    在软件测试中,模糊测试(Fuzz Testing)是一种测试方法,旨在发现软件中的缺陷和漏洞。Peach是一款功能强大且流行的模糊测试框架,使用C#语言开发,支持多种协议和文件格式。下面我们将详细介绍使用Peach进行模糊...

    模糊测试Fuzzing详细总结.md

    fuzz 模糊测试Fuzzing详细总结.md

    fuzz字典大全。多形式fuzz

    这个压缩包文件“fuzzDicts-master”显然包含了一整套的fuzz字典,这些字典用于在模糊测试过程中生成各种输入数据,以探测程序可能的异常反应。下面将详细讨论fuzzing的概念、重要性以及如何利用字典进行模糊测试。 ...

    Google模糊测试服务OSS-Fuzz.zip

    OSS-FuzzOSS-Fuzz 能够针对开源软件进行持续的模糊测试,它的目的是利用更新的模糊测试技术与可拓展的分布式执行相结合,提高一般软件基础架构的安全性与稳定性。OSS-Fuzz 结合了多种模糊测试技术/漏洞捕捉技术(即...

    OSS-Fuzz:容器和云计算在模糊测试中的应用 - Hanhan1

    OSS-Fuzz是谷歌发起的一个项目,专注于对开源软件进行持续的模糊测试,以提高其安全性和稳定性。模糊测试,或称Fuzzing,是一种自动化的软件测试技术,通过向程序提供无效、意外或随机的输入来检测潜在的漏洞。这种...

    模糊测试 强制发掘安全漏洞的利器_高清版.rar

    所有代码的图片都是看的清楚的!...“模糊测试”方法使用随机数据作为被测程序的输入,然后系统性地找出这些输入数据导致的应用故障。本书是第一本,也是到目前为止唯一的一本劝募覆盖模糊测试的著作。

    Peach对Modbus功能码的模糊测试 · Uknow - Stay hungry Stay foolish1

    【Peach模糊测试框架】 Peach是一个强大的模糊测试框架,由Michael Eddington等人开发,遵循MIT开源许可证。自2004年首次发布以来,它经历了多次迭代,最新的第三版使用C#语言完全重写了整个框架。Peach支持对各种...

    upload-fuzz-dic-builder-master.zip

    文件上传时,文件名的fuzz模糊测试,可以利用脚本生成大量测试名,可以用burpsuite进行测试。

    fuzz.rar(模糊测试工具)

    Fuzz是世界上第一款Fuzzing工具,也是该技术称之为‘fuzzing’的原因,它是威斯康星大学研究所测试windows程序的工具。该工具利用windows的消息机制向窗口随机的发送数据,试图使得应用程序崩溃。

    Fuzzing-模糊测试--强制性安全漏洞发掘

    ### Fuzzing-模糊测试--强制性安全漏洞发掘 #### 概述与核心概念 模糊测试(Fuzzing)作为一种有效的安全漏洞发掘方法,近年来在IT安全领域引起了广泛关注。其核心理念是通过向目标系统输入大量未经筛选的、随机...

    模糊测试 强制性安全漏洞发掘

    ### 模糊测试强制性安全漏洞发掘 #### 模糊测试概述 模糊测试(Fuzzing)是一种通过向目标系统输入非预期数据来检测软件缺陷或安全漏洞的技术。这种技术通常用于发现潜在的安全问题,例如缓冲区溢出、格式字符串...

    cargo-fuzz:用于模糊测试的命令行助手

    cargo-fuzz 是 Rust 生态系统中的一个非常有用的工具,它是一个命令行助手,专门用于进行模糊测试(fuzzing)。模糊测试是一种自动化测试方法,通过大量随机输入来寻找软件中的漏洞和不稳定点。在 Rust 语言中,由于...

    FUZZ爆破字典.rar

    在IT安全领域,FUZZ(模糊测试)是一种广泛使用的漏洞发现技术,通过对软件或系统输入端口提供非预期的数据来探测潜在的错误和崩溃。"FUZZ爆破字典.rar"是一个专门用于FUZZ测试的资源包,它包含了各种可能的输入数据...

    开源项目-dvyukov-go-fuzz.zip

    【开源项目-dvyukov-go-fuzz.zip】是一个聚焦于Go语言库的开源模糊测试工具,由dvyukov开发并维护。模糊测试,也称为fuzzing,是一种黑盒软件测试方法,通过生成随机输入数据来发现程序中的错误和漏洞。在Go-fuzz...

    基于AFL的模糊测试要求

    基于AFL的模糊测试要求 本资源摘要信息涵盖了基于AFL的模糊测试要求,涵盖了测试、标签、部分内容等方面的知识点。 1. 测试 AFL(American Fuzzy Lop)是一种模糊测试工具,用于检测软件中的漏洞。基于AFL的模糊...

    api-fuzz:python restful api模糊测试

    IntelliFuzzTest cli测试工具安装git clone https://github.com/smasterfree/api-fuzz.gitpip install -r requirements.txt快速开始将curl请求体放进request.txt文件中,执行命令python IntelliFuzzTest_cli.py ...

Global site tag (gtag.js) - Google Analytics