前言
本文主要想说明当手头没有真机,并且一些已有的一键模拟仿真工具也不好用的时候,该如何来手动模拟固件执行,从而分析和验证我们通过静态分析所挖掘到的漏洞。从本篇文章的标题也能看得出,这次的重点并不是漏洞挖掘或分析,而是如何在现有工具不好用的情况下,一步步的成功将固件中的某一部分功能模拟出来
正文
固件版本:DIR-816
下载链接:http://support.dlink.com.cn:9000/ProductInfo.aspx?m=DIR-816
解包命令:
binwalk -Me DIR-816A2_FWv1.img
解包之后可以看到直接解出了文件系统,并没有什么加密手段
漏洞编号:CVE-2018-17066
首先来说一下我们这次选取的漏洞,是一个比较老的命令注入漏洞,位于/bin/goahead程序中,form2systime.cgi所对应的函数:
程序获取json数据中的datetime,拼接到date命令中,然后放进doSystem执行,而doSystem函数中也没有对所执行的字符串进行任何特殊字符的过滤或者检查:
有一定基础的人肯定能立刻知道这里存在一个命令注入漏洞,可以通过修改datetime字段的值,配合$或者其他特殊字符来实现远程执行命令
简要介绍完漏洞之后,我们来尝试模拟固件执行来验证与调试漏洞,首先肯定是尝试一些自动化的一键模拟仿真工具,这里先试试firmAE,是一个firmadyne的一个wrapper,集成了一些功能,在firmadyne的基础上变得更加的一键化
采用命令:
sudo '/home/iot/tools/FirmAE/run.sh' -r dir '/home/iot/Desktop/D-LINK/DIR-816/DIR-816A2.img'
等了很久很久也没反应,访问web端也访问不到,此时我们考虑,我们需要模拟的功能其实并不多,只需要能够正确执行goahead程序即可,如果要把程序改到能够通过一键模拟仿真脚本,可能需要做大量的patch,能不能直接利用qemu来只执行goahead呢?
首先我们来试试看
命令:
sudo qemu-mipsel -L ../squashfs-root/ ./bin/goahead
可以看到这里一直在等待nvram_daemon启动,每隔几秒打印一个点,一定时间之后程序退出了,显然我们是卡在了一个死循环里,我们呢来分析一下为什么会这样,将goahead程序拖进IDA中
从这里可以看到,这个while循环应该就是卡住我们的地方,从代码逻辑来看也的确对应了我们看到的现象,sleep一秒,然后打印一个点,15个点之后退出程序
可是现在我们显然是没有nvramd.pid这个文件的,那能不能通过动态调试的方式直接将这个v1变量改成1呢,来试一下
将启动命令改成如下:
sudo qemu-mipsel -L ../squashfs-root/ -g 1234 ./bin/goahead
然后ifconfig查看自己的网卡:
修改IDA的debugger设置
然后执行命令,在IDA中点击启动按钮开始动态调试
然后定位到刚才分析的while循环处的逻辑
针对v1的分支判断处的汇编语句打下断点
然后按F9运行到这里,在右上角的寄存器框中将v0寄存器修改为1
然后F9继续运行,结果debugger啪的一下子退出去了
此时到qemu启动终端那里看看log如何
可以看到刚才那里的逻辑确实通过了,但是又因为一些问题退出了,将log里的错误信息复制一下到IDA里搜索一番:
在这里成功定位到了最后出错的位置
走到报错逻辑显然是因为v17变量等于-1,而v17是inet_addr的返回值,inet_addr的参数是v14,且报错信息中将v14当做字符串打印了出来,此时可以通过log中,%s的位置为空串判断出,inet_addr返回-1的原因是v14不是一个合法的地址字符串
v14又是来自v13,v13来自nvram_bufget函数获取的lan_ipaddr字段的值,显然我们并没有提供这样的字段,如果这里直接将v17改成0而不是-1,能否绕过这里的检查呢?我们来试试
同样的方法启动qemu,然后把IDA的debugger连上去,不要忘记上一处的逻辑也要修改
修改好了以后按F9,IDA的debugger成功跑起来没有停下
此时我们可以访问web页面试试了
成功访问到了web界面,现在想要调试到位于时间修改界面的漏洞,还需要绕过登录这一关,先访问一下d_wizard_step1_start.asp试试,但是会跳回登录界面,看样子是必须手动绕一下这里了
在IDA中搜索login,找到这里
显然这里应该就是处理登录验证的函数
我们可以大致的来分析一下程序的登录验证逻辑,首先通过nvram_bufget获取到正确的username和password,然后通过websGetVar获取用户登录的时候输入的username和password
然后通过websDecode64分别将username和password解码并存到v10和v11里,这里猜测应该是在登录前端对username和password进行了base64加密后发包给后端。
接下来是两个strcmp函数,用来比较nvram获取的和用户输入的是否一致,这里我们考虑,既然是strcmp函数,且没有检测username和password是否为空,那我直接什么都不输入不是就绕过了吗,尝试一下
发现用户名必须输入,由前端js代码决定的,密码可以不输入,这里改前端或者动态调试改寄存器应该都可以,我是选择的在IDA中下断点手动将username修改成0来绕过
这意味着我们启动之前要在IDA中下好三处断点,前两处用来启动环境,第三处用来绕过登录验证,再来试一下
首先进到登录界面,然后输入用户名aaaa,点击登录,到IDA中看断点状态
可以看到成功断了下来,v0显然是strcmp的返回值,我们手动将v0改成0,理论上来说就可以绕过那段逻辑,修改过后继续F9,然后试着访问一下
可以看到现在已经成功进入了后台,接下来进入漏洞功能处
这种简单的命令注入想要验证的话直接抓个包就行,如何做呢,可以在dosystem里的system函数打个断点,然后抓包修改datetime进行命令注入,最后在system函数执行的时候查看是否成功注入即可
本次的文章就到这里,感谢阅读!