mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/binary-exploitation/common-binary-protections-and-bypas
This commit is contained in:
parent
0b23c3763c
commit
ab49df1398
@ -4,30 +4,96 @@
|
||||
|
||||
## Relro
|
||||
|
||||
**RELRO** 代表 **Relocation Read-Only**,它是用于二进制文件的安全特性,旨在减轻与 **GOT (Global Offset Table)** 重写相关的风险。**RELRO** 保护有两种类型:(1)**Partial RELRO** 和(2)**Full RELRO**。它们都重新排列了 ELF 文件中的 **GOT** 和 **BSS**,但结果和影响不同。具体来说,它们将 **GOT** 部分放在 **BSS** 之前。也就是说,**GOT** 的地址低于 **BSS**,因此通过溢出 **BSS** 中的变量来重写 **GOT** 条目是不可能的(记住,写入内存是从低地址向高地址进行的)。
|
||||
**RELRO** 代表 **Relocation Read-Only**,这是链接器(`ld`)实施的一种缓解措施,它在所有重定位应用后将 ELF 的数据段的一个子集设置为 **只读**。 其目的是阻止攻击者覆盖 **GOT(全局偏移表)** 或其他在程序执行期间被解引用的与重定位相关的表(例如 `__fini_array`)中的条目。
|
||||
|
||||
让我们将这个概念分解为两个不同的类型以便于理解。
|
||||
现代链接器通过 **重新排序** **GOT**(和其他几个部分)来实现 RELRO,使其位于 **.bss** 之前,并且最重要的是,通过创建一个专用的 `PT_GNU_RELRO` 段,该段在动态加载器完成应用重定位后被重新映射为 `R–X`。 因此,典型的 **.bss** 中的缓冲区溢出不再能够到达 GOT,任意写入原语无法用于覆盖位于 RELRO 保护页面内的函数指针。
|
||||
|
||||
### **Partial RELRO**
|
||||
链接器可以发出 **两级** 保护:
|
||||
|
||||
**Partial RELRO** 采取更简单的方法来增强安全性,而不会显著影响二进制文件的性能。Partial RELRO 使 **.got 只读(GOT 部分的非 PLT 部分)**。请记住,部分区域(如 .got.plt)仍然是可写的,因此仍然容易受到攻击。这 **并不防止 GOT** 被 **任意写入** 漏洞滥用。
|
||||
### Partial RELRO
|
||||
|
||||
注意:默认情况下,GCC 编译的二进制文件使用 Partial RELRO。
|
||||
* 使用标志 `-Wl,-z,relro`(或在直接调用 `ld` 时仅使用 `-z relro`)生成。
|
||||
* 仅将 **GOT** 的 **非 PLT** 部分(用于数据重定位的部分)放入只读段。 需要在运行时修改的部分 – 最重要的是支持 **懒绑定** 的 **.got.plt** – 保持可写。
|
||||
* 因此,**任意写入** 原语仍然可以通过覆盖 PLT 条目(或通过执行 **ret2dlresolve**)来重定向执行流。
|
||||
* 性能影响微乎其微,因此 **几乎每个发行版多年来都在发布至少具有部分 RELRO 的软件包(自 2016 年起,它是 GCC/Binutils 的默认设置)**。
|
||||
|
||||
### **Full RELRO**
|
||||
### Full RELRO
|
||||
|
||||
**Full RELRO** 通过 **使整个 GOT(包括 .got 和 .got.plt)和 .fini_array** 部分完全 **只读** 来加强保护。一旦二进制文件启动,所有函数地址都会在 GOT 中解析并加载,然后,GOT 被标记为只读,有效地防止在运行时对其进行任何修改。
|
||||
* 使用 **两个** 标志 `-Wl,-z,relro,-z,now`(也称为 `-z relro -z now`)生成。 `-z now` 强制动态加载器提前解析 **所有** 符号(急切绑定),以便 **.got.plt** 不再需要被写入,并且可以安全地映射为只读。
|
||||
* 整个 **GOT**、**.got.plt**、**.fini_array**、**.init_array**、**.preinit_array** 和一些额外的内部 glibc 表最终位于只读的 `PT_GNU_RELRO` 段中。
|
||||
* 增加可测量的启动开销(所有动态重定位在启动时处理),但 **没有运行时开销**。
|
||||
|
||||
然而,Full RELRO 的权衡在于性能和启动时间。因为它需要在启动时解析所有动态符号,然后再将 GOT 标记为只读,**启用 Full RELRO 的二进制文件可能会经历更长的加载时间**。这种额外的启动开销就是为什么并非所有二进制文件默认启用 Full RELRO。
|
||||
自 2023 年以来,几种主流发行版已切换为默认使用 **Full RELRO** 编译 **系统工具链**(和大多数软件包) – 例如 **Debian 12 “bookworm” (dpkg-buildflags 13.0.0)** 和 **Fedora 35+**。 因此,作为渗透测试人员,您应该预期遇到 **每个 GOT 条目都是只读** 的二进制文件。
|
||||
|
||||
可以通过以下方式查看二进制文件是否 **启用** Full RELRO:
|
||||
---
|
||||
|
||||
## 如何检查二进制文件的 RELRO 状态
|
||||
```bash
|
||||
readelf -l /proc/ID_PROC/exe | grep BIND_NOW
|
||||
$ checksec --file ./vuln
|
||||
[*] '/tmp/vuln'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Full
|
||||
Stack: Canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x400000)
|
||||
```
|
||||
`checksec`(是 [pwntools](https://github.com/pwncollege/pwntools) 和许多发行版的一部分)解析 `ELF` 头并打印保护级别。如果您无法使用 `checksec`,请依赖 `readelf`:
|
||||
```bash
|
||||
# Partial RELRO → PT_GNU_RELRO is present but BIND_NOW is *absent*
|
||||
$ readelf -l ./vuln | grep -E "GNU_RELRO|BIND_NOW"
|
||||
GNU_RELRO 0x0000000000600e20 0x0000000000600e20
|
||||
```
|
||||
## 绕过
|
||||
|
||||
如果启用了完整的 RELRO,绕过它的唯一方法是找到另一种不需要在 GOT 表中写入以获得任意执行的方法。
|
||||
```bash
|
||||
# Full RELRO → PT_GNU_RELRO *and* the DF_BIND_NOW flag
|
||||
$ readelf -d ./vuln | grep BIND_NOW
|
||||
0x0000000000000010 (FLAGS) FLAGS: BIND_NOW
|
||||
```
|
||||
如果二进制文件正在运行(例如,一个 set-uid root 助手),你仍然可以通过 **`/proc/$PID/exe`** 检查可执行文件:
|
||||
```bash
|
||||
readelf -l /proc/$(pgrep helper)/exe | grep GNU_RELRO
|
||||
```
|
||||
---
|
||||
|
||||
请注意,**LIBC 的 GOT 通常是部分 RELRO**,因此可以通过任意写入进行修改。更多信息请参见 [Targetting libc GOT entries](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#1---targetting-libc-got-entries)**.**
|
||||
## 在编译自己的代码时启用 RELRO
|
||||
```bash
|
||||
# GCC example – create a PIE with Full RELRO and other common hardenings
|
||||
$ gcc -fPIE -pie -z relro -z now -Wl,--as-needed -D_FORTIFY_SOURCE=2 main.c -o secure
|
||||
```
|
||||
`-z relro -z now` 适用于 **GCC/clang**(在 `-Wl,` 后传递)和直接使用 **ld**。 当使用 **CMake 3.18+** 时,您可以通过内置预设请求完整的 RELRO:
|
||||
```cmake
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) # LTO
|
||||
set(CMAKE_ENABLE_EXPORTS OFF)
|
||||
set(CMAKE_BUILD_RPATH_USE_ORIGIN ON)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-Wl,-z,relro,-z,now")
|
||||
```
|
||||
---
|
||||
|
||||
## 绕过技术
|
||||
|
||||
| RELRO 级别 | 典型原语 | 可能的利用技术 |
|
||||
|-------------|-------------------|----------------------------------|
|
||||
| 无 / 部分 | 任意写入 | 1. 重写 **.got.plt** 条目并转移执行。<br>2. **ret2dlresolve** – 在可写段中构造伪造的 `Elf64_Rela` 和 `Elf64_Sym` 并调用 `_dl_runtime_resolve`。<br>3. 重写 **.fini_array** / **atexit()** 列表中的函数指针。 |
|
||||
| 完全 | GOT 是只读的 | 1. 寻找 **其他可写代码指针**(C++ vtables, `__malloc_hook` < glibc 2.34, `__free_hook`, 自定义 `.data` 段中的回调, JIT 页)。<br>2. 滥用 *相对读取* 原语泄露 libc 并执行 **SROP/ROP 进入 libc**。<br>3. 通过 **DT_RPATH**/`LD_PRELOAD` 注入恶意共享对象(如果环境由攻击者控制)或 **`ld_audit`**。<br>4. 利用 **格式字符串** 或部分指针重写来转移控制流而不触碰 GOT。 |
|
||||
|
||||
> 💡 即使是完全 RELRO,**加载的共享库(例如 libc 本身)的 GOT** 也是 **仅部分 RELRO**,因为这些对象在加载器应用重定位时已经被映射。如果你获得了一个 **任意写入** 原语,可以针对另一个共享对象的页面,你仍然可以通过重写 libc 的 GOT 条目或 `__rtld_global` 栈来转移执行,这是一种在现代 CTF 挑战中经常被利用的技术。
|
||||
|
||||
### 现实世界的绕过示例 (2024 CTF – *pwn.college “enlightened”*)
|
||||
|
||||
该挑战附带了完全 RELRO。利用了一个 **越界** 来破坏堆块的大小,通过 `tcache poisoning` 泄露了 libc,最后重写了 `__free_hook`(在 RELRO 段外)并使用一个单一 gadget 获得代码执行。无需进行 GOT 写入。
|
||||
|
||||
---
|
||||
|
||||
## 最近的研究与漏洞 (2022-2025)
|
||||
|
||||
* **glibc 2.40 废弃 `__malloc_hook` / `__free_hook` (2025)** – 大多数利用这些符号的现代堆漏洞现在必须转向替代向量,如 **`rtld_global._dl_load_jump`** 或 C++ 异常表。由于钩子位于 **RELRO 之外**,它们的移除增加了完全 RELRO 绕过的难度。
|
||||
* **Binutils 2.41 “最大页面大小” 修复 (2024)** – 一个错误允许 RELRO 段的最后几个字节与某些 ARM64 构建中的可写数据共享一个页面,留下一个微小的 **RELRO 缺口**,可以在 `mprotect` 之后写入。上游现在将 `PT_GNU_RELRO` 对齐到页面边界,消除了这个边缘情况。
|
||||
|
||||
---
|
||||
|
||||
## 参考文献
|
||||
|
||||
* Binutils 文档 – *`-z relro`, `-z now` 和 `PT_GNU_RELRO`*
|
||||
* *“RELRO – 完全、部分和绕过技术”* – 博客文章 @ wolfslittlered 2023
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user