Summary
mp4box.js accepts an undersized tkhd track-header atom through its public parsing API and dispatches it to tkhdBox.parse() without first proving that the atom body contains the fixed fields the parser will read. A crafted local MP4 with a moov/trak/tkhd chain whose tkhd payload contains only the FullBox version/flags bytes causes a deterministic DataView RangeError, which can turn media metadata parsing into an availability failure for services that process attacker-supplied MP4 files. This may have some secruity relevant issues, but as we failed to find any private means of contact, we decided to file this via github issue.
Affected
Root cause
The public entry point is createFile() in src/create-file.ts:9, which returns an ISOFile parser object used by callers to process MP4 data. ISOFile.appendBuffer() accepts caller-supplied bytes at src/isofile.ts:498 and immediately invokes parsing at src/isofile.ts:505. During generic box parsing, src/parser.ts:124 checks that the declared box size fits within the available stream, but that guard only validates containment; it does not validate the semantic minimum length of the concrete box type. Because the attacker-declared tkhd fourcc is registered, src/parser.ts:139 constructs the specialized box parser and src/parser.ts:173 calls box.parse(stream) without a tkhd-specific minimum-payload check. In src/boxes/tkhd.ts:21, tkhdBox.parse() reads the 4-byte FullBox header, then the version 0 path immediately reads creation_time with stream.readUint32() at src/boxes/tkhd.ts:30. For a tkhd body that ends immediately after the FullBox header, that reaches DataStream.readUint32() at src/DataStream.ts:454, whose underlying DataView.getUint32() throws once the current position is outside the available atom data.
Reproduction
poc.zip
RangeError: Offset is outside the bounds of the DataView at DataView.getUint32 (<anonymous>) at MultiBufferStream.readUint32 (file:///home/brian/work/variant-supreme/worktrees/exp-supreme-expansion_20260611T063343/runs/isobmff/INT-isobmff-mp4boxjs-short-tkhd-rangeerror/artifacts/positive/inputs/build-scratch/src/DataStream.js:351:34) at tkhdBox.parse
The RangeError plus the tkhdBox.parse frame shows that the malformed tkhd atom was accepted by the generic box parser and reached the fixed-field reads in the track-header parser. A setup or build failure would not include this parser frame and would not be evidence of this bug.
Suggested fix
diff --git a/src/boxes/tkhd.ts b/src/boxes/tkhd.ts
index 0d05e68..10e3fd9 100644
--- a/src/boxes/tkhd.ts
+++ b/src/boxes/tkhd.ts
@@ -19,7 +19,22 @@ export class tkhdBox extends FullBox {
height: number;
parse(stream: MultiBufferStream) {
+ if (this.size - this.hdr_size < 4) {
+ this.has_unparsed_data = true;
+ this.data = stream.readUint8Array(this.size - this.hdr_size);
+ return;
+ }
+
this.parseFullHeader(stream);
+ const remaining = this.size - this.hdr_size;
+ const minimumFieldBytes = this.version === 1 ? 92 : 80;
+
+ if (remaining < minimumFieldBytes) {
+ this.has_unparsed_data = true;
+ this.data = stream.readUint8Array(remaining);
+ return;
+ }
+
if (this.version === 1) {
this.creation_time = stream.readUint64();
this.modification_time = stream.readUint64();
this.track_id = stream.readUint32();
Reported by Team Atlanta.
Summary
mp4box.js accepts an undersized
tkhdtrack-header atom through its public parsing API and dispatches it totkhdBox.parse()without first proving that the atom body contains the fixed fields the parser will read. A crafted local MP4 with amoov/trak/tkhdchain whosetkhdpayload contains only the FullBox version/flags bytes causes a deterministicDataViewRangeError, which can turn media metadata parsing into an availability failure for services that process attacker-supplied MP4 files. This may have some secruity relevant issues, but as we failed to find any private means of contact, we decided to file this via github issue.Affected
Root cause
The public entry point is
createFile()insrc/create-file.ts:9, which returns anISOFileparser object used by callers to process MP4 data.ISOFile.appendBuffer()accepts caller-supplied bytes atsrc/isofile.ts:498and immediately invokes parsing atsrc/isofile.ts:505. During generic box parsing,src/parser.ts:124checks that the declared box size fits within the available stream, but that guard only validates containment; it does not validate the semantic minimum length of the concrete box type. Because the attacker-declaredtkhdfourcc is registered,src/parser.ts:139constructs the specialized box parser andsrc/parser.ts:173callsbox.parse(stream)without atkhd-specific minimum-payload check. Insrc/boxes/tkhd.ts:21,tkhdBox.parse()reads the 4-byte FullBox header, then the version 0 path immediately readscreation_timewithstream.readUint32()atsrc/boxes/tkhd.ts:30. For atkhdbody that ends immediately after the FullBox header, that reachesDataStream.readUint32()atsrc/DataStream.ts:454, whose underlyingDataView.getUint32()throws once the current position is outside the available atom data.Reproduction
poc.zip
The
RangeErrorplus thetkhdBox.parseframe shows that the malformedtkhdatom was accepted by the generic box parser and reached the fixed-field reads in the track-header parser. A setup or build failure would not include this parser frame and would not be evidence of this bug.Suggested fix
Reported by Team Atlanta.