fuzz libmodbus by AFL
下载编译libmodbus库
libmodbus库源码可从github仓库下载,命令如下:
1 | git clone https://github.com/stephane/libmodbus/ |
下载好后进入源码文件夹,编译命令如下:
需要注意的是:编译使用afl-gcc,而不使用默认的gcc
1 | cd libmodbus |
--enable-static
:用于生成静态库
注意:如果./autogen.sh
命令运行出错,提示 autoreconf not found 如下图,则表示缺少autoconf
。此外,还需安装libtool
。
命令如下:
1 | sudo apt install autoconf libtool |
安装完毕后,重新运行autogen.sh
后,会提示我们可以运行./configure
,如下图。
接着运行 ./configure
和 make
,执行成功则会看到如下界面,显示afl插桩提示。
安装Preeny库
为什么需要安装Preeny库?
Modbus协议依托socket实现进程间的通信,而AFL本身并未提供对 socket
通信的支持。使用 AFL 对其进行fuzzing 时,需要将其输入输出重定向 stdio
中。纵然可以修改部分代码使其 socket
通信转移到 stdio
,但这一过程可能会对 fuzz 的结果造成影响,同时工作量可能也较为繁杂。若是直接修改系统的 socket.h
,可能会对其他的程序造成难以估量的影响。
这个库利用 LD_PRELOAD
机制,重写了 很多库函数, 其中 desock.c 这个文件负责重写 socket
相关的函数,其实现的功能就是当应用从 socket
获取输入时,其实是从 stdin
获取输入。
安装Preeny的依赖
Preeny需要使用 libini_config 和 libseccomp 来实现相关功能,在安装 Preeny之前要先安装这两个依赖,否则在编译时汇错,安装命令如下:
1 | sudo apt install libini-config-dev libseccomp-dev |
编译安装Preeny
安装完Preeny需要的依赖后,进入到preeny目录下,make
即可。
安装成功后,可以在 preeny/x86_64-linux-gnu
目录下找到编译好的 desock.so
,可以通过一个简单的demo来测试 desock.so
是否正常工作,demo如下:
1 | /* demo.c */ |
编译运行
1 | gcc -o sock_test demo.c |
可以看到,send
函数成功将消息发送给了 stdout
,而 recv
函数也成功从 stdin
中接受了消息。
编译测试程序
在 libmodbus 目录下有一个 test 文件,包含几个官方提供的测试样例,可以通过afl-gcc对其进行插桩编译,作为被测程序。此处值得注意的是,在编译时要注意手动指定modbus库和头文件,命令如下:
1 | afl-gcc bandwidth-server-many-up.c -o server -I ../src/ ../src/.libs/libmodbus.a |
-I
后面的两个参数分别指定了 modbus.h
和 libmodbus.a
的路径,两者缺一不可。
生成测试用例
可以直接生成一串随机字符串作为 modbus 测试程序的输入,但这种随机生成的数据并不够 interesting。
在 libmodbus 提供的测试用例中,random-test-client 和 random-test-server 这对程序能为我们提供不错的测试数据,在 test/README.md 中对这两个程序的描述如下:
1 | - `random-test-server` is necessary to launch a server before running random-test-client. By default, it receives and replies to Modbus query on the localhost and port 1502. |
我们在本地运行这一堆程序,然后通过 tcpdump 工具将数据包保存到本地,从中挑选若干数据包作为输入测试样例,步骤如下:
首先使用 random-test-server 在 127.0.0.1:1502 起一个 modbus tcp 服务
server端:
1
./random-test-server
然后开启 tcpdump 保存数据包到目录
~/modbus.pcap
1
sudo tcpdump -i lo -w ~/modbus.pcap
最后使用 random-test-client 随机发送各种 modbus 请求到 127.0.0.1:1502
client端
1
./random-test-client
注:上述三个步骤命令分别在三个不同的终端下运行。
然后写一个脚本(源自参考博客)把 ~/modbus.pcap 中由客户端发送的数据包(也就是目的地址是 127.0.0.1:1520 的数据包)的内容提取出来,每个数据包内容保存为一个单独的文件。
1 | # python2 |
注意:如果向偷懒直接运行该脚本,则将该脚本放置在 modbus.pcap 同目录下。
运行脚本后,可以得到提取后的测试用例。
开始测试
现在,我们可以使用生成的数据集作为初始样本集进行 fuzz 了。这里最好使用 afl-cmin 来精简下数据集,直接使用的话被AFL判 uselee 的 testcase 挺多的。
fuzz命令如下:
1 | LD_PRELOAD="/home/guoxb/Downloads/preeny/x86_64-linux-gnu/desock.so" afl-fuzz -i /tmp/seeds/ -o out ./server |
不出意外的话,就进入熟悉的AFL界面了,如下:
参考博客
参考博客1中还提到了AFL的另一种模式 Persistent Mode,该模式在程序的某个代码位置不断喂生成的变异数据 进行 fuzz
, 而不用每次喂数据都得重新 fork
一个程序。要使用这个特性则需要编译 llvm_mode。
挖坑,留着以后再看。