diff --git a/src/binary-exploitation/libc-heap/heap-overflow.md b/src/binary-exploitation/libc-heap/heap-overflow.md index 9ce39e5b5b4..923de826a57 100644 --- a/src/binary-exploitation/libc-heap/heap-overflow.md +++ b/src/binary-exploitation/libc-heap/heap-overflow.md @@ -76,6 +76,43 @@ Source-side checks such as `count <= remaining_input` are **not enough**. The pa This pattern is especially relevant in **games, importers, asset packs, media parsers, and mod/plugin ecosystems**, where a “passive” file can trigger complex stateful parsing and attacker-controlled heap shaping.[[2]](#references) +### Canonicalization output overflowing fixed-stride object pools + +Do not size a destination from the raw XML alone: **canonicalization can produce a different-length serialization**. In a NetScaler SAML path, exclusive C14N canonicalized attacker-controlled `ds:SignedInfo` into a fixed-size network-buffer object before authentication completed. Most signature fields were constrained, but `ec:InclusiveNamespaces PrefixList` accepted a long list when every space-separated prefix was unique, making it a useful expansion surface. The patched implementation rejects a computed canonical form of `0x1001` bytes or more.[[10]](#references)[[11]](#references) + +A compact way to generate uniquely identifiable C14N input is:[[10]](#references) + +```python +prefixes = " ".join(f"N{i}" for i in range(2000)) +inclusive_ns = ( + '' +) +``` + +The important exploitation pattern is **delayed cross-object corruption**. The canonicalized data started at `+0x180` in a fixed-stride `0x980`-byte packet-buffer pool; a linear overflow entered the next object's header and reached a type field, a data pointer at `+0x50`, and a freelist link at `+0x60`. The fault occurred only after the poisoned object was recycled and packet-send code trusted those fields, so a crash in `memcpy` was far removed from the original C14N write. On FreeBSD, the resulting invalid access may appear as `SIGBUS` rather than `SIGSEGV`.[[10]](#references) + +For this kind of pool allocator, use the overflow length as a grooming parameter and map the victim header systematically:[[10]](#references) + +1. Fill the input with fixed-width, self-describing tokens such as `N%07d` or `M0000000`, then inspect corrupted qwords to recover the exact source offset controlling each field. +2. Sweep lengths while breaking on both allocation and the later consumer; the length can select the recycled victim as well as the deepest overwritten field. +3. Preserve fields needed to reach the dangerous use. For example, set a damaged freelist link to a known valid object instead of leaving marker bytes there. +4. Trace the corrupted pointer through all arithmetic immediately before its use rather than assuming the stored value becomes the final pointer unchanged. + +In the demonstrated consumer, the overwritten neighbor supplied the destination, the original object supplied attacker-controlled canonicalized bytes, and packet metadata supplied the copy length:[[10]](#references) + +```c +memcpy(*(victim + 0x50), + *(source + 0x50), + *(source + 0xE0) - *(source + 0x50)); +``` + +The implementation also performed `destination = stored_pointer - packet_length`. Therefore, targeting address `X` required storing `X + packet_length`, illustrating why controlled pointer arithmetic must be inverted when building a write-what-where primitive. In a non-PIE process without ASLR, that write can target a frequently executed global callback; if attacker data is at a predictable executable heap address, replacing the callback with that address can yield direct shellcode execution without an information leak or ROP.[[10]](#references) + +> [!NOTE] +> If the corrupted process cannot safely continue, execute persistence or cleanup logic **before** the inevitable fault. In the NetScaler chain, shellcode replaced fatal-signal handlers with `SIG_IGN` via FreeBSD `sigaction`, preventing the handlers from asking the supervisor to reboot the appliance. The supervisor then respawned only the packet engine, preserving files written before the crash. This behavior is supervisor-specific and should be verified by tracing the process-to-watchdog notification path.[[10]](#references) + ### Real-World Example: CVE-2025-40597 – Misusing `__sprintf_chk` In SonicWall SMA100 firmware 10.2.1.15 the reverse-proxy module `mod_httprp.so` allocates an **0x80-byte** heap chunk and then concatenates several strings into it with `__sprintf_chk`:[[1]](#references) @@ -120,5 +157,7 @@ Practical exploitation would require **heap grooming** to place a controllable o - [7] [8kSec – ARM64 Reversing and Exploitation Part 1: ARM Instruction Set & Simple Heap Overflow](https://8ksec.io/arm64-reversing-and-exploitation-part-1-arm-instruction-set-simple-heap-overflow/) - [8] [7rocky – Auth-or-out. Hack The Box](https://7rocky.github.io/en/ctf/htb-challenges/pwn/auth-or-out/) - [9] [MITRE CWE-122 – Heap-based Buffer Overflow](https://cwe.mitre.org/data/definitions/122.html) +- [10] [watchTowr Labs – You’re Back In The Room: Citrix NetScaler pre-authentication RCE](https://labs.watchtowr.com/youre-back-in-the-room-citrix-netscaler-pre-auth-rce-cve-2026-8452/) +- [11] [Citrix Security Bulletin CTX696604](https://support.citrix.com/support-home/kbsearch/article?articleNumber=CTX696604) {{#include ../../banners/hacktricks-training.md}}