-
-
[翻译]写一个简单的fuzzer – part 1
-
发表于:
2020-5-8 01:16
13919
-
[翻译]写一个简单的fuzzer – part 1
因为新冠病毒的原因我们已经在家待了很久了,趁此机会我们可以在家搞点事情,也有很多人是这么想的,很多人在Twitter上发了文章,即便如此,我还是准备开一个系列,写一个简单的fuzzer,事实上我认为每一个从事信息安全的技术人员都应该写一个自己的fuzzer,倒不是说真的去用自己写的fuzzer挖洞(考虑到已经有人写了很多更高效的fuzzer),而是去了解fuzzer的工作原理和机制;事实上像我这么想的人并不在少数,之前有个叫h0mbre的铁子写了一篇“像老八一样去fuzzing”后来又写了一篇“超越老八的fuzzer”,我在拜读他的fuzzer code的同时还发现了一些改进fuzzer的方法,在开始我自创简易fuzzer之前我强烈建议你们先去看一下h0mbre的文章 :p
首先说明一下我们将用Python写一个最简单的fuzzer,但是这个fuzzer是不可能用于实际的模糊测试的,真正的fuzzer最好要用更接近底层的高级语言来写,比如C/C++和Rust(但是几乎没有人用Python写),这是因为像Python这样的解释性语言执行的时候要比C/C++慢很多。其次,我们会使用h0mbre老铁在他的文章里的目标库文件:exif,这个库文件是多年之前写的并且写的很好,所以并不会一下子爆出很多crash,这导致的结果就是我们会不断地思考是不是我们写的代码不够好,或者是模糊处理的辣鸡,如此思考下来会让我们迅速的进步。
一开始fuzzer是很简单的:只要不断的创建随机数据并喂给程序看它有没有crash,然后不断地变换随机策略再次喂给程序,循环往复…… 所以本质上fuzzer就是不断的做着同样的事情,并且期待着不同的结果(就想孤岛危机里的那个CG一样)。在开始写代码之前我们需要记住一件事情,那就是fuzzer永不崩溃!!!就像海森堡定理、薛定谔的喵还有兽人永不为奴一样!!!
我认为每个fuzzer都至少得有两个组件 – 变异引擎和执行引擎(“像老八一样fuzzing”这篇文章里也提到了这一点):
main函数里的代码主要分为两部分:初始化阶段和fuzz循环
在理想情况下每次模糊测试都应该有且只有一次初始化,所以吃性能的操作都可以放在初始化里(必要组件的初始化、读取配置文件等等),事实上我们只读取原始文件样本(稍后会介绍到)并设置调试器(稍后也会介绍到)
Fuzz循环是一段可能会被执行数万甚至数十万次的代码段,因此我们会尽量简化这段代码,在这个例子中,我们的循环做了两件重要的事情 – 修改数据并运行目标程序,代码中可以去除一些多余的东西(比如计数器),但是我们先不考虑这个。
变异引擎是我们的fuzzer代码中最重要的组件之一,也是能改进的最多的地方(特别是性能方面的改进);另外就是应该选用一个有效的数据样本开始fuzzing,并对这个数据样本进行修改,如果选择随机数据的话,fuzzer会产生许多垃圾数据,因为前两个字节与期望值不匹配。
在这个例子中,我们先从一个已知有效的JPEG文件开始:canon_40D.jpg(该文件已包含有效的exif数据),我们通过get_bytes()函数读取它,让后将其转换为一个字节数组,并提供给下面的函数:
前两行代码负责选择要修改文件中多少字节以及哪些字节,如果FLIP_RATIO被设置成了1%,我们就会修改jpg文件80个不同的位置,我们会在这些位置填充任意值,往后在我们不断的优化和改进下我们就会知道哪些值是更好的,这样就可以优化我们的fuzzer,另外jpg的文件格式导致文件头和尾是不可以修改的。
在主循环里可以用两种方法修改文件的值——位翻转(xor)或者直接给一个有效的值,我们首先进行位翻转(因为简单):
我们从数组中随机选择一个值并进行异或(xor)操作。
直接给一个有效的值会稍微复杂一些,但简而言之我们会用一个接近整数类型的最大或最小值,这些值很有可能会因为一些没有检查正负号的算术操作而出发bug:
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2020-5-8 01:21
被pureGavin编辑
,原因: 添加超链接