fuzzing with peach(Just a toy)

0x00: 前言

之前学习过peach的使用,在willj师傅的指导下尝试去fuzz了某播放器,只是个尝试,并没有更深入去搞(本来的计划是结合winafl的),虽然没啥产出,但是fuzz过程还算清晰。过程中实现了自动化的fuzz脚本,有完整的功能,就是效率堪忧-。-

0x01: 关于peach

peach是一款优秀的文件格式fuzz工具。
peach是基于模板变异工作的,而且开源,文档虽然不是那么多,但是自己多摸索、学习还是可以学会一些基本的用法的。我在看使用peach做文件fuzz相关的资料的时候,最开始看的《0day2》里作者给的例子,从编写pit file到fuzz跑起来;之后看了一份国际友人写的Fuzzing with Peach – Part 1 « Flinkd!,他这篇文章写的非常好,几乎涵盖了peach 90%的语法,仔细阅读,自己动手实践,会很快入门pit file的编写。

0x02: 文件fuzz的思路

fuzz嘛,简单的来看就是

  • 构造输入
  • 传给目标程序
  • 程序状态检测(是否crash)
  • 做log

之后根据你的log,把有用的样本拿出来在分析。
我的想法也很简单,就是利用peach基于我给的一个小文件,生成很多样本,然后写自动化的脚本去fuzz,并且做好异常检测的工作。
我的目标是adobe flash player sa版本,刚开始尝试就做一点简单的,选择flv文件作为fuzz的点。下面的问题就是:

  1. flv文件格式
  2. 根据文件格式编写pit file
  3. 如何加载我的fuzz.flv文件?
  4. 异常检测怎么做

下面慢慢来分析。

  1. flv文件格式
    flv文件主要分为headerbody两个部分。

    1. header部分
      1
      2
      3
      4
      第1-3字节:文件标志,FLV的文件标志为固定的“FLV",字节(0x46, 0x4C,0x56),见上面的字节序和字符序两行;
      第4字节:当前文件版本,固定为1(0x01)
      第5字节:此字节当前用到的只有第6,8两个bit位,分别标志当前文件是否存在音频,视频。参见上面bit序,即是第5字节的内容;
      第6-9字节:此4字节共同组成一个无符号32位整数(使用大头序),表示文件从FLV Header开始到Flv Body的字节数,当前版本固定为9(0x00,0x00,0x00,0x09)

    2.body部分
    这部分其实就是很多的tag的组合。
    不过tag的种类有三种,分别是script、Audio、Video。每种tag的tag data又各不相同,详细的可以看一些文档了解。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    -------------------------
    | Previous Tag Size |
    -------------------------
    | Tag |
    -------------------------
    | Previous Tag Size |
    -------------------------
    | Tag |
    -------------------------
    | Previous Tag Size |
    -------------------------
    | Tag |
    -------------------------
    | Previous Tag Size |
    -------------------------

    一些参考的文档flv文件格式详解,以及是官方的flv格式相关的文档都可以。

  2. 根据文件格式编写pit file
    了解了文件格式之后就是编写pit file了,困难的地方可能就在于tag结构,因为数目不确定,而且相互之间有联系,比如某个bit为1或者0,影响着后面的某个结构的有无。
    我的做法是,我使用类似Switch case这样的结构,让peach自己去判断选择对应的结构,即我写三种tag,然后限定最大的出现次数,因为样本很小,几百个最多了,然后peach根据模板文件的tag的标志,找到我pit file里对应的tag的结构,然后根据pit file里的结构进行变异,然后生成新的样本。
    这里可以集合010 editor的二进制模板功能,可以对比你生成的样本是否正确,方便调试。

  3. 如何加载我的fuzz.flv文件?
    有了样本,下面的问题就是怎么加载样本然后播放了。flash并不直接打开flv文件,而是使用swf来加载,所以我需要用as语言来编写一个swf来加载。这时候就有又一个问题:swf要编译的,即我的文件名会写死,这时候就麻烦了。
    不过这个也好解决,我可以在后续的fuzz脚本中,每次单独复制一个样本到工作目录,然后重命名为swf要加载的文件的名字,然后起flash,加载swf,然后做后续的工作;完成之后,循环这个工作。这样就可以很好的解决这个问题了。

  4. 异常检测怎么做
    我觉得最简单的办法就是调试器了,如果你进程崩了,你的just in time debugger会启动,问你要不要调试,你检测进程就可以了,然后做log,杀了所有进程,继续下一轮fuzz就好了。缺点很明显,这方法贼搓,而且效率低的要死,刚开始搞嘛,凑合用咯。

0x03: 编写pit file

首先是针对flv header的部分的编写

1
2
3
4
5
6
7
8
9
10
11
<DataModel name="flvHeader">
4<String name="flv_Signature" value="464C5601" valueType="hex" token="true" mutable="false"/>
<Flags name="HeadFlags" size="8">
<Flag name="dummy" position="3" size="5"/>
444<Flag name="audio" position="2" size="1"/>
<Flag name="dummy2" position="1" size="1"/>
<Flag name="video" position="0" size="1"/>
</Flags>
<Number name="dataoffset" value="9" size="32"/>
4<Number name="zero" size="32"/>
</DataModel>

这部分是script tag部分的编写

1
2
3
4
5
6
7
8
9
10
<Block name="script">
<Number name="type" size="8" signed="false" endian="big" value="18" token="true" mutable="false"/>
<Number name="datasize" size="24" endian="big" signed="false"/>
<Number name="timestamp" size="24" endian="big" signed="false"/>
<Number name="timestampi" size="8" endian="big" signed="false"/>
<Number name="streamid" size="24" endian="big" signed="false"/>
<Number name="firstbyte" size="8" endian="big" signed="false"/>
<Blob name="data2" lengthType="calc" length="int(self.find('datasize').getInternalValue())-1"/>
<Number name="lastsize" size="32" endian="big" signed="false"/>
</Block>

这部分是audio tag部分的编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<Block name="audio">
4<Number name="type" size="8" signed="false" endian="big" value="8" token="true" mutable="false"/>
4<Number name="datasize1" size="24" endian="big" signed="false"/>
4<Number name="timestamp" size="24" endian="big" signed="false"/>
4<Number name="timestampi" size="8" endian="big" signed="false"/>
4<Number name="streamid" size="24" endian="big" signed="false"/>
44<Flags name="Flag3" size="8">
444<Flag name="fmt" position="0" size="4"/>
444<Flag name="sr" position="4" size="2"/>
444<Flag name="bits" position="6" size="1"/>
<Flag name="channels" position="7" size="1"/>
44</Flags>
<Block>
<Relation type="when" when="int(self.find('Flag3.fmt').getInternalValue()) == 10"/>
<Blob name="frmtype" length="1"/>
<Blob name="data1" lengthType="calc" length="int(self.find('datasize1').getInternalValue())-2"/>
</Block>
44<Block>
<Relation type="when" when="int(self.find('Flag3.fmt').getInternalValue()) != 10"/>
<Blob name="data1" lengthType="calc" length="int(self.find('datasize1').getInternalValue())-1"/>
</Block>
4<Number name="lastsize" size="32" endian="big" signed="false"/>
</Block>

这部分是video tag的部分的编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<Block name="video">
4<Number name="type" size="8" signed="false" endian="big" value="9" token="true" mutable="false"/>
4<Number name="datasize2" size="24" endian="big" signed="false"/>
4<Number name="timestamp" size="24" endian="big" signed="false"/>
4<Number name="timestampi" size="8" endian="big" signed="false"/>
4<Number name="streamid" size="24" endian="big" signed="false"/>
4<Flags name="Flag2" size="8">
<Flag name="frmtype" position="0" size="4"/>
<Flag name="codecid" position="4" size="4"/>
</Flags>
4<Block>
<Relation type="when" when="int(self.find('Flag2.codecid').getInternalValue()) == 7"/>
44<Blob name="pkttype" length="1"/>
44<Blob name="compotime" length="3"/>
<Blob name="data" lengthType="calc" length="int(self.find('datasize2').getInternalValue())-5"/>
</Block>
4<Block>
<Relation type="when" when="int(self.find('Flag2.codecid').getInternalValue()) != 7"/>
44<Blob name="data" lengthType="calc" length="int(self.find('datasize2').getInternalValue())-1"/>
4</Block>
4<Number name="lastsize" size="32" endian="big" signed="false"/>
</Block>

0x04: swf加载样本

利用as语言编写的代码,编译后得到swf文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package
{
import flash.display.Sprite;
import flash.net.*;
import flash.media.*;
import flash.utils.*;
import flash.display.*
import flash.events.*;
import flash.system.fscommand;
import flash.display3D.textures.VideoTexture;
public class Main extends Sprite
{
public function Main():void
{
var video:Video;
var netCon:NetConnection;
var stream:NetStream;
function loadVideo(url:String):Video
{
video = new Video();
netCon = new NetConnection();
netCon.connect(null);
stream = new NetStream(netCon);
stream.play(url);
var client:Object = new Object();
client.onMetaData = onMetaEvent;
stream.client = client;
stream.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
video.attachNetStream(stream);
return video;
}
function onMetaEvent(e:Object):void
{
}
function netStatus(e:NetStatusEvent):void
{
video.width = stage.stageWidth;
video.height = stage.stageHeight;
}
stage.addChild(loadVideo("fuzz.flv"));
}
}
}

0x05: 自动化fuzz脚本

核心部分的代码如下。

1
2
3
4
5
6
7
def run(fileID):
copyFile(fileID)
subprocess.Popen(runCmd)
#sleep(2)
checkCrash()
#sleep(1)
clean()

首先会拷贝一个样本文件到工作目录

1
2
def copyFile(fileID):
shutil.copyfile(fileDict.get(fileID),workDir+"fuzz.flv")

然后开始一轮的fuzz

1
2
3
4
fuzzFilename = "fuzz.swf"
programName = "flashplayer_22_sa_debug.exe"
runCmd = programName +" "+ fuzzFilename
subprocess.Popen(runCmd)

然后是异常检测(贼搓的方法…TAT)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def checkCrash():
winDbg = "windbg.exe"
#get process list
try:
processList = psutil.process_iter()
except Exception as e:
print e
for p in processList:
if(p.name == winDbg):
print "[#]Crash Found! Writing to log now ..."
log(fileID)
sleep(1)
p.kill()
else:
pass

最后就是收尾的工作了

1
2
3
4
5
def clean():
subprocess.Popen(killProgram)#kill programName for next one
sleep(1)
if(os.path.exists(workDir+"fuzz.flv")):
os.remove(workDir+"fuzz.flv")

0x06: 结束语

我这个东西只能叫toy吧,效率低下,简单粗暴。但是过程中是学习到不少东西,之后的打算是多看一些论文,多学习一些漏洞挖掘的方法,之前尝试了结合winafl来搞,不过问题很多,有待解决…慢慢来吧。
所有的东西我都丢github了,有啥错误欢迎各位师傅留言/email指导我 传送门在这里:fuzz with peach

0x07: 参考

flv文件格式详解
peach 文档
Fuzzing with Peach – Part 1 « Flinkd!

文章目录
  1. 1. 0x00: 前言
    1. 1.1. 0x01: 关于peach
    2. 1.2. 0x02: 文件fuzz的思路
    3. 1.3. 0x03: 编写pit file
    4. 1.4. 0x04: swf加载样本
    5. 1.5. 0x05: 自动化fuzz脚本
    6. 1.6. 0x06: 结束语
    7. 1.7. 0x07: 参考
,