本文对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 * |
攻击效果如下:
