DIR815路由器溢出
前言
固件下载地址:legacyfiles.us.dlink.com - /DIR-815/REVA/FIRMWARE/,下载1.01版本。
漏洞描述:
DIR-815 cgibi中hedwig_cgi函数中处理HTTP 头中 Cookie 字段中 uid 的值时存在栈溢出漏洞
binwalk解包
binwalk -Me firmware.bin |
解包后结构如下:
固件模拟(系统级)
架构定位
进入文件系统,查看固件架构:
file bosybox |
32为mips小端序:
模拟依赖下载
进行系统级模拟需要下载mips架构的内核镜像和文件系统:(因为是小端序,所以选择mipsel)
下载地址:Index of /~aurel32/qemu/mipsel (debian.org)
debian_squeeze_mipsel_standard.qcow2是文件系统,vmlinux-3.2.0-4-4kc-malta是内核镜像。
在宿主机中使用wget
命令下载,随便找一个目录下载 ,只不过这个目录在后面也要同时存放qemu
的启动脚本start.sh
:
wget https://people.debian.org/~aurel32/qemu/mipsel/vmlinux-3.2.0-4-4kc-malta |
启动qemu虚拟机
然后在该目录编辑qemu
启动脚本start.sh
:
|
给启动脚本加上可执行权限:chmod +x start.sh
运行过程中提示输入用户名和密码,都是root,即可登录qemu系统。
通信
为了实现宿主机和qemu虚拟机之间的通信,我们需要再宿主机上创建一个虚拟网卡。
安装依赖库:
sudo apt-get install bridge-utils uml-utilities |
在宿主机编写如下文件保存为net.sh并运行,也是放在之前start.sh
同级目录下:
!/bin/sh |
加上可执行权限并运行后,使用ifconfig
指令查看网卡配置是否成功。
然后需要把qemu虚拟机也接入这个虚拟网卡中,这样二者就能通信了。
在qemu
虚拟机中分别执行以下两条指令:
给虚拟机的eth0网卡分配静态ip地址 |
ok,在qemu中用ifconfig
查看配置:eth0网卡已经分配了我们定义的静态ip。
测试宿主机和虚拟机之间的通信:
宿主机:ping 192.168.100.2
qemu虚拟机中:ping 192.168.100.254
ok,都是可以正常连通的。
文件传输及虚拟机设置
可以通信后,我们在宿主机上使用scp
指令将解压出来的squashfs-root文件夹上传到qemu系统中的/root
路径下:
sudo scp -r squashfs-root/ root@192.168.100.2:/root |
然后在qemu虚拟系统中将squashfs-root文件夹下的库文件替换掉原有的(使用下面的脚本),此操作会改变文件系统,如果不小心退出了虚拟系统,再次启动qemu时会失败,原因是因为改变了文件系统的内容。此时需要使用新的文件系统,因此在此操作之前可以先备份一份。
在squashfs-root编写auto.sh
并执行:
!/bin/sh |
接下来在qemu虚拟系统的根目录( / )下,创建一个名为conf
的文件,此文件是httpd
服务的配置文件。内容如下:
Umask 026 |
启动httpd服务
在qemu虚拟机的根目录(/)下:./httpd -f conf
在宿主机的浏览器中访问:192.168.100.2:4322/hedwig.cgi
发现网页报错,这是因为hedwig.cgi
服务没有收到请求,需要提前配置qemu虚拟环境中的REQUEST_METHOD
等方法,因为httpd
是读取的环境变量,这里就直接通过环境变量进行设置。
在qemu虚拟机中的根目录下编写setenv.sh
脚本并运行:
!/bin/sh |
这里在qemu虚拟系统中运行hedwig.cgi
,再次访问http://192.168.100.2:4321/hedwig.cgi
就可以正常收到内容了:
到这里环境搭建完成,可以开始调试了。: )
调试
调试工具下载
在宿主机中安装异架构的gdb:sudo apt install gdb-multiarch
在qemu中使用gdbserver
来启动程序,首先也需要安装,在如下网址中找到编译好的异构gdbserver: https://github.com/rapid7/embedded-tools/tree/master/binaries
在宿主机中下载好上面红框中的gdbserver
后,使用scp
传到qemu虚拟机中:
sudo scp /mnt/hgfs/iot/gdbserver.mipsle root@192.168.100.2:/root/ |
gdbserver
的用法如下:
./gdbserver 远程gdb的IP:port ./test |
例如这里是用的:
./gdbserver 192.168.100.254:8888 /htdocs/web/hedwig.cgi |
确定栈溢出偏移
因为hedwig.cgi
是链接到cgibin
文件的:
所以只需要将cgibin
文件放到IDA中分析就行。通过查找资料和分析得知,程序的溢出点和HTTP_COOKIE
字段有关。通过查找字符串引用,在IDA中查看伪代码如下:
它存在于sess_get_uid
函数,getenv
获取变量信息,因此可以通过设置全局变量来控制此参数。
查看sess_get_uid
函数的引用,在hedwigcgi_main
函数中找到如下内容:
此处值得注意的是sprintf
将string和字符串拼接,放入到v27变量中,并未对长度进行检查。所以存在栈溢出。
总结一下函数功能就是:sess_get_uid
函数就是http
请求中cookie
字段中uid=
后的值进行了提取,然后通过sobj_get_string
进行简单检测之后给sprintf
,由于cookie
字段可控,所以可以构造payload
造成缓冲区溢出。
生成字符串序列:
cyclic 2000 |
复制下来之后粘贴到如下的调试脚本中,XXXXXXXXXXXXXXXXX的位置就是cyclic2000生成的乱序字符串。
在qemu虚拟机的根目录下编写启动脚本:
!/bin/bash |
运行脚本后,在宿主机依次使用如下指令进行调试:
gdb-multiarch |
发现程序出现段错误,错误地址为:0x646b6161
查看偏移:
cyclic -l 0x646b6161 |
到此为止已经确定偏移为1009.
修改exp.sh
脚本,我们验证以下是不是1009:
!/bin/bash |
在gdb-multiarch
中下断点后调试:断点位置是0x00409A28
函数返回前我们可以控制s0-s8的寄存器,这也会是我们之后利用的重点:
可以看到最后会调用0x42424242
,所以确定偏移就是1009.
ROP构造
目的是为了劫持返回地址,调用libc中的system。但为了避免cache incoherency机制,这里使用system构造反弹shell,而非直接调用shellcode。首先要确定可以调用system的libc,使用vmmap查看得知为libc.so.0:
当然为了使libc
基址固定,我们需要关闭qemu虚拟机的aslr保护:
echo 0 > /proc/sys/kernel/randomize_va_space |
这样libc
基址就是0x77f3400
。而libc.so.0
是链接到libuClibc-0.9.30.1.so
:
所以我们把libuClibc-0.9.30.1.so
放入IDA中。
我们先找到system
的基址0x53200
:
ok,接下来就是利用IDA寻找gadget
。
复制以下代码到ida的plugins目录中,并命名为mipsrop.py:
ida/plugins/mipsrop/mipsrop.py at master · grayhatacademy/ida · GitHub
修改82行from shims import ida_shims
为import ida_shims
。
复制以下代码到ida的plugins目录中,并命名为ida_shims.py:
import idc |
之后在idapython输入框中输入:
import mipsrop |
然后输入mipsrop.find("")
即可查询可用的gadget。
gadget1
根据大佬博客:由于基地址为0x77f34000,system地址为0x00053200,两者相加会出现00造成截断,为了利用,可以先将system-1,避免截断的问题。
因此我们查找addiu $s0,1
指令,选用gadgets1
:0x158c8
因为我们可以控制s0-s8寄存器,利用这个gadget1
,我们将system_addr-1
传给s0寄存器。由于mips架构cpu的流水线并行机制,当我们执行jalr $t9
时,cpu也会执行addiu $s0,1
,这里就到了真正的system_addr
。
gadget2
现在我们还需要给system
函数传参。
利用mipsrop.stackfinder
,选用gadget2
:0x159cc
。因为其既可以跳转至system
函数,又可以通过s5给system
函数传参:
这个gadget2
也会有流水线机制。
exp
有了上面两个gadget之后,整体流程如下:
- 劫持地址–>0x158c8(给s0赋值为
system
函数地址,跳转至s5) - 0x159cc(给system函数传参并跳转执行)
在宿主机中编写payload.py
:
from pwn import * |
运行后生成context
文件,将congtext上传到qemu,然后运行hedwig.cgi服务:
!/bin/bash |
运行这个exp.sh
,然后再宿主机中监听:
nc -lvnp 9999 |
最后可以得到shell:
反思总结
- 提高仿真能力,仿真永远是找漏洞第一步,并且会比较繁琐,虽然也有现成的工具可以使用,但是我认为应该学习qemu用法,以应对更加复杂的环境
- 复现要耐得住寂寞,复现一个漏洞可能时间会很长,但是希望不要中途放弃,不然学到的东西会大大减少
- 多上网查找资料,看看大佬们的博客
参考: