这里跟大家聊一聊二进制文件保护中的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 | from pwn import * |
解法二:(pie低位覆盖,之后就是ret2libc套路)
1 | from LibcSearcher import * |
脚本解释补充:
1.’\x20’返回到fun函数处,我们可以看一下ida:
上图就是fun函数的汇编代码截图,由于开了pie,对应地址在程序加载时候都不是对应的,但是最后两位的地址都是一样的,调试结果:
我们把最低位覆盖成20即可,也可以验证一下pie的地址随机化,我们让程序启动第二次。
跟第一次fun函数起始地址不一样。但是低位一样,验证pie的特性。
2.为什么’__libc_start_main’-247为libc_base?
答:调试寻找到的,根据gef和栈中的数据。
直接上调试的图:
由于破坏了’\x00’终止,write函数会输出栈中的数据,直到遇到’\x00才会停止’,