本文对DVRF路由器漏洞靶机中的一道题目(stack_bof_02
)进行了复现,详细记录了复现过程中如何利用gadget
片段来构造ROP
链进行攻击。
逆向分析
直接看main
函数:

漏洞点在第14行,strcpy
函数会把命令行参数argv[1]
复制到v4
中,造成栈溢出且argv[1]
可控。程序中并没有给出后门函数,只能执行shellcode
来攻击。
ROP构造
调用sleep函数
通常将shellcode
写到某个区域时,这块区域是作为数据缓存区来存放的,在执行shellcode
的时候该区域又必须是指令缓存区,但是在MIPS
架构中指令缓存区和数据缓存区的同步是需要一个时间的,也就是说在我们写入shellcode
后不能立即将其执行,需要一个sleep
来等待缓存区同步。
因为要先设置$a0
为1作为sleep
函数的参数,故可以找到如下gadget1
:

可知这段代码最后会跳转到$s1
中的地址,因此还需要一个gadget2,使在执行gadget1前可以控制$s1
为下一个跳转地址。
利用mipsrop.find("lw $s1,")
可以找到如下gadget2
:

先执行gadget2,设置$ra
为gadget1的地址、设置$s1
为目标地址,然后跳转执行gadget1,设置$a0
为1,最后跳转到$s1
中的地址。因为jalr
指令在跳转前还会将PC
加8
作为返回地址存入$ra
寄存器,这样我们在执行完sleep
后就不能继续控制程序流了,所以不能将$s1
设置为sleep_addr
来通过jalr
跳转。
还需要一个gadget3来,要求是可以通过jr
跳转到sleep
函数,并且可以控制$ra
寄存器(执行完sleep
会执行jr $ra
返回)。
利用mipsrop.tails()
可以找到如下gadget3
:

现在先执行gadget2,设置$ra
为gadget1的地址、$s1
为gadget3的地址、$s2
为sleep_addr
;然后jr $ra
跳转执行gadget1,设置$a0
为1;然后jalr $t9($s1)
跳转执行gadget3,设置$ra
为目标地址(目前未知用0xdeadbeef
替代)作为sleep
函数的返回地址;然后jr $t9(s2)
跳转执行sleep
函数,执行完后便能返回到目标地址。
构造的payload如下:
libc = 0x7f6ea420-0x17420 |
调用shellcode
接下来就是要执行shellcode
了,因为shellcode
会被布置到栈上,所以我们需要一个可以把栈地址写到寄存器里的gadget4,利用mipsrop.stackfinders()
进行搜索,可以找到如下gadget4
:

在执行gadget3时将$ra
设置为gadget4的地址,执行完sleep
函数便能返回执行gadget4,gadget4会把$sp+0x38+var_20
处的地址存储到$a0
中,因此需要把shellcode
写到$sp+0x38+var_20
处,接着jalr
跳转到$s0
中的地址,因此在执行gadget3的时候还需要把$s0
设置为下一个目标地址。
最后还需要一个gadget5用来跳转到$a0
中的地址,直接mipsrop.find("move $t9,$a0")
搜索即可:

这段gadget还会执行一个sw
将$v0
的值写到栈上0x30+var_18($sp)
处,调试的过程中发现这里正好是存储shellcode
的首地址,因此在布置shellcode
时多填充一个内存单元即可。
编写shellcode
时还需要注意由于溢出函数是strcpy
函数会有00
截断,所以shellcode
的机器码中不能含有\x00
。
exp
exp如下:
from pwn import * |
攻击效果如下: