Heap Overflow
Reading time: 10 minutes
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 来分享黑客技巧。
基本信息
堆溢出类似于 栈溢出,但发生在堆中。基本上,这意味着在堆中保留了一些空间来存储数据,而 存储的数据大于保留的空间。
在栈溢出中,我们知道一些寄存器,如指令指针或栈帧,将从栈中恢复,并且可能会被滥用。在堆溢出的情况下,默认情况下堆块中没有存储任何敏感信息,可以被溢出。然而,它可能包含敏感信息或指针,因此这种漏洞的 严重性 取决于 哪些数据可能被覆盖 以及攻击者如何利用这一点。
tip
为了找到溢出偏移量,您可以使用与 栈溢出 相同的模式。
栈溢出与堆溢出
在栈溢出中,触发漏洞时栈中将存在的排列和数据是相当可靠的。这是因为栈是线性的,总是增加在碰撞的内存中,在 程序运行的特定位置,栈内存通常存储类似类型的数据,并且它具有一些特定的结构,末尾有一些指向每个函数使用的栈部分的指针。
然而,在堆溢出的情况下,使用的内存不是线性的,而是 分配的块通常位于内存的不同位置(而不是一个接一个),因为 bins 和 zones 按大小分隔分配,并且因为 先前释放的内存在分配新块之前被使用。因此,很难知道将与易受堆溢出影响的对象发生碰撞的对象。因此,当发现堆溢出时,需要找到一种 可靠的方法使所需对象在内存中紧挨着可以被溢出的对象。
用于此的一种技术是 Heap Grooming,例如在 这篇文章 中进行了说明。文章解释了当 iOS 内核中的一个区域没有足够的内存来存储内存块时,它通过一个内核页面进行扩展,并且该页面被分割成预期大小的块,这些块将按顺序使用(直到 iOS 版本 9.2,然后这些块以随机方式使用,以增加这些攻击的利用难度)。
因此,在发生堆溢出的前一篇文章中,为了强制溢出的对象与受害者对象发生碰撞,多个 kallocs
被多个线程强制执行,以确保所有空闲块都被填满,并且创建一个新页面。
为了强制用特定大小的对象填充,与 iOS mach 端口相关的离线分配是一个理想的候选者。通过精确设置消息的大小,可以准确指定 kalloc
分配的大小,当相应的 mach 端口被销毁时,相应的分配将立即释放回 kfree
。
然后,这些占位符中的一些可以被 释放。kalloc.4096
空闲列表以后进先出顺序释放元素,这基本上意味着如果一些占位符被释放,而利用尝试在分配易受溢出影响的对象时分配多个受害者对象,则该对象很可能会被一个受害者对象跟随。
示例 libc
在此页面 可以找到一个基本的堆溢出仿真,展示了如何通过覆盖下一个块的 prev in use 位和 prev size 的位置来 合并一个已使用的块(使其认为是未使用的),然后 再次分配它,能够覆盖在不同指针中使用的数据。
另一个来自 protostar heap 0 的示例展示了一个非常基本的 CTF 示例,其中 堆溢出 可以被滥用以调用赢家函数以 获取标志。
在 protostar heap 1 示例中,可以看到如何通过滥用缓冲区溢出来 在一个临近块中覆盖一个地址,该地址将 写入用户的任意数据。
示例 ARM64
在页面 https://8ksec.io/arm64-reversing-and-exploitation-part-1-arm-instruction-set-simple-heap-overflow/ 中,您可以找到一个堆溢出示例,其中要执行的命令存储在溢出块的下一个块中。因此,可以通过用简单的利用覆盖它来修改执行的命令,例如:
python3 -c 'print("/"*0x400+"/bin/ls\x00")' > hax.txt
其他示例
- Auth-or-out. Hack The Box
- 我们利用整数溢出漏洞来获取堆溢出。
- 我们破坏指向溢出块内
struct
中函数的指针,以设置如system
的函数并获得代码执行。
真实世界示例:CVE-2025-40597 – 错误使用 __sprintf_chk
在 SonicWall SMA100 固件 10.2.1.15 中,反向代理模块 mod_httprp.so
分配了一个 0x80-byte 的堆块,然后使用 __sprintf_chk
将多个字符串连接到其中:
char *buf = calloc(0x80, 1);
/* … */
__sprintf_chk(buf, /* destination (0x80-byte chunk) */
-1, /* <-- size argument !!! */
0, /* flags */
"%s%s%s%s", /* format */
"/", "https://", path, host);
__sprintf_chk
是 _FORTIFY_SOURCE 的一部分。当它接收到一个 正 的 size
参数时,它会验证结果字符串是否适合目标缓冲区。通过传递 -1
(0xFFFFFFFFFFFFFFFF),开发者实际上 禁用了边界检查,将强化调用转回经典的不安全 sprintf
。
因此,提供一个过长的 Host:
头部允许攻击者 溢出 0x80 字节的块并覆盖下一个堆块的元数据(根据分配器的不同,可能是 tcache / fast-bin / small-bin)。可以通过以下方式重现崩溃:
import requests, warnings
warnings.filterwarnings('ignore')
requests.get(
'https://TARGET/__api__/',
headers={'Host': 'A'*750},
verify=False
)
实际利用需要堆整理以将可控对象放置在易受攻击的块之后,但根本原因强调了两个重要的要点:
- _FORTIFY_SOURCE 不是灵丹妙药 – 错误使用可能会使保护失效。
- 始终将正确的缓冲区大小传递给
_chk
系列(或者,更好的是,使用snprintf
)。
References
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 来分享黑客技巧。