对linux下的ASLR保护的思考
对linux下的ASLR保护的思考
对ASLR的理解一阶段
参考我的这篇博客:https://sf2333.github.io/2022/02/01/CTF-pwn-%E6%8A%80%E6%9C%AF%E6%80%BB%E7%BB%93%EF%BC%883%EF%BC%89/
对ASLR的理解二阶段
全称为地址空间布局随机化(Address Space Layout Randomization)
仅仅是在 操作系统层面实现的,一般远程题目是默认开启的。但是在打本地的题目时我们可以手动关闭它,这样程序所有的地址都可以通过调试获得(包括代码段地址)。
但是开了PIE却没开ALSR会怎么样呢?
下面我们做实验探究一下:
实验程序:
它会打印代码段、BSS段、栈、共享库、堆的地址,我们通过多次运行这个程序,观察各个地址的变化情况,得出ASLR与PIE对程序地址的影响。
一个小细节:
没有启用-fpie -pie
选项进行编译的程序:
启用-fpie -pie
选项进行编译的程序:
再看看共享库文件:
我们可以知道开启PIE后,程序变成了一个类似共享库的文件,这使程序可以被加载到任意地址。
一个小疑问:为什么开了ASLR后在gdb调试栈地址还是保持不变?
答:gdb里默认关闭ASLR,每次起点gdb都需要手动输入aslr on
打开
1.alsr:0 + pie
可以发现所有地址都没有变化,说明PIE无效了!
2.alsr:1 + pie
可以发现所有地址都发生变化了,说明PIE和ASLR都生效了!
小问题:但是原来我们了解到的是aslr为1时并不会随机heap的地址,但在这个实验中却发现heap地址确实被随机化了,这是什么原因呢?
答:如果ASLR=1的话,即使按照ASLR定义这个级别似乎不会对heap基址随机化,但是由于开启了PIE使得executable的基址已经随机化了,所以heap的基址自然也就被随机化了。
3.alsr:2 + pie
可以发现也是所有地址都发生改变了,毫无疑问PIE和ASLR都生效了!
所以完整的ASLR与PIE搭配的表格:
ASLR | Executable | PLT | Heap | Stack | shared libraries |
---|---|---|---|---|---|
0 | x | x | x | x | x |
1 | x | x | x | O | O |
2 | x | x | O | O | O |
0+ PIE | x | x | x | x | x |
1+ PIE | O | O | O | O | O |
2+ PIE | O | O | O | O | O |
结论:
ASLR 不负责代码段以及数据段的随机化工作,这项工作由 PIE 负责。但是只有在开启 ASLR 之后,PIE 才会生效。故只要不开启ASLR我们就能通过调试获得程序中所有的地址。
为什么呢,因为一个是能力赋予,一个是真正使用能力。
这个是有历史原因的,ASLR刚开始设计的时候是作为操作系统功能提供的,只考虑了当时技术背景下executable加载后stack、heap、libraries的随机化功能,因而有多种绕过方式,这一时期的ASLR的定义也就成了“对stack、heap、libraries的随机化”。后来设计者就考虑executable的加载基址随机化以使得bypass失效,然而这一点在技术细节上需要编译器来实现,因此gcc开始支持PIE选项,使得编译出来的executable像是一个特殊的so,可以被操作系统加载到随机化的内存地址(只是可以),因而到这一阶段你会发现ASLR的定义变成了“对stack、heap、libraries、executable base的随机化”。然而你要将PIE出来的executable理解为特殊的so的话,原来的定义还是可以自圆其说的。