CVE-2018-8941分析

Author Avatar
Xzhah 10月 31, 2019
  • 在其它设备中阅读本文章

[TOC]

文章首发于先知社区 :https://xz.aliyun.com/t/6607

cve-2018-8941分析

想开始入门搞搞路由器,选择cve-2018-8941入门。因为感觉网上能找到的资料不是太详细,所以想写一篇新手入门向的记录一下。

漏洞信息:https://github.com/SECFORCE/CVE-2018-8941

参考:https://www.freebuf.com/articles/wireless/168870.html

固件下载: ftp://ftp.dlink.eu/Products/dsl/dsl-3782/driver_software/DSL-3782_A1_EU_1.01_07282016.zip

漏洞信息

在/userfs/bin/tcapi 二进制文件中存在栈溢出漏洞,tcapi是一个被用作Web GUI中”诊断”功能的包装。

现实世界中触发该漏洞需要通过身份验证,经过身份验证的用户可以使用”set Diagnostics_Entry”功能将一个长缓冲区作为”Addr”参数传递给” /user / bin / tcapi”二进制文件,并导致内存损坏。进一步可以覆盖返回地址,劫持控制流执行任意代码。

详细分析

首先用firmware-analysis-toolkit进行一个仿真模拟,在里面发现了Diagnostics Entry Address的接口

但是无法输入造成栈溢出的数据(因为在前端有校验),所以通过burpsuite发送造成栈溢出的数据。

把burpsuite发送的内容改成栈溢出的数据,可以看到存返回地址的ra寄存器已经被覆盖了

路由器也崩了

现在回到具体漏洞点,首先运行tcapi文件,用qemu-mips-static来运行程序以及进行调试。

我们要运行的是set 功能 后面应该跟三个参数,正常来说应该是这样的:sudo chroot . ./qemu-mips-static userfs/bin/tcapi set Diagn
ostics_Entry Addr www 或者IP (如192.168.100.1)

我们使用IDA进行调试来看一下,给qemu提供了调试功能,-g 参数加上端口号就可以用gdb或者IDA的remote gdb 进行调试了。

可以看到程序调用tcapi_set函数,该函数位于libtcapi中实现。

而tcapi_set中的strcpy调用没有检验长度导致了栈溢出的发生。且简单计算可以得知存放返回地址的栈距离第三个参数在栈上的偏移是596

我们令第三个参数为’A’*596+’BBBB’,实际调试的时候可以看到,当栈溢出发生后,存放返回值的寄存器的值变为了42424242(‘BBBB’)

已经可以覆盖返回地址了,那么接下来怎么执行我们自己的命令呢?checksec可以看到什么保护都没开,那首先考虑写shellcode?本来我的想法是找到类似jmp esp这样的指令然后在后面写Shellcode就行了。这个打算后面花时间看看mips有没有类似的指令,或者看看有没有哪里可以泄露栈地址?

老老实实构造rop链,然而发现tcapi这个程序里没有合适的gadget,只能去libc里找了,可以通过readelf -d 判断程序的依赖库

我们选择libc.so.0

首先我们可以控制的是s0-s3,ra,使用mipsrop查找符合条件的gadget

我们选择16710偏移处的gadget,最终调用的函数地址由s0寄存器决定,而参数是sp+24,所以我们只需要让libcbase+16708覆盖$ra寄存器,system实际地址覆盖s0寄存器,和esp+24。

那么为了测试,我们需要获得libc基址,原Poc作者说”since we are exploiting through the WEB GUI, binary process mappings (/proc/pid of boa/maps) were obtained from ‘/userfs/bin/boa’ binary”,意思是运行boa,然后通过cat proc/pid/boa 获得libc基址吗?但是经过我的测试发现并不能对应上,我拿到基址的方法是通过给qemu 加上-strace参数,然后看系统调用得到的。

这里我也不太确定,我看到open libc.so.0过后用read读进了0x40867000,把0x40867000+offset带进IDA里去找,稍微调整一下发现了0x40868000是libc基址。

所以最后我们的payload就是

1
2
3
4
5
6
7
8
9
10
import struct
libc_base = 0x40868000
libc_system = struct.pack(">I",libc_base+0x59bb0)
rop_pad = 'A'*580
s0 = libc_system
s1 = 'BBBB'
s2 = 'BBBB'
s3 = 'BBBB'
ra = struct.pack(">I",libc_base+0x16708)
payload = rop_pad + s0 + s1 + s2 + s3 + ra + "C"*24+'ls'

最后使用qemu触发就行了