Fuzz 初探
更新中,未完待续
翻译自@h0mbre的两篇文章Fuzz Like A Caveman
介绍
本文将会展示如何创建一个非常简单的基于变异的fuzzer,理想情况下可以在开源的软件里找到一些crash。文章创建的fuzzer是基于油管上的@gynvael的fuzz教程,另外还要推荐一下Brandon Faulk的fuzz直播,他还有一个很有意思的介绍fuzz思想概念的视频
挑个目标
我们要找一个C/C++编写的、可以解析从文件里解析出来数据的软件。我找到的第一个软件是一个从图像里解析出Exif的软件,我们还将挑选一个几乎完全不处理安全问题的软件,因为我会直接公开这些漏洞研究。
通过这个文章,我们可以了解到一些Exif基础,Exif文件格式和JPEG文件相同,Exif按照JPEG的规范,将一些图像信息数据和缩略图,插入到JPEG文件中,因此你可以在兼容JPEG的浏览器/图片查看器/图片修改等软件中,如同浏览普通JPEG文件一样浏览Exif格式的图像文件。
因此Exif可以按照JPEG规范,把元数据类型信息插入到图像中,并且有很多软件可以用来解析这些数据。
开搞
我们将用Python3做一个基于突变样本的fuzzer,来调整合法的,填充了Exif数据的JPEG文件,然后把它们提供给解析器,看看能不能得到一个崩溃。
首先,我们需要一个填充了Exif数据的合法的JPEG文件,谷歌搜索Sample JPEG with Exif
可以找到这个repo,我接下来会用Canon_40D.jpg
来作为测试样本。
了解一下JPEG和Exif的规范
在开始写代码前,我们应该先了解一下JPEG和Exif规范,保证我们制作的样本都是符合规范的,这样可以防止浪费我们宝贵的fuzz循环。
通过这个文档我们可以知道JPEG文件以0xFFD8
开始,以0xFFD9
结束,前两个字节即操作系统用来标志文件类型的所谓的魔数。
root@kali:~# file Canon_40D.jpg
Canon_40D.jpg: JPEG image data, JFIF standard 1.01, resolution (DPI), density 72x72, segment length 16, Exif Standard: [TIFF image data, little-endian, direntries=11, manufacturer=Canon, model=Canon EOS 40D, orientation=upper-left, xresolution=166, yresolution=174, resolutionunit=2, software=GIMP 2.4.5, datetime=2008:07:31 10:38:11, GPS-Data], baseline, precision 8, 100x68, components 3
我们可以去掉.jpeg
得到相同的输出,因为操作系统靠前文中的魔数来识别文件类型。
root@kali:~# file Canon
Canon: JPEG image data, JFIF standard 1.01, resolution (DPI), density 72x72, segment length 16, Exif Standard: [TIFF image data, little-endian, direntries=11, manufacturer=Canon, model=Canon EOS 40D, orientation=upper-left, xresolution=166, yresolution=174, resolutionunit=2, software=GIMP 2.4.5, datetime=2008:07:31 10:38:11, GPS-Data], baseline, precision 8, 100x68, components 3
用hexdump查看文件的话,可以看到开头和结尾是0xFFD8
和0xFFD9
root@kali:~# hexdump Canon
0000000 d8ff e0ff 1000 464a 4649 0100 0101 4800
------SNIP------
0001f10 5aed 5158 d9ff
文件规范中有个很有意思的信息是标记以0xFF
开头,然后有几个静态标记:
- 图像开始标记:
0xFFD8
- APP1标记:
0xFFE1
- 通用标记:
0xFFXX
- 图片结束标记:
0xFFD9
由于我们不想改变图像的长度或者类型,所以让我们搞个计划,尽可能保持文件头尾不变,比如我们不希望把0xFFD9
插入到图像中央,这样的话就会截断图像,导致图像解析器以非崩溃的方式出错。
开始我们的fuzzer
首先我们要提取出来我们要作为有效
输入的JPEG样本中的所有字节,当然我们会对它进行一些修改。我们的代码开始是这样的:
#!/usr/bin/env python3 |
如果想看一下读取到的数据是什么样的,可以输出一下前十个数据:
else: |
接下来试着用我们的字节数组创建一个新的有效的JPEG文件:
def create_new(data): |
看一下md5:
root@kali:~# shasum Canon_40D.jpg mutated.jpg
c3d98686223ad69ea29c811aaab35d343ff1ae9e Canon_40D.jpg
c3d98686223ad69ea29c811aaab35d343ff1ae9e mutated.jpg
文件变异
为了保持fuzzer的简单,接下来将仅使用两种突变方法:
- 比特翻转
- 用Gynvael的魔数来覆盖字节序列
我们先从比特翻转开始,如果255(0xFF)用二进制表示是11111111
,随即反转一位比如下标是2的位,就会变成11011111
,即223或者0xDF。