有个未曾谋面的看雪的坛友问我一门关于IOT挖洞的课怎么样,因为课表里写的太笼统,所以我也就没给明确答复,不过我说我愿意免费教教我会的东西,另外之前关于我DIR-645漏洞文章结尾也说了要讲讲怎么fuzz出那个漏洞,于是便有了这篇文章
Ubuntu 20.04
Python、pip、qemu之类的直接用apt-get下载安装就好
IDA pro之前提供过下载链接
binwalk里有需要用到sasquatch程序,需要手动下载一下,命令如下
使用binwalk解包以后可以在bin文件夹下看到httpd程序,此时如果直接运行的话会卡在欢迎banner信息处

需要patch一些代码,修改判断逻辑
下图中红框内就是已经patch好的代码,点击Edit > Patch program > Apply pathes to input file > OK 即可保存

修改完成后再次运行依然会报错

这个错误主要是IP地址不正确,需要查看一下httpd服务具体是怎么获取IP的,需要从check_network函数开始查,这个函数是引用了第三方的lib库(至少我在Linux源码里没找到这个函数)

结果会找到libcommon.so文件,用IDA打开后可以看到依然是调用了别的so库代码



需要继续搜get_eth_name函数位置

有四个匹配,事实上是libChipAPI.so文件

从代码里可以看到程序在尝试读网卡信息,因为没有对应的网卡,所以程序IP地址会出错,所以这里需要手动创建一个br0网卡,并给一个IP地址(在创建之前建议先保存一个快照以防万一)
修改好后httpd程序就能正确运行了

此处需要抓包查看协议结构,但是因为只是普通的HTTP协议,我就直接给出boofuzz代码了
在开始之前记得用pip安装一下boofuzz,因为已经知道漏洞点了,所以很快就能跑出结果了

可以看到在password给出了一个非常长的值后程序崩溃了,验证其实也很简单,直接用Python脚本访问之前给br0的地址,端口是80,cookie中给一个超长的值就能复现崩溃了
最后在bLanguage这个字段也有个溢出,大家可以尝试修改上面的fuzz脚本复现一下
这是一个2017年爆出的贼老的栈溢出漏洞,不过用来学习boofuzz的使用还是不错的
首先使用binwalk解包固件后会有不少文件,文件系统在这个目录下
http服务用的是boa
这里有两个点需要修复
首先将宿主机中/etc/hosts文件夹中的内容全部复制到固件文件系统的/etc/hosts文件中去

然后将 _31.extracted/defconf/_CC8160.tar.bz2.extracted/_0.extracted/etc/ 目录直接拷贝到 squashfs-root/mnt/flash/ 目录中去,这一步主要是解决boa的config文件缺失问题

接下来直接用qemu命令运行httpd服务就行了

fuzz这个洞的脚本如下

几乎是一瞬间,boa服务就崩溃了,从输出信息来看,是因为 Content-Length 过长导致的,这也确实是这个洞的成因
boofuzz是个挺不错的对协议的fuzz工具,比AFL好在不需要参与编译过程,也就是说不需要收集代码覆盖率信息;缺点也很明显,需要对协议格式深入分析,且因为没有代码覆盖率信息,所以对未知代码的触发基本靠运气
这里使用这两个IOT固件且只fuzz了HTTP服务是因为这两个固件的环境问题相对好解决且HTTP服务fuzz起来相对简单,所以解决环境问题最好的方式还是直接买硬件
5faK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6^5P5W2)9J5k6h3q4D9K9i4W2#2L8W2)9J5k6h3y4G2L8g2)9J5c8Y4c8Q4x3V1j5#2x3o6f1@1i4K6y4r3N6r3W2E0k6g2)9#2k6W2)9#2k6U0p5K6x3e0q4Q4x3@1c8F1y4q4)9J5y4e0u0n7P5r3&6p5x3o6N6A6N6r3W2Q4x3U0f1K6c8r3Z5J5c8p5u0I4L8$3!0s2K9#2W2x3N6%4p5$3c8p5u0p5h3g2c8m8c8l9`.`.
e81K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2S2L8Y4q4#2j5h3&6C8k6g2)9J5k6h3y4G2L8g2)9J5c8Y4m8G2M7%4c8Q4x3V1k6A6k6q4)9J5c8U0p5^5y4e0x3K6y4R3`.`.
53cK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1L8r3!0Y4i4K6u0W2j5%4y4V1L8W2)9J5k6h3&6W2N6q4)9J5c8Y4y4G2L8X3N6Q4y4h3k6D9k6h3g2Q4x3V1k6S2M7Y4c8A6j5$3I4W2i4K6u0r3k6r3g2@1j5h3W2D9M7#2)9J5c8U0p5I4x3K6R3H3x3o6l9#2z5l9`.`.
sudo
git clone https:
//github
.com
/devttys0/sasquatch
cd
sasquatch
sudo
apt-get
install
build-essential liblzma-dev liblzo2-dev zlib1g-dev
.
/build
.sh
sudo
git clone https:
//github
.com
/devttys0/sasquatch
cd
sasquatch
sudo
apt-get
install
build-essential liblzma-dev liblzo2-dev zlib1g-dev
.
/build
.sh
find
.
/lib/
-name
"*"
|
xargs
grep
'check_network'
find
.
/lib/
-name
"*"
|
xargs
grep
'check_network'
find
.
/lib/
-name
"*"
|
xargs
grep
'get_eth_name'
find
.
/lib/
-name
"*"
|
xargs
grep
'get_eth_name'
sudo
tunctl -t br0 -u
sudo
ifconfig
br0 192.168.10.1
/24
sudo
tunctl -t br0 -u
sudo
ifconfig
br0 192.168.10.1
/24
from
boofuzz
import
*
IP
=
"10.10.10.1"
PORT
=
80
def
check_response(target, fuzz_data_logger, session,
*
args,
*
*
kwargs):
fuzz_data_logger.log_info(
"Checking test case response..."
)
try
:
response
=
target.recv(
512
)
except
:
fuzz_data_logger.log_fail(
"Unable to connect to target. Closing..."
)
target.close()
return
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
def
main():
session
=
Session(
target
=
Target(
connection
=
SocketConnection(IP, PORT, proto
=
"tcp"
),
),
post_test_case_callbacks
=
[check_response],
)
s_initialize(name
=
"Request"
)
with s_block(
"Request-Line"
):
s_group(
"Method"
, [
"GET"
])
s_delim(
" "
, fuzzable
=
False
, name
=
"space-1-1"
)
s_string(
"/goform/123"
, fuzzable
=
False
)
s_delim(
" "
, fuzzable
=
False
, name
=
"space-1-2"
)
s_static(
"HTTP/1.1"
, name
=
"HTTP_VERSION"
)
s_static(
"\r\n"
, name
=
"Request-Line-CRLF-1"
)
s_static(
"Host"
)
s_delim(
": "
, fuzzable
=
False
, name
=
"space-2-1"
)
s_string(
"10.10.10.1"
, fuzzable
=
False
, name
=
"IP address"
)
s_static(
"\r\n"
, name
=
"Request-Line-CRLF-2"
)
s_static(
"Connection"
)
s_delim(
": "
, fuzzable
=
False
, name
=
"space-3-1"
)
s_string(
"keep-alive"
, fuzzable
=
False
, name
=
"Connection state"
)
s_static(
"\r\n"
, name
=
"Request-Line-CRLF-3"
)
s_static(
"Cookie"
)
s_delim(
": "
, fuzzable
=
False
, name
=
"space-4-1"
)
s_string(
"bLanguage"
, fuzzable
=
False
, name
=
"key-bLanguage"
)
s_delim(
"="
, fuzzable
=
False
)
s_string(
"en"
, fuzzable
=
False
, name
=
"value-bLanguage"
)
s_delim(
"; "
, fuzzable
=
False
)
s_string(
"password"
, fuzzable
=
False
, name
=
"key-password"
)
s_delim(
"="
, fuzzable
=
False
)
s_string(
"ce24124987jfjekfjlasfdjmeiruw398r"
, fuzzable
=
True
)
s_static(
"\r\n"
, name
=
"Request-Line-CRLF-4"
)
s_static(
"\r\n"
)
s_static(
"\r\n"
)
session.connect(s_get(
"Request"
))
session.fuzz()
if
__name__
=
=
"__main__"
:
main()
from
boofuzz
import
*
IP
=
"10.10.10.1"
PORT
=
80
def
check_response(target, fuzz_data_logger, session,
*
args,
*
*
kwargs):
fuzz_data_logger.log_info(
"Checking test case response..."
)
try
:
response
=
target.recv(
512
)
except
:
fuzz_data_logger.log_fail(
"Unable to connect to target. Closing..."
)
target.close()
return
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
def
main():
session
=
Session(
target
=
Target(
connection
=
SocketConnection(IP, PORT, proto
=
"tcp"
),
),
post_test_case_callbacks
=
[check_response],
)
s_initialize(name
=
"Request"
)
with s_block(
"Request-Line"
):
s_group(
"Method"
, [
"GET"
])
s_delim(
" "
, fuzzable
=
False
, name
=
"space-1-1"
)
s_string(
"/goform/123"
, fuzzable
=
False
)
s_delim(
" "
, fuzzable
=
False
, name
=
"space-1-2"
)
s_static(
"HTTP/1.1"
, name
=
"HTTP_VERSION"
)
s_static(
"\r\n"
, name
=
"Request-Line-CRLF-1"
)
[注意]看雪招聘,专注安全领域的专业人才平台!