使用 AFL++ 对 IoT 二进制文件进行模糊测试 - 第二部分
在上一部分中,我们研究了如何使用 AFL++ 对简单的物联网二进制文件进行模糊测试。这些程序接受来自文件的输入,并且易于模糊测试。
在本文中,我们将研究套接字二进制文件。使用套接字进行网络通信的模糊测试二进制文件与使用基于文件 I/O 的模糊测试二进制文件不同。Vanilla AFL 和 AFL++ 不支持对套接字二进制文件进行模糊测试,尽管AFLNet和AFLNW等项目使用 AFL 的修改版本进行模糊测试。不过,本文我们将了解如何使用普通的 AFL++ 来模糊测试网络程序。httpd
此处的二进制文件/usr/sbin/httpd
是固件的 Web 服务器,可以作为模糊测试的候选对象。
httpd
我们可以像下面这样启动sudo
。需要使用 Sudo 才能绑定到 80 端口。
请注意,qemu 是从www/
目录内部启动的,因为这是 Web 资源(html、css、js 文件)所在的位置。虽然它显示了绑定错误,但运行后netstat
可以确认它httpd
确实在监听 80 端口。
我们可以打开http://127.0.0.1来交叉检查Web界面是否可以访问。
还可以使用 访问 Web 界面curl
。
使用拦截代理(例如 Burp Suite),我们可以查看正在发送的实际 HTTP 请求。尝试使用凭据登录仪表板admin:123456
将导致 POST 请求,如下所示。
在上图中,我们通过附加*-p 8080
*到 qemu 命令行在端口 8080(而不是 80)上运行 Web 服务器。
从这里开始,我们的想法是使用模糊器以微妙的方式修改这个基本请求,从而使 Web 服务器崩溃。
最简单的方法是通过网络发送实际请求。然而,这会很慢。更聪明且推荐的方法是让 Web 服务器从文件中读取 HTTP 请求数据。我们将分别讨论这两种方法。
使用 Radamsa 进行简单模糊测试
Radamsa不是模糊测试器。它是一个测试用例生成器,可以读取文件并以微妙的方式对其进行修改。如何使用修改后的输出取决于我们。在这里,我们将文件的输出发送到正在运行的 Web 服务器。
# fuzz-radamsa.py
import socket
import pyradamsabase_login_request = open("base-login-request.txt", "rb").read()rad = pyradamsa.Radamsa()
i = j = 0while True:# Create a modified request based on the base requestfuzzed_request = rad.fuzz(base_login_request)sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 1 second timeoutsock.settimeout(1)sock.connect(("127.0.0.1", 8080))j += 1print(f"[+] Request {j} - ", end="")sock.sendall(fuzzed_request)try:sock.recv(50000)print("OK")except Exception as ex:i += 1open(f"interesting/{i}.txt", "wb").write(fuzzed_request)print(f" {ex} -> saved to {i}.txt")sock.close()
上述代码使用 Radamsa 根据基础登录请求生成修改后的请求数据。然后,这些数据通过套接字发送到运行在 8080 端口的 Web 服务器。如果服务器在 1 秒内没有响应,则输入将保存到目标目录中的文件中。
我们可以按照所示运行模糊测试器。
请求 3 在响应时超时,相应的输入被保存到1.txt 文件中。需要注意的是,超时并不等同于崩溃。如果服务器在请求 3 时崩溃,后续请求将无法成功。这种模糊测试效率极低、速度慢且容易出错,并且经常会导致误报。
使用 AFL++ 进行模糊测试
如前所述,要使用 AFL 进行模糊测试,程序必须接受来自文件的输入。我们没有httpd的源代码,无法根据我们的目的进行修改。因此,我们只能采取二进制级别的修改,例如修补汇编指令和LD_PRELOAD
技巧。使用后者,我们可以覆盖网络函数,libc
使其接受来自文件的输入。GitHub上的desockmulti项目可以用于此目的。
在演示如何使用*desockmulti 之前,*我们需要进行一些修改。httpd
二进制文件目前使用该函数 fork 到后台daemon
。我们不希望在模糊测试过程中出现这种 fork 行为。
我们需要重写daemon
它,使其返回 0,而不是真正地进行分叉。这可以通过使用 LD_PRELOAD 或修改汇编指令来实现。
我们需要做的另一个修改是让httpd只处理一个请求(不像典型的 Web 服务器那样无限期地处理请求),然后退出。这样我们就能知道是哪个请求(如果有的话)导致了 Web 服务器崩溃。
要关闭套接字