令人疑惑的boofuzz

本文最后更新于:2024年1月6日 早上

令人疑惑的boofuzz

本文最后更新于:2023年8月22日 晚上

0x0: 写在所有之前

boofuzz这一基于生成的fuzz框架,笔者一直有所耳闻

且在笔者学习Iot后,对于能fuzz网络协议的boofuzz,更是产生的极大的兴趣。所以,笔者抱着学习boofuzz后能在Iot漏洞挖掘中提高效率的目的于最近浅浅的接触了一下boofuzz。

然后,没有然后了

给笔者整破防了,直接原地道心破碎

PS:笔者这篇文章主要是记一点无用的板子和吐槽,各位大师傅如果想看源码解析或者逻辑思路的可以点×了。

0x1: 还算正经的板子

笔者这边fuzz的target是Tenda AX3

首先要抓包抓一点原始数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
GET / HTTP/1.1

Host: 192.168.0.1

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.171 Safari/537.36

Sec-Purpose: prefetch;prerender

Purpose: prefetch

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7

Accept-Encoding: gzip, deflate

Accept-Language: en-US,en;q=0.9

Connection: close

其实根据这个就可以写原语了,但这个Request是login请求,没什么多大意义

于是笔者找了个存在漏洞的函数接口,先fuzz试试效果

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
from boofuzz import *


def main():
host = '192.168.0.1'
port = 80

# 设置 logging 配置
csv_log = open('fuzz_results.csv', 'w', encoding='utf-8') #python3要设置字符串,byte会报错
my_logger = [FuzzLoggerCsv(file_handle=csv_log)] #将日志写入csv文件

session = Session(
target=Target(
connection=SocketConnection(host,port,proto='tcp'),
),
receive_data_after_fuzz=True,#接受程序返回的数据
ignore_connection_reset=True,
restart_sleep_time=10,
crash_threshold_element=1,#会停止在第一次crash的地方
fuzz_loggers=my_logger,
)

s_initialize("Request")
#line1
s_static("POST",name="Method")
s_static(" ",name="space-1-1")
s_static("/goform/SetNetControlList") #漏洞存在接口
s_static(" ",name="space-1-2")
s_static("HTTP/1.1",name="HTTP_VERSION")
s_static("\r\n",name="Request-Line-CRLF-1")
#line2
s_static("Host:")
s_static(" ",name="space-2-1")
s_static("192.168.0.1",name="IP_ADDRESS")
s_static("\r\n",name="Request-Line-CRLF-2")
#line3
s_static("Content-Length:",name="Content-Length")
s_static(" ",name="space-3-1")
s_size('data',output_format='ascii',fuzzable=False)
s_static("\r\n",name="Request-Line-CRLF-3")
#line4
s_static("Accept:",name="Accept")
s_static(" ",name="space-4-1")
s_static("*/*",name="Accept-value")
s_static("\r\n", name="Request-Line-CRLF-4")
#line5
s_static("X-Requested-With:", name="X-Requested-With")
s_static(" ", name="space-5-1")
s_static("XMLHttpRequest", name="X-Requested-With-value")
s_static("\r\n", name="Request-Line-CRLF-5")
#line6
s_static("User-Agent:", name="User-Agent")
s_static(" ", name="space-6-1")
s_static("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.171 Safari/537.36", name="User-Agent-value")
s_static("\r\n", name="Request-Line-CRLF-6")
#line7
s_static("Content-Type:", name="Content-Type")
s_static(" ", name="space-7-1")
s_static("application/x-www-form-urlencoded; charset=UTF-8", name="Content-Type-value")
s_static("\r\n", name="Request-Line-CRLF-7")
#line8
s_static("Origin:", name="Origin")
s_static(" ", name="space-8-1")
s_static("http://192.168.0.1", name="Origin-value")
s_static("\r\n", name="Request-Line-CRLF-8")
#line9
s_static("Referer:", name="Referer")
s_static(" ", name="space-9-1")
s_static("http://192.168.0.1/wireless_ssid.html?random=0.7532778770368576&", name="Referer-value")
s_static("\r\n", name="Request-Line-CRLF-9")
#line10
s_static("Accept-Encoding:", name="Accept-Encoding")
s_static(" ", name="space-10-1")
s_static("gzip, deflate", name="Accept-Encoding-value")
s_static("\r\n", name="Request-Line-CRLF-10")
#line11
s_static("Accept-Language:", name="Accept-Language")
s_static(" ", name="space-11-1")
s_static("en-US,en;q=0.9", name="Accept-Language-value")
s_static("\r\n", name="Request-Line-CRLF-11")
#line12
s_static("Connection:")
s_static(" ", name="space-12-1")
s_static("close", name="Connection-value")
s_static("\r\n", name="Request-Line-CRLF-12")

s_static("\r\n")
#important key!!
with s_block('data'):
# line14
s_group("key1",['mac','list','ssid']) #危险参数,其中有一个参数能造成栈溢出
s_static("=")
s_string("korey0sh1", max_len=0x1000,fuzzable=True)


session.connect(s_get("Request"))
session.fuzz()

if __name__ == "__main__":
main()

具体怎么写框架笔者就不赘述了

在我指定漏洞位置(/goform/SetNetControlList)和危险参数的情况下,boofuzz效率确实还行,大概一分钟就跑出来了

可以看到在第1524个case DoS了

img

那说明1523个case发生了什么

img

很好,list参数造成了crash

笔者一开始还很兴奋,毕竟与AFL++的Qemu mode相比,boofuzz在fuzz Iot中无论是效率还是自由度高的不是一点半点,但是这是笔者定位好了漏洞所在位置的情况下。因为之前几个礼拜笔者对Tenda AX3小挖了一手,对历史漏洞自然是能很快的定位。但是,如果面对一个新设备,不同的功能函数接口有几十个,且不同功能对应的参数也不同,这时fuzz的效率就很令人质疑。

于是笔者便设置了3组group,每组group中设置了5个参数,将上面利用过的漏洞放入其中,类似

1
2
3
4
5
6
7
8
s_group("url",['url1','url2','url3','url4','url5'])
s_group("key1",[arg1','arg2','arg3','arg4','arg5'])
s_static("=")
s_string("korey0sh1", max_len=0x1000,fuzzable=True)
s_static("&")
s_group("key2",['arg1','arg2','arg3','arg4','arg5'])
s_static("=")
s_string("korey0sh1", max_len=0x1000,fuzzable=True)

但不幸的是,跑了5、6个小时,快100万条case,还是没跑出来

且就算跑出crash,也只是大致漏洞定位,具体的poc还是得人工仔细审。

0x2: 一些怨气碎碎念

神奇的callback

因为发现DoS后py程序并不能停止,还是会不停尝试连接,于是便抄了一个callback板子根据http的回显来判断服务是否停止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def check_response(target, fuzz_data_logger, session, *args, **kwargs):
# callback
fuzz_data_logger.log_info("Checking this response...")
fuzz_data_logger.log_info("We will receive 512 bytes data...")
try:
response = target.recv(512)
except:
fuzz_data_logger.log_fail("Unable to connect to target. Closing...")
target.close() # close this target (fuzzer's thread)
return
# if empty response
if not response:
fuzz_data_logger.log_fail("Empty response, target may be hung. Closing...")
target.close()
return
fuzz_data_logger.log_info("response check...\n" + response.decode())
target.close()
return

结果发现我POST都没发送呢你先执行callback了,于是乎整个程序在case 1就直接down掉了

img

我是SB,我真nm是SB,😝——>🤡

原来我以为callback是放session.connect里的,结果要放到session里就好了,我真TM是个SB啊

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
session.connect(s_get("Request"),callback=check_response)
|
|
|
\ /
session = Session(
target=Target(
connection=SocketConnection(host,port,proto='tcp'),
),
post_test_case_callbacks=[check_response],
receive_data_after_fuzz=False, #这个一定要关掉,不然callback那个recv就会出问题
ignore_connection_reset=True,
restart_sleep_time=10,
crash_threshold_element=1,
)

神奇的monitor

boofuzz 只提供了三种 monitor:

ProcessMonitor 大概是和 Procman 进行 rpc 通讯来监控;

NetworkMonitor 具体用法不太清楚,看文档里说用了 wireshark,ubuntu没装wireshark,hai

CallbackMonitor 是默认的 Monitor,提供回调函数的功能,就是前面哪个callback

然后笔者试了process monitor,结果跑不起来一点,后来一想process monitor也不怎么重要,实在不行用gdbserver拿case一跑就行了

神奇的log

想把每个case都写进文件,到时候打exp拿payload会方便。结果问gpt全是错的,后来总算找到了一个把log写进.csv文件的方法

1
2
3
4
5
6
7
8
9
10
11
session = Session(
target=Target(
connection=SocketConnection(host,port,proto='tcp'),
),
post_test_case_callbacks=[check_response],
receive_data_after_fuzz=False,
ignore_connection_reset=True,
restart_sleep_time=10,
crash_threshold_element=1,
fuzz_loggers=my_logger
)

Tips

对于某些要先登录的设备,可以这么写

1
2
3
4
session.connect(s_get('login'))     # 默认前置节点为root
session.connect(s_get('login'), s_get(action1'), callback=add_auth_callback)
session.connect(s_get('login'),s_get('action2'), callback=add_auth_callback)
session.connect(s_get('login'),s_get('action3'), callback=add_auth_callback)

0x3: 最后的最后

有失望,或许吧,开始学Iot的时候一直想寻找一个能全自动化固件模拟,漏洞分析的工具,然而,想想就有点不太现实(但Shambles这东西感兴趣的师傅可以了解下,铸币笔者因为实在太穷不是很了解,但感觉很diao的样子,md快6位数一年的费用想想就恐怖)。

现在我可能会倾向于用FirmAE、fat、fap这些模拟一遍然后再用qemu进行手动验证吧,当然有真机能实操那就太棒了。

扯远了,前几天觉得AFL对这些闭源的固件用处不是很大,现在看来boofuzz的作用也有限,😂

可能过几天会看看FirmAFL,好像手上还有一篇对应论文来着。

翻出来了三个月前想入门Iot时和Iot界的传说的聊天记录

img


令人疑惑的boofuzz
http://example.com/2023/08/26/boofuzz/
作者
korey0sh1
发布于
2023年8月26日
许可协议