CVE-2024-2961: iconv Buffer Overflow
Vulnerability Summary
| Field | Value |
|---|---|
| CVE | CVE-2024-2961 |
| CVSS | 8.8 (High) |
| Affected | glibc <= 2.39 |
| Disclosed | 2024-04-17 |
| Type | Out-of-bounds write in iconv() |
The iconv() function in glibc overflows the output buffer by 1-3 bytes when converting strings to the ISO-2022-CN-EXT character set. The overflow occurs because escape sequence writes for SS2 and SS3 designations lack bounds checks.
Exploitation Technique
In real-world exploitation (the CNEXT exploit chain against PHP), attackers use this 1-3 byte overflow to corrupt tcache forward pointers in adjacent freed heap chunks:
- Groom the heap so a freed chunk sits immediately after the iconv output buffer
- Trigger the iconv overflow to modify the low byte(s) of the tcache
fdpointer - The corrupted pointer redirects a subsequent
malloc()to an attacker-controlled address - Write to that address to overwrite
__free_hookor a GOT entry - Trigger the hook to achieve remote code execution
This technique -- tcache poisoning via buffer overflow -- works because glibc stores freelist metadata (the fd pointer) inline within freed chunks, directly adjacent to user data.
Proof of Concept
Source: tests/cve/tcache_poison.c
The PoC demonstrates the exploitation technique (1-byte write past the requested allocation size) rather than calling iconv() directly. This keeps it simple and version-independent.
gcc -o /tmp/tcache_poison tests/cve/tcache_poison.c
glibc output
=== Tcache Poisoning via 1-Byte Overflow Demo ===
(CVE-2024-2961 exploitation technique)
[1] chunk_a = malloc(50) => 0x...
[2] chunk_b = malloc(50) => 0x...
distance: 64 bytes
[3] free(chunk_b) => chunk_b enters tcache
[4] chunk_b tcache fd = 0x...
[5] Simulating 1-byte overflow from chunk_a into chunk_b...
[6] free(chunk_a) => queued for deferred canary check
[7] Triggering batch flush (70 frees)...
[!] 1-byte overflow was NOT detected.
Under glibc, writing 1 byte past the requested 50-byte allocation lands within glibc's usable 56-byte region of the 64-byte chunk. No detection occurs. In the real CVE, larger overflows (1-3 bytes into adjacent chunks) corrupt the tcache fd pointer.
compatmalloc output
=== Tcache Poisoning via 1-Byte Overflow Demo ===
(CVE-2024-2961 exploitation technique)
[1] chunk_a = malloc(50) => 0x...
[2] chunk_b = malloc(50) => 0x...
distance: -46784 bytes
[3] free(chunk_b) => chunk_b enters tcache
[4] chunk_b tcache fd = 0x4242424242424242
[5] Simulating 1-byte overflow from chunk_a into chunk_b...
[6] free(chunk_a) => queued for deferred canary check
[7] Triggering batch flush (70 frees)...
compatmalloc: heap buffer overflow detected (canary corrupted)
compatmalloc aborts immediately when the canary check detects the overflow.
What compatmalloc catches
Two independent layers of defense apply:
-
Canary bytes. compatmalloc places canary bytes in the padding between the requested size and the slot size. For
malloc(50)in a 64-byte slot, bytes[50..64)contain canary values. The 1-byte write at offset 50 corrupts the canary, which is detected onfree(). -
Out-of-band metadata. Even without canaries, compatmalloc stores all freelist metadata in a separate
mmapregion -- not inline within freed chunks. There are nofdpointers adjacent to user data to corrupt. The fundamental prerequisite of tcache poisoning (corruptible inline metadata) does not exist. -
Slot randomization. Allocations are not placed adjacently in predictable order, making heap grooming significantly harder.
What compatmalloc does NOT catch
- The overflow is not detected at the moment it happens. Canary checks run on
free()(specifically during deferred batch verification). If the overflowed buffer is never freed, detection is delayed. - compatmalloc does not fix the
iconv()bug itself. It prevents the exploitation technique (tcache poisoning) from succeeding, but the overflow still occurs iniconv(). - Intra-slot overflows between adjacent slots in the same slab are caught by canaries, not guard pages. Guard pages only protect slab boundaries.