讲讲 ASLR 怎么绕过
ASLR 的核心思想
首先,我们简要回顾一下 ASLR 的原理。ASLR 是一种漏洞缓解机制,它的核心思想是将程序在内存中的关键区域(如可执行文件、共享库、堆和栈)加载到随机的地址
没有 ASLR 时,每次运行程序,其内存布局都是固定的。攻击者可以精确地预测函数和变量的地址,然后利用漏洞(如缓冲区溢出)来劫持程序流,跳转到这些预先知道的地址
有了 ASLR,程序每次运行时地址都会变化,攻击者无法再依赖硬编码的地址,这大大增加了攻击的难度
绕过 ASLR 的基本思路
ASLR 依赖于地址的保密性。如果攻击者能够泄露(也就是获得)任何一个模块的真实地址,那么 ASLR 就会失效。这是因为一个模块内部的相对偏移(RVA)是固定的。一旦知道了一个函数的真实地址,攻击者就可以通过这个地址减去其 RVA,得到模块的基址,进而计算出该模块中所有其他函数和数据的位置
因此,绕过 ASLR 的核心思路就是信息泄露
常见绕过 ASLR 的技术
以下是一些最常见的绕过 ASLR 的技术,从简单到复杂
1. 信息泄露漏洞
这是最直接的绕过方式。如果程序本身存在一个信息泄露漏洞,攻击者就可以利用它来获得内存中的地址
- 格式化字符串漏洞(Format String Bug): 攻击者可以利用
%p
格式符来打印栈上的指针。如果栈上恰好有一个指向模块基址或函数地址的指针,攻击者就可以轻松获得这些关键地址 - 堆溢出或栈溢出: 如果攻击者能够通过溢出漏洞读取栈或堆上的数据,他们可能会读出函数返回地址、栈指针或堆块指针,这些指针都包含有地址信息
- 未初始化变量: 如果一个变量没有初始化,它可能包含之前内存中的残余数据,而这些残余数据可能恰好是一个地址
如何利用:
- 利用信息泄露漏洞获得某个函数的真实地址,比如
puts()
的地址 - 通过
puts()
的地址,计算出libc
库的基址(puts_addr - puts_libc_offset = libc_base
) - 有了
libc
的基址,就可以计算出system()
函数和字符串"/bin/sh"
的地址,从而构造 ROP 链来执行 Shellcode
2. 局部 ASLR 绕过
某些系统或老旧的程序只对部分内存区域进行了 ASLR 随机化,或者随机化范围非常小
- 弱 ASLR: 32 位系统上的 ASLR 随机化范围通常只有 16 位(64KB)。攻击者可以通过暴力破解或多次尝试来命中正确的地址,这被称为暴力破解攻击(Brute-force Attack)
3. 非随机化区域利用
有些程序或系统组件没有启用 ASLR 保护,它们总会被加载到固定的地址。攻击者可以利用这些非随机化区域作为跳板
- 静态编译的程序: 如果一个程序是静态编译的,不依赖任何共享库,那么它的所有代码和数据都在一个文件中。即使开启了 ASLR,其自身的基址是随机化的,但如果攻击者能够泄露出一个地址,就能推算出所有其他地址
- 禁用 ASLR 的模块: 有些老旧的 DLL 可能没有启用 ASLR 编译。这些模块每次都会被加载到固定地址,攻击者可以直接利用其中的 gadgets 或函数
4. 通过其他漏洞组合绕过
ASLR 很少被单独绕过,通常需要与其他漏洞(如代码执行漏洞)结合使用
- Return-to-PLT/GOT: 攻击者可以利用漏洞劫持程序流,跳转到 GOT(Global Offset Table)中一个已解析的函数地址。因为 GOT 存放的是外部函数的真实地址,通过读取这个地址,攻击者就能获得
libc
等共享库的基址 - ROP Gadget 寻找: 在没有信息泄露漏洞的情况下,攻击者可以使用多次尝试或堆喷射等技术。在现代 64 位系统上,ASLR 随机化范围很大,暴力破解几乎不可行。所以,信息泄露是绕过 ASLR 的首选方式