欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

路由器漏洞复现:从原理到第一步验证

来源:本站整理 作者:佚名 时间:2018-07-25 TAG: 我要投稿

物联网的漏洞复现和传统系统的漏洞复现的不同点在于,物理网漏洞依赖于硬件,几乎每一个漏洞都得买一个新的硬件复现,这不同于传统系统只要下载好正确的对应版本软件即可。因此,在本地的虚拟环境中复现漏洞是一个极其经济的做法。且在正式向实际硬件复现漏洞之前,在本地虚拟环境中复现将有利于调试实际环境中的不可预期问题。
本文因此以路由器漏洞D-Link DIR-505为例,介绍如何在本地虚拟机中完成漏洞复现。在《揭秘家用路由器0day漏洞挖掘技术》里面的第12章,介绍了如何挖掘以及利用D-Link DIR-505的漏洞。但是书本里面介绍的方法,如果要在本地的QEMU虚拟机里面执行的话,有问题,因为在使用bash脚本输出执行的时候,会自动过滤掉null 字符,然后一个关键的调用system函数的地址就包含了null字符,因此使得在本地QEMU的验证失败。本文与书本里面的方法不同,利用调用外部库libc.so.0里面的system函数,并且在libc.so.0里面寻找可利用的gadget,来实现在本地QEMU虚拟机环境里面,对于D-Link DIR-505的漏洞利用,无需在实际的设备中测试。如果需要在实际的设备中测试的时候,仅需要改变代入库函数的基地址就可以。
在本文中,我将介绍:1.如何调用从共享库libc.so.0里面调用system函数,2.如何确定共享库libc.so.0的基地址。其中,对于2,我提供了两种方式,其实还有第三种方式,但是我还尚未验证,理论上也是可行的,也将在文后提出来。
下面进入正题。前提是假设读者已经拥有了搭建好的QEMU环境。
 
1. binwalk 固件提取
首先老规矩,利用binwalk 将下载的Dlink固件提取:
$ binwalk -Me DIR505A1_FW108B07.bin
 
2. 分析漏洞关键位置
复用书本中对于公布的漏洞细节的分析,可以发现漏洞出现的关键位置,在根目录的/usr/bin/my_cgi.cgi中。且关键输入源是storage_path=xx. 虽然CONTENT_LENGTH对于读取的storage_path的内容长度有所限制,但是对于CONTENT_LENGTH的数值没有限制,导致了实际上任意长度的字符串内容都可以被读取,导致了栈溢出的漏洞。详细的分析可以参考书中的第12章,这里不再赘述。
 
3. 分析RA偏移
接下来就是分析能够对于函数返回寄存器RA产生溢出的偏移地址位置。使用书本提供的patternLocOffset.py脚本生成字符串匹配脚本,来计算偏移。
$ python patternLocOffset.py –c –l 600 –f dir505test
[*] Create pattern string contains 600 characters ok!
[+] output to passwd ok!
[+] take time: 0.0026 s
 
4. QEMU运行漏洞程序
接着使用如下脚本来使得my_cgi.cgi在QEMU的环境下执行:
# sudo bash my_cgi_test.sh
# INPUT=`python -c "print 'storage_path='+open('dir505test','r').read()"`
INPUT=`python -c "print 'storage_path='+'B'*477450+open('dir505test','r').read()"`
# LEN=$(echo -n "INPUT" | wc -c)
((LEN=477472+0x100))
PORT="1234"
if [ "$LEN" == "0" ] || [ "$INPUT" == "-h" ] || [ "$UID" != "0" ]
then
    echo -e "usage: sudo bash my_cgi_test.sh"
    exit 1
fi
cp $(which qemu-mips-static) ./qemu
echo "CONTENT_LENGTH" + $LEN
echo "$INPUT" | chroot . ./qemu -E CONTENT_LENGTH=$LEN -E CONTENT_TYPE="manultipart/form-data" -E SCRIPT_NAME="common" -E REQUEST_METHOD="POST" -E REQUEST_URI="/my_cgi.cgi" -g $PORT /usr/bin/my_cgi.cgi 2>/dev/null
echo "youtest"
rm -f ./qemu
这里要特别注意的有两点:一点是,CONTENT_LENGTH最好自己手动指定长度,原始书本里面提供的脚本设置的CONTENT_LENGTH长度是不对的,它显示只是bash脚本参数的个数(一般来说就是3或者5),那这样的话,就始终无法读取到我们之后所有设置的payload内容,这个部分我一直卡住了很久,而书本中也未曾提到这一点,希望各位小伙伴在复现的时候一定注意。所以我们这里设置((LEN=477472+0x100))。 之后LEN会赋值给CONTENT_LENGTH。
另一点,storage_path这里要先覆盖掉整个全局变量的长度,这个部分类似于书本中分析,可以发现memset(entries, 0 , 477450)的477450长度,所以我们设置477450的预先覆盖长度。
 
5. 运行脚本之后,使用IDA挂载程序,在返回RA处设置断点,查看RA的数据。

可以发现覆盖的字符串内容为61374161, 那么我们来查找一下:
$ python patternLocOffset.py -s 0x61374161  -l 700
[*] Create pattern string contains 700 characters ok!
[*] Exact match at offset 22
[+] take time: 0.0012 s
发现是第22个偏移。那么如果想要覆盖到RA的话,前面总共的偏移量为477450+22=477472.
 
6. gadget寻找与利用
按照书本原来的做法,直接找到在my_cgi.cgi里面的一个gadget 地址为0x00405B1C,这个地址是调用system函数的地址,然后在对应的位置覆盖要传入的CMD就可以。但是前面提到过,如果用bash脚本的话,会把null字符过滤掉,导致地址无法正确输入,0x00405B1C中包含了一个null字符0x00. 所以我们下面开始要寻找共享库函数libc.so.0里面的system函数,并且也同时在libc.so.0利用寻找可以利用的gadget来实现system函数调用、传参。
为了完成上述任务,需要完成以下步骤:A. 寻找libc.so.0的基地址B. 寻找system函数的位置C. 寻找libc.so.0里面可以利用gadget
 
7. 寻找libc.so.0的基地址
本文提供两种方法:利用读取proc文件的方式,和利用gdb调试的方式。
第一种,利用读取proc文件的方式。
在运行了bash脚本,且用IDA挂载之后,运行ps –ef查看对应进程的id

[1] [2] [3] [4]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载