本环境是蛇矛实验室基于"火天网演攻防演训靶场"进行搭建,通过火天网演中的环境构建模块,可以灵活的对目标网络进行设计和配置,并且可以快速进行场景搭建和复现验证工作。
自 2005年国际电信联盟正式提出“物联网(IoT)”这一概念以来,物联网在全球范围内迅速获得认可,并成为信息产业革命第三次浪潮和第四次工业革命的核心支撑。同时,数以亿计的设备接入物联网,这些设备如今已渗透到我们生活的方方面面,从家居到工厂无处不在。一方面物联网设备使我们的生活更加便捷,而另一方面物联网安全事件频发,全球物联网安全支出不断增加。
当前,大量物联网设备及云服务端直接暴露于互联网,这些设备和云服务端存在的漏洞一旦被利用,可导致设备被控制、用户隐私泄露、云服务端数据被窃取等安全风险,甚至会对基础通信网络造成严重影响。从2018年全球统计数据来看,路由器、视频监控设备暴漏数量占比较高。
路由器暴漏数量超过3000万台,视频监控设备暴露数量超过1700万台,并且这些设备往后几年会一年比一年多,物联网安全的事件也会越来越多。由此,物联网安全行业需要大力发展,物联网安全人才的培养也刻不容缓。
蛇矛实验室在后续将利用火天网境系列靶场中的相关目标仿真和环境构建的特性,将持续发布关于“物联网安全-CVE实战分析”系列的文章来帮助大家入门物联网安全。
关于模糊测试
模糊测试(Fuzz Testing)是一种自动化的软件测试技术,最初是由威斯康辛大学的Barton Miller于1989年开发的,通常用于识别程序中的潜在漏洞。其核心思想是自动或半自动的生成随机数据输入到一个程序中,并监视程序异常,如***,断言 (assertion)失败,以发现可能的程序错误,比如内存泄漏。
模糊测试常常用于检测软件或计算机系统的安全漏洞。Boofuzz是一个基于python生成的协议Fuzz工具,它通过python语言来描述协议的格式。Boofuzz对协议的模糊测试有着良好的支持,且其代码开源,目前被广泛使用,但Boofuzz无法直接获取协议相关知识,需人工定义协议模型。这一小节,我们将使用Boofuzz实战测试vivotek摄像头栈溢出漏洞。
漏洞分析
下载好固件后,我们使用"binwalk 固件名"进行分析,发现固件并没有加密,并且文件系统是squashfs格式,固件中的数据都经过了xz压缩。
我们使用"binwalk -Me 固件名"递归提取里面的文件系统。
里面的目录比较多,我们可以使用”find . -name ’squashfs-root‘ “,也可以使用tree命令快速预览文件目录分布情况。这里我使用"tree -d -L 5"命令,递归查看5层以内的目录,然后就找到了"squashfs-root"文件目录。
还是和前面小节的步骤相同,我们进入到文件系统的根目录中,查看开机自启"etc/init.d/rcS"文件。
开机自启文件中,首先初始化系统,然后使用run-parts命令遍历执行"/etc/rcS.d"目录下的所有可执行脚本。
进入"etc/rcS.d/"目录下后发现执行脚本均为软连接文件,连接至"etc/init.d/"下的对应文件。我们在这里看到了httpd脚本,一般情况下路由器系统中的httpd为web服务程序,所以这里可能是启动httpd服务的脚本。
我们查看"/etc/init.d/httpd"文件,发现httpd程序为 "/usr/sbin/httpd"程序,并且执行参数为"-c /etc/conf.d/boa -d"。确定了路由器的服务器程序,我们进行漏洞分析。
这一小节的漏洞信息以及POC已经在[exploit-db]中详细的展示了出来。根据这里的漏洞详情我们知道了vivotek摄像头漏洞是栈溢出漏洞,触发的具体位置为"Content-Length"头。根据以上信息,我们将httpd程序放入IDA Pro进行分析。
IDA Pro分析完毕后,我们按"shift+F12"搜索Content-Length字符串,双击进入其存储变量地址。
鼠标点击"Content-Length"存储变量,按“x”可以找到该变量的交叉引用。发现sub_17F80函数引用了该变量。进入函数进行分析
函数获取由用户"POST或PUT"方法请求包中的"Content-Length"头位置并存入v33变量中。然后获取" "的位置赋值给v34,获取":"的位置赋值给v35变量。
随后使用strncpy函数从":"后面+1位置的字节一直复制到“ "字符前面一字节的位置,将所有的字节复制到dest变量,dest变量为4字节的char型数组。
由于开发者没有对“Content-Length”参数的值进行限制,导致攻击者可以伪造"Content-Length"头,写入大量不为"x00"的垃圾数据就可以导致栈溢出。
知道了漏洞成因以及触发位置,下面我们使用boofuzz进行fuzz测试,以下测试环境在ubuntu18.04中完成。
首先安装boofuzz:
Boofuzz 官方要求 Python ≥ 3.5,并且推荐安装方式为pip。那么我们先确保python和pip已经安装。
sudo apt-getinstall python3python3-pip python3-dev
Boofuzz官方建议我们在虚拟环境 (venv)中设置 boofuzz 。那么我们需要安装boofuzz的依赖
sudo apt-get installpython3-venv build-essential
创建boofuzz目录,并在其目录下启动虚拟环境env,使其与其他包隔离
mkdirboofuzz && cdboofuzz python3-mvenv env
这会在当前文件夹中创建一个新的虚拟环境 env。这里的虚拟环境中的 Python 版本是固定的,并且可以在创建时选择。与全局安装不同,虚拟环境中的虚拟环境python别名为 Python 版本。然后我们使用下面的命令来激活虚拟环境:
sourceenv/bin/activate
激活环境后,我们终端前面的标识多了一个"(env)",这代表我们在虚拟环境中操作。
然后安装最新版的pip和setuptools:
pip install-U pip setuptools
最后,安装 boofuzz:
pip installboofuzz
安装好以后,进入python3,测试导入boofuzz库不报错即为安装成功。
安装好boofuzz以后,我们就可以使用boofuzz框架进行fuzz测试了。我们需要编写一个fuzz脚本,脚本如下:
fromboofuzz import* IP = "127.0.0.1" PORT = 80 defcheck_response(target,fuzz_data_logger,session,*args,**kwargs): fuzz_data_logger.log_info("Checking for response ...") try: response = target.recv(512) except: fuzz_data_logger.log_fail("Unable to connect ...") return ifnotresponse: fuzz_data_logger.log_fail("Empty response ...") target.close() return fuzz_data_logger.log_info("Start checking ... "+response.decode()) target.close() return defmain(): session = Session( target=Target(connection=SocketConnection(IP,PORT,proto="tcp"), ), post_test_case_callbacks=[check_response], ) s_initialize(name="FUZZ") withs_block("Request-Line"): # Method s_group("Method",["POST"]) s_delim(" ",fuzzable=False) s_string("/cgi-bin/admin/upgrade.cgi ",fuzzable=False,name="URI") s_static("HTTP/1.1",name="Version") s_static(" ",name="CRLF") # Host s_static("Host") s_delim(": ",fuzzable=False) s_string("127.0.0.1",fuzzable=False,name="IP") s_static(" ") # Connection s_static("Connection") s_delim(": ",fuzzable=False) s_string("Close",fuzzable=False,name="Active") s_static(" ") # Content-Length s_static("Content-Length") s_delim(": ", fuzzable=False) s_string("65", fuzzable=True) # End s_static(" ") s_static(" ") session.connect(s_get("FUZZ")) session.fuzz() if__name__=="__main__": main()
上面的脚本***有俩个函数,一个main函数(脚本入口函数),一个check_response函数(回调函数)。
main函数中,首先我们创建一个session,session可以设置一些基础属性,我们可以使用Target设置我们要fuzz的目标的ip地址和端口号以及协议类型(这里我们要fuzz的目标为httpd服务器,即tcp协议)。Target还可以使用post_test_case_callbacks指定回调函数。
当我们发送fuzz的请求包后,boofuzz需要判断返回包的内容来确定是否具有漏洞特点,这里就需要用到回调函数。我们可以自己编写回调函数,这里我们将回调函数指定为check_response函数,回调函数的参数'fuzz_data_logger'记录测试检查和通过/失败。
boofuzz数据生成的特点为一个数据报文由Primitives和Blocks组成,多个Primitives可以组成Block,Blocks之间可以相互嵌套。所以我们需要初始化一个消息请求块,这里我设置请求块的名称为FUZZ,然后通过s_block设置对应的http请求数据。然后我们可以指定对应的字段选择fuzz,设置Content-Length参数的值为”fuzzable=True“。使用session.connect(s_get("FUZZ"))发送请求包裹,session.fuzz()开始驱动整个fuzz过程。
下面开始测试fuzz脚本,我们先模拟运行vivotek的httpd服务,访问后发现服务正常。
运行我们的fuzz脚本,蓝色字体的部分为脚本发送的request包数据,我们发现Content-Length参数后面的值确实是由boofuzz产生的fuzz数据。这里case 1中出现了红色字体的报错,为"Empty reponse ...",也就是说没有返回包。猜测服务器可能已经***,切换至模拟的路由器系统进行查看
果然,服务器已经***,说明我们的fuzz脚本是正确的,服务器不能正确处理Content-Length的垃圾数据,导致服务器***。
这时,我们可以访问localhost:26000可以观察boofuzz web服务中这次fuzz的详细信息。
确定了确实是有栈溢出漏洞后,我们写一个poc测试一下栈溢出偏移
正式运行路由器httpd服务,使用gdbserver进行attach,使用gdb-multiarch进行远程调试。
我们在0x18538(strncpy函数)处下断点,然后c运行,这时发送poc,程序断在了strncpy处。pwndbg中显示了strncpy函数执行时src和dest的地址。
拷贝到栈上时,0x7effeb4c处为返回地址,那么我们就算出了拷贝字符串到返回地址的偏移为0x34字节。下面我们开始写exp
编写exp的过程中,需要注意的点为strncpy复制时遇到"x00"停止,所以我们需要绕过"x00"找到可以用的rop gadget,然后通过已知的libc基地址找到system函数的地址运行命令。最终代码如下
动态调试整个过程,下断strncpy,没拷贝前栈空间正常
strncpy拷贝完成后,我们观察一下栈的空间分布
返回地址确实被我们覆盖成了gadget的地址,说明我们前面的分析正确。
漏洞复现
启动路由器后,运行我们的exp脚本
此时路由器系统中,已经运行了"nc -lp2222 -e /bin/sh",我们使用nc连接后就可以获得shell
使用nc连接2222端口后,随意运行命令,命令成功执行,本次攻击成功。
总结
这一小节,我们以vivotek摄像头为例,简短介绍了模糊测试框架Boofuzz,以及学习了如何利用该框架编写脚本进行IoT设备服务进行fuzz。
审核编辑:刘清
-
路由器
+关注
关注
22文章
3692浏览量
113405 -
python
+关注
关注
55文章
4766浏览量
84367 -
IOT
+关注
关注
186文章
4161浏览量
195937
原文标题:物联网安全实战从零开始-Vivotek的Fuzzing 栈溢出
文章出处:【微信号:蛇矛实验室,微信公众号:蛇矛实验室】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论