1. 过程概览
- 下载编译libmodbus
- 利用 preeny 库辅助fuzz
- 获取样本数据,数据处理
- 启动afl
2. 具体步骤
2.1 下载编译libmodbus
libmodbus库是一个用于 modbus 通讯的库,通过这个库可以很方便的实现 modbus 服务器和客户端的通讯。
下载地址:https://github.com/stephane/libmodbus
首先下载好源码,目录下的 tests 目录里面有一些示例程序。
然后编译 modbus server,使用 afl-gcc 编译 libmodbus ,对 libmodbus 插桩。
unzip libmodbus-master.zip
cd libmodbus-master/
./autogen.sh
CC=afl-gcc CXX=afl-g++ ./configure --enable-static
make -j4
然后在 src/.libs 下就可以看到编译好的库
ls src/.libs/
libmodbus.a libmodbus.la libmodbus.lai libmodbus.so libmodbus.so.5 libmodbus.so.5.1.0 modbus-data.o modbus.o modbus-rtu.o modbus-tcp.o
2.2 利用penny库辅助
因为 afl 默认只能 fuzz 通过 stdin 和 文件 获取输入的程序, 要 fuzz 网络相关的程序,需要使用一个库penny。这个库利用 LD_PRELOAD 机制,重写了 很多库函数, 其中 desock.c 这个文件负责重写 socket 相关的函数,其实现的功能就是当应用从 socket获取输入时,其实是从 stdin 获取输入。
首先下载编译
git clone https://github.com/zardus/preeny.git
cd preeny/
make
然后会在 x86_64-linux-gnu 目录下生成编译好的 lib 。
写个测试脚本,测试一下 (根据 tests 目录里面的 sock.c 改造)
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main()
{
int s = socket(AF_INET, SOCK_STREAM, 0);
char buf[1024]={0};
char send_msg[] = "hello, send by send() :\n";
send(s, send_msg, strlen(send_msg), 0);
recv(s, buf, 1024, 0);
printf("recv from recv() : %s\n", buf);
}
编译运行
gcc test.c -o test
LD_PRELOAD="/home/路径/preeny/x86_64-linux-gnu/desock.so" ./test
往 socket 调用 send , 成功往 stdout 输出了 字符串。
从 stdin 输入 xxx ,可以看到成功写入 buf 里面。
2.3 获取样本数据
一组好的样本数据对 fuzzer 的影响还是非常大的,一般我们可以去网上搜索样本,比如图片,视频文件等。对于我们这次的目标 libmodbus , 它自带了很多的测试程序,我们可以利用这些测试程序测试,然后用 tcpdump 抓包, 最后在把其中的请求数据保存下来,作为测试样本集。
首先使用 random-test-server 在 127.0.0.1:1502 起一个 modbus tcp 服务
cd tests/
./random-test-server
然后开启 tcpdump , 保存数据包到 指定路径/modbus.pcap
sudo tcpdump -i lo -w ~/modbus.pcap
最后使用 random-test-client 随机发送各种 modbus 请求到 127.0.0.1:1502
cd tests/
./random-test-client
然后写一个脚本把 ~/modbus.pcap 中由客户端发送来的数据包的内容提取出来,把每个数据包内容保存为一个单独的文件。
#transfer.py
from scapy.all import *
save_path = "./seeds/"
uuid = 0
if not os.path.exists(save_path):
os.system("mkdir %s" %(save_path))
def save_to_file(data):
global uuid
with open("{}{}".format(save_path, uuid), "w") as fp:
fp.write(str(data))
uuid += 1
print "write test file: {}".format(uuid)
modbus_session = ''
pg = rdpcap("modbus.pcap")
session = pg.sessions()
for k in session.keys():
if k.endswith("127.0.0.1:1502"):
modbus_session = session[k]
for s in modbus_session:
payload = s[TCP].payload
if len(payload) > 4:
save_to_file(payload)
print "Total: %d tests" %(uuid)
2.4 启动fuzz
无样本
直接用 echo 生成了一个测试文件,如果直接用这个去测的话会发现速度非常的慢。
afl-gcc bandwidth-server-one.c -I../src ../src/.libs/libmodbus.a -o server
mkdir in
echo 11111 > in/1
LD_PRELOAD="/home/路径/preeny/x86_64-linux-gnu/desock.so" afl-fuzz -i in -o out ./server
有样本
然后以生成的样本集作为初始样本集进行 fuzz
LD_PRELOAD="/home/路径/preeny/x86_64-linux-gnu/desock.so" afl-fuzz -i /seeds/ -o out ./server
速度有一定的提升,而且总路径数直接 1000+