对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会怎么样呢?

下面我们做实验探究一下:

实验程序:

image-20220420095252127

它会打印代码段、BSS段、栈、共享库、堆的地址,我们通过多次运行这个程序,观察各个地址的变化情况,得出ASLR与PIE对程序地址的影响。

一个小细节:

没有启用-fpie -pie选项进行编译的程序:

image-20220420102211706

启用-fpie -pie选项进行编译的程序:

image-20220420102110456

再看看共享库文件:

image-20220420102424906

我们可以知道开启PIE后,程序变成了一个类似共享库的文件,这使程序可以被加载到任意地址。

一个小疑问:为什么开了ASLR后在gdb调试栈地址还是保持不变?

答:gdb里默认关闭ASLR,每次起点gdb都需要手动输入aslr on打开

image-20220419204730594

image-20220419204856923

1.alsr:0 + pie

image-20220420095712657

可以发现所有地址都没有变化,说明PIE无效了!

2.alsr:1 + pie

image-20220420095823087

可以发现所有地址都发生变化了,说明PIE和ASLR都生效了!

小问题:但是原来我们了解到的是aslr为1时并不会随机heap的地址,但在这个实验中却发现heap地址确实被随机化了,这是什么原因呢?

答:如果ASLR=1的话,即使按照ASLR定义这个级别似乎不会对heap基址随机化,但是由于开启了PIE使得executable的基址已经随机化了,所以heap的基址自然也就被随机化了。

3.alsr:2 + pie

image-20220420100235476

可以发现也是所有地址都发生改变了,毫无疑问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的话,原来的定义还是可以自圆其说的。

参考文章:https://www.cnblogs.com/rec0rd/p/7646857.html