DVRF路由漏洞靶机中几道题的复现
项目地址:https://github.com/praetorian-inc/DVRF
使用binwalk提取固件DVRF_v03.bin
本地环境:kali-2022.2,qemu-7.0
stack_bof_02
栈溢出分析
使用ida分析,使用strcpy了函数,只要不出现\x00字符,就可以实现栈溢出。
启动程序:
1 |
|
关于溢出的字节数,我是直接用pwndbg观察栈上的地址和程序中的汇编计算出的:0x407ffdd0 + 0x214 - 0x407ffde8 = 0x1fc
由于没有后门函数,这里可以使用shellcode,
- MIPS不支持NX保护,写入栈中的shellcode可以直接被执行;
- 使用ROP劫持控制流,虽然程序gadget很少,但libc文件中有大量的gadget,且qemu模拟无法做到地址随机化,libc的基地址每次启动也都是固定的;
- 由于缓存不一致性,指令cache和数据cache两者的同步需要一个时间来同步,否则就会失效;这里需要调用sleep函数来让shellcode从数据cache刷新到指令cache,然后在跳转到shellcode去执行。
libc基地址的寻找:
查看puts函数的调用 ,然后在libc文件中找到偏移 0x3fefc420 - 0x17420 = 0x3fee5000 (不同的环境模拟出的地址也会有所不同)
编写ROP
这里我是参考H4lo师傅的文章去寻找的gadget
使用mipsrop找到的gadget跳转到指定地址大多都是通过另一个寄存器去赋值,而原程序中我们溢出后只能控制$ra寄存器,无法控制更多的寄存器;为了方便gadget的使用,首先溢出后劫持到scandir函数结尾部分,让我们可以控制更多的寄存器:
1 |
|
再使用mipsrop.find(“li $a0,1”)找到相应的gadget,将$a0赋值为1,作为sleep函数的参数,如果$a0本身就存在值也可以不用这个操作,可能sleep的时间会长一点:
1 |
|
调用sleep函数后还需要进一步调用shellcode,所以给$a0赋值值后不能直接去执行sleep函数,这里要进一步调用xdr_union函数结尾部分,执行完sleep函数后可以继续沿着ROP链执行:
1 |
|
最后使用mipsrop.stackfinders()找到获取栈地址的相对偏移的gadget;使用mipsrop.tail()或mipsrop.find(“”)找到跳转地址的gadget,通过偏移量写入shellcode,找出如下gadget:
1 |
|
完整的ROP如下:
1 |
|
最后调用该payload
题外话
H4lo师傅第一个版本的ROP通过mipsrop.stackfinders()是找到 0x000171CC
这一处的 gadget:
1 |
|
最后也是通过0x000214A0
的gadget跳转到shellcode,0x000171CC
处的 gadget获取栈地址的相对偏移是sp + 0x18,并依此写入0x18字节的数据填充,然后写入shellcode,而0x000214A0
处会有sw $v0, 0x18($sp)
的操作,最后会将shellcode的前4位字节赋值为$v0寄存器的值,如下图:
虽然最后也可以成功执行shellcode,但是对于后面的socket_bof这种题就无法成功;所以最好继续多写入4字节的数据填充,然后写入shellcode,这样就不会让shellcode上的数据被修改。
不过H4lo师傅第二个版本的ROP就没有使用该处的gadget。
socket_bof
使用ida查看伪代码:
read函数输入字符到v10上后,再由sprintf函数将v10的字符加上“nom nom nom, you sent me ”这串字符一同复制到v11上,最后的溢出也是v11的溢出,所以在调试时在栈上应找到如下地址去计算溢出长度:
溢出后的ROP可以直接使用上一题stack_bof_02的,最后将shellcode改为可以反弹shell的代码。
可以参考shell-storm上的代码,再改一下ip和端口即可
完整exp如下:
1 |
|
执行exp
socket_cmd
使用snprintf函数,控制大小为0x64,程序无栈溢出漏洞。将v10的字符直接拿去和“echo ”拼接后去作为system的参数,然后输出字符串;可以使用‘&’字符,当执行完输出后继续执行自己写入的命令。
就有如下exp:
1 |
|
也可以反弹shell
1 |
|
执行后: