0x00 简介
DVRF是IOT固件漏洞靶场。
下载项目https://github.com/praetorian-inc/DVRF后,使用binwalk将固件提取出来,主要存在漏洞固件在 squashfs-root/pwnable/Intro 中。
0x01 分析
这里主要看 stack_bof_1,通过file查看该文件属性:
可知这是一个32位的运行在MIPS中的ELF文件。
使用IDA pro 静态分析,可以看到存在一个危险函数 strcpy,且参数可控:
使用patternLocOffset.py生成300字节长度的随机字符串进行测试:
1 |
|
进行动态调试:
首先在装有QEMU的Linux虚拟机中,赋值qemu-mispel-static到squashfs-root目录中,通过-L参数指定根目录为本目录,-g指定远程调试端口启动该程序:
1 |
|
在IDA中进行远程调试,对参数的两个mov进行断点,F9运行到断点后,单步运行,查看a1值:
可以看到a1值就是输入的参数值,执行完strcpy后,a0中被复制成a1相同的值。
这里存在一个与x86或者x64不同的地方,MIPS的函数分为叶子函数和非叶子函数。在一个函数中,若没有调用其他函数,则此函数被称为叶子函数,反之则被称为非叶子函数。
叶子函数和非叶子函数在处理函数返回地址时有一个区别:
- 叶子函数的返回地址是直接放在 ra 寄存器中
- 非叶子函数把当前的返回地址暂时存放在栈上,再去调用函数内部的其他函数。会在代码中看到 lw $ra,xxx
因此在处理MIPS的栈溢出时,主要寻找非叶子函数的栈溢出,可以通过覆盖栈上保存的返回地址来控制程序执行流。当然叶子函数也不是完全没有可能做到栈溢出,如果在叶子函数上层是非叶子函数,若缓冲区足够大,可能可以覆盖到上层非叶子函数的返回地址。
代码流程下对 lw $ra, 0xE0+var_s4($sp) 下断点,查看覆盖情况:
RA被覆盖为 41386741,通过 patternLocOffset.py 搜索偏移:
得到偏移为204。
程序在 dat_shell 函数中有system(“/bin/sh -c”)这个后门函数,因此只需要将返回地址覆盖为dat_shell地址,即可完成利用。
dat_shell 地址为 0x00400950。
因此利用为:
1 |
|
但是继续运行仍然出现crash:
这里先科普一个MIPS的知识点:
MIPS的函数调用不像x86 call address就能实现,MIPS是使用t9寄存器保存函数地址,再通过jalr t9 实现函数跳转,如图:
而t9和gp这两个寄存器也有关系。gp指向的是64k(2^16)大小的静态数据块的中间地址,函数内部会通过gp来进行数据寻址等操作,而当函数调用时,又可能会存在对gp进行设置的语句,而这里的设置,就是通过t9,如dat_shell中:
通过上面的描述,就能知道crash的原因了,由于缺少正常函数调用的过程,t9寄存器中的值并不是函数地址,也就导致对gp的设置出了问题,从而影响程序对数据和地址的访问。因此就有两个方案解决这个问题:
- 直接跳过对gp的设置
- 完成正常的函数调用流程
0x02 利用
2.1 跳过
这里要跳过对gp的设置,因此把地址改为0x0040095c:
2.2 ROP
这里可以使用MIPSROP这个IDA pro插件去寻找可用的gadget:
1 |
|
但是并没有可用的gadget,因此通过readelf查看该文件有哪些动态链接库:
IDA pro 导入libc.so.0,misprop寻找可用gadget,但是不知道为什么我找不到,只能找到这一条,:
但实际上在00006B20 有合适的gadget:
将栈上第一块地址空间复制给t9后,跳转到 t9对应的地址执行。
这里就涉及到一个问题,定位libc.so.0的基址。
通常来说,通过gdb进行调试后,可以执行vmmap来查看具体内存布局, 但此处报错:
这里尝试另一个方法,涉及到一个概念:动态链接。
GOT:Global Offset Table,全局偏移表,包含所有需要动态链接的外部函数的地址
PLT:Procedure Link Table,程序链接表,包含调用外部函数的跳转指令(跳转到GOT表中),以及初始化外部调用指令(用于链接器动态绑定dl_runtime_resolve)
通过动态链接, 我们可以调用外部共享库中的函数,而不需要将其编译在可执行文件中。PLT和GOT就一起完成了动态链接的过程。
libc.so就是包含了许多函数的动态链接库,因此确定libc.so基址的方法就是确定一个libc.so的函数,动态调试找到函数地址,减去静态分析时的偏移地址。
以memset函数举例,即 libc_baseAddress = memset_Address - memset_offset
因此这里使用gdb进行调试
1 |
|
再用IDA pro 打开 lib.so.0,找到memset:
因此得到libc.so.0的基地址为 0x7f700e10 - 0x0001be10 = 0x7f6e5000。
因此gadget地址为 0x7f6e5000 + 0x6b20 = 0x7f6ebb20
因此PoC为:
1 |
|