Translated ['src/binary-exploitation/libc-heap/use-after-free/first-fit.

This commit is contained in:
Translator 2025-08-20 09:38:00 +00:00
parent 34e6f9d6d6
commit 5b64b2e262

View File

@ -4,18 +4,18 @@
## **First Fit**
当你在程序中使用 glibc 释放内存时,会使用不同的“桶”来管理内存块。以下是两种常见场景的简化解释:未排序桶和快速桶
当你在程序中使用 glibc 释放内存时,会使用不同的“bins”来管理内存块。以下是两种常见场景的简化解释未排序的 bins 和快速 bins
### Unsorted Bins
当你释放一个不是快速块的内存块时,它会进入未排序桶。这个桶就像一个列表,新释放的块会添加到前面(“头”)。当你请求一个新的内存块时,分配器会从未排序桶的后面(“尾”)查看,以找到一个足够大的块。如果未排序桶中的块大于你所需的大小,它会被拆分,前面的部分被返回,剩余的部分留在中。
当你释放一个不是快速块的内存块时,它会进入未排序的 bin。这个 bin 像一个列表,新释放的块会添加到前面(“头”)。当你请求一个新的内存块时,分配器会从未排序的 bin 的后面(“尾”)查看,以找到一个足够大的块。如果未排序的 bin 中的块大于你所需的大小,它会被拆分,前面的部分被返回,剩余的部分留在 bin 中。
示例:
- 你分配 300 字节(`a`),然后 250 字节(`b`),然后释放 `a` 并再次请求 250 字节(`c`)。
- 当你释放 `a` 时,它会进入未排序
- 如果你再次请求 250 字节,分配器会在尾部找到 `a` 并将其拆分,返回适合你请求的部分,并将其余部分保留在中。
- `c` 将指向之前的 `a`并填充 `a` 的内容。
- 当你释放 `a` 时,它会进入未排序的 bin
- 如果你再次请求 250 字节,分配器会在尾部找到 `a` 并将其拆分,返回适合你请求的部分,并将其余部分保留在 bin 中。
- `c` 将指向之前的 `a` 并填充 `a` 的内容。
```c
char *a = malloc(300);
char *b = malloc(250);
@ -24,7 +24,7 @@ char *c = malloc(250);
```
### Fastbins
Fastbins 用于小内存块。与未排序的 bins 不同fastbins 将新块添加到头部,形成后进先出 (LIFO) 行为。如果您请求一个小内存块,分配器将从 fastbin 的头部提取。
Fastbins用于小内存块。与未排序的bins不同fastbins将新块添加到头部形成后进先出LIFO行为。如果您请求一个小内存块分配器将从fastbin的头部提取。
示例:
```c
@ -55,10 +55,10 @@ d = malloc(20); // a
for(int i = 0; i < 7; i++) pool[i] = malloc(0x100);
for(int i = 0; i < 7; i++) free(pool[i]);
```
一旦 tcache 耗尽,后续的释放将进入未排序的桶,并且经典的首次适应行为(尾部搜索,头部插入)可以再次触发
一旦 tcache 耗尽,后续的释放将进入未排序的桶,并且可以再次触发经典的首次适应行为(尾部搜索,头部插入)。
---
### 🚩 使用首次适应制作重叠块 UAF
### 🚩 使用首次适应制作重叠块 UAF
下面的片段(在 glibc 2.38 上测试)展示了如何滥用未排序桶中的分割器来创建 2 个 **重叠指针** 这是一个强大的原语,将单个释放转换为写后释放。
```c
@ -98,23 +98,21 @@ fwrite(C2, 1, 0x10, stdout); // prints Xs
4. **再次分配** 剩余部分与现有的使用中块重叠 → UAF。
5. 覆盖敏感字段函数指针、FILE vtable 等)。
一个实际应用可以在 2024 HITCON Quals *Setjmp* 挑战中找到,其中使用了这个确切的原语来从 UAF 转向完全控制 `__free_hook`。{{#ref}}
../../../../references/2024_setjmp_firstfit.md
{{#endref}}
一个实际应用可以在 2024 HITCON Quals *Setjmp* 挑战中找到,其中使用了这个确切的原语来从 UAF 转向完全控制 `__free_hook`
---
### 🛡️ 缓解措施与加固
* **安全链接glibc ≥ 2.32** 仅保护单链表的 *tcache*/**fastbin** 列表。未排序/小/大桶仍然存储原始指针,因此如果可以获得堆泄漏,基于首次适配的重叠仍然可行。
* **堆指针加密 MTE**ARM64尚未影响 x86-64 glibc但发行版加固标志如 `GLIBC_TUNABLES=glibc.malloc.check=3` 将在元数据不一致时中止,并可能破坏简单的 PoC。
* **在释放时填充 tcache**(在 2024 年为 glibc 2.41 提出的)将进一步减少未排序的使用;在开发通用利用时,请关注未来的版本
* **安全链接glibc ≥ 2.32** 仅保护单链表的 *tcache*/**fastbin** 列表。未排序/小/大桶仍然存储原始指针,因此如果可以获得堆泄漏,基于首次适配的重叠仍然可行
* **堆指针加密 MTE**ARM64尚未影响 x86-64 glibc但发行版加固标志如 `GLIBC_TUNABLES=glibc.malloc.check=3` 将在元数据不一致时中止,并可能破坏简单的 PoC。
* **在释放时填充 tcache**(在 2024 年为 glibc 2.41 提出的)将进一步减少未排序的使用;在开发通用利用时,请关注未来的发布
---
## 其他参考与示例
- [**https://heap-exploitation.dhavalkapil.com/attacks/first_fit**](https://heap-exploitation.dhavalkapil.com/attacks/first_fit)
- [**https://8ksec.io/arm64-reversing-and-exploitation-part-2-use-after-free/**](https://8ksec.io/arm64-reversing-and-exploitation-part-2-use-after-free/)
- ARM64. 使用后释放:生成一个用户对象,释放它,生成一个获取已释放块并允许写入的对象,**覆盖用户->密码** 的位置。重新使用用户以**绕过密码检查**
- ARM64. 使用后释放:生成一个用户对象,释放它,生成一个获取已释放块并允许写入的对象,**覆盖用户->密码** 的位置。重用用户以**绕过密码检查**
- [**https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/use_after_free/#example**](https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/use_after_free/#example)
- 该程序允许创建笔记。一个笔记将包含在 malloc(8) 中的笔记信息(带有可以调用的函数指针)和指向另一个 malloc(<size>) 的指针,后者包含笔记的内容。
- 攻击将是创建 2 个笔记note0 和 note1其 malloc 内容大于笔记信息大小,然后释放它们以使其进入快速桶(或 tcache
@ -122,8 +120,8 @@ fwrite(C2, 1, 0x10, stdout); // prints Xs
- [**https://guyinatuxedo.github.io/26-heap_grooming/pico_areyouroot/index.html**](https://guyinatuxedo.github.io/26-heap_grooming/pico_areyouroot/index.html)
- 可以分配一些内存,写入所需值,释放它,重新分配它,由于之前的数据仍然存在,它将根据块中的新预期结构进行处理,从而可以设置值以获取标志。
- [**https://guyinatuxedo.github.io/26-heap_grooming/swamp19_heapgolf/index.html**](https://guyinatuxedo.github.io/26-heap_grooming/swamp19_heapgolf/index.html)
- 在这种情况下,需要在特定块中写入 4该块是第一个被分配的即使在强制释放所有块之后。在每个新分配的块中其在数组索引中的编号被存储。然后分配 4 个块(+ 最初分配的),最后一个将包含 4释放它们并强制重新分配第一个块这将使用最后释放的块即包含 4 的块。
- 2024 HITCON Quals Setjmp 文章Quarkslab 的首次适配/未排序拆分重叠攻击: <https://ctftime.org/writeup/39355>
- 在这种情况下,需要在特定块中写入 4该块是第一个被分配的(即使在强制释放所有块之后)。在每个新分配的块中,其在数组索引中的编号被存储。然后,分配 4 个块(加上最初分配的),最后一个块将包含 4释放它们并强制重新分配第一个块这将使用最后释放的块即包含 4 的块。
- 2024 HITCON Quals Setjmp 文章Quarkslab 的首次适配/未排序拆分重叠攻击: <https://ctftime.org/writeup/39355>
- Angstrom CTF 2024 *heapify* 文章 利用未排序桶拆分泄漏 libc 并获得重叠: <https://hackmd.io/@aneii11/H1S2snV40>
{{#include ../../../banners/hacktricks-training.md}}