Memory-exhaustion DoS in BMP decoder (bundled bmp-ts dependency) via unbounded width/height

Aperta
#1,415 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
62/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
node.js, typescript
Ambito
security

Direzione di ricerca

Inizia da plugins/js-bmp/src/index.ts e dai percorsi di allocazione e riempimento di decoder.js di bmp-ts incluso, descritti nel report. Esegui la riproduzione BMP fornita e appositamente creata con Jimp.read() per stabilire il comportamento della crescita della memoria, quindi aggiungi una copertura per rifiutare le dimensioni eccessive prima dell'allocazione. Il lavoro è completato quando la riproduzione viene rifiutata senza eseguire la grande allocazione e la normale decodifica BMP continua a funzionare.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Summary

@jimp/js-bmp delegates BMP decoding entirely to the bmp-ts npm package with no validation of its own. bmp-ts's decoder allocates its output pixel buffer directly from the BMP header's width/height fields with no upper bound and no comparison against the actual input size:

// [email protected] dist/commonjs/decoder.js:153
this.data = Buffer.alloc(this.width * this.height * 4);

For RLE4/RLE8-compressed BMPs, the very next thing that happens is:

// decoder.js:201 (bit4) and :279 (bit8)
this.data.fill(0);

Buffer.fill() forces real physical memory to be committed across the whole buffer (unlike Buffer.alloc's lazily-zero-mapped pages), and this happens before any actual pixel bytes are read from the file, so a tiny file with a big declared width/height is enough to force a large real memory allocation.

Proof of Concept

A crafted 1078-byte BMP (valid BM magic + 40-byte BITMAPINFOHEADER, bitPP=8, compression=1 i.e. BI_RLE8, width=10000, height=10000, a 256-entry zero palette, and no actual RLE pixel data after the palette) causes roughly 405MB of RSS growth in a single Jimp.read() call, confirmed against the real published [email protected] (which bundles [email protected]):

input size 1078 bytes   rss before ~67.7MB
Jimp.read(buffer) -> eventually throws RangeError (only AFTER the .fill(0) commits memory)
time 278ms              rss after ~472.4MB   (~405MB forced commit from a 1KB file)

Scaling width/height further is unbounded — nothing in bmp-ts or in @jimp/js-bmp's wrapper (plugins/js-bmp/src/index.ts, which calls BMP.decode() directly with no width/height sanity check before delegating) caps the allocation. A handful of concurrent uploads of ~1KB crafted files could force multi-GB memory growth or an allocator OOM in any service that calls Jimp.read() on user-uploaded images (a very common pattern — avatar/image upload handling).

This is a memory-exhaustion availability issue (CWE-400/789), not memory corruption — Node's Buffer/TypedArray bounds checks prevent any actual out-of-bounds read/write.

Suggested Fix

Before allocating the output buffer, validate that width * height * bytesPerPixel is consistent with the actual input buffer size (or with a reasonable configurable maximum), and reject the file early if not. This applies both in bmp-ts's decoder.js (root cause) and as a defense-in-depth check in @jimp/js-bmp's wrapper before it delegates to bmp-ts, since jimp is the package most people actually depend on directly.

Disclosure note

Checked for existing reports first: no GHSA/CVE/OSV advisory exists for bmp-ts, jimp, or @jimp/js-bmp. Neither repo has SECURITY.md or GitHub private vulnerability reporting enabled, so filing this as a plain public issue rather than a private advisory — severity is DoS-only (not RCE/memory-corruption), so this seemed reasonable to disclose directly; happy to move to a private channel if one gets set up.

Lingua principale
TypeScript
Stelle
14.7k
Fork
777
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di jimp-dev/jimp

Tutte le issue di jimp-dev/jimp

Issue simili

Altre issue su TypeScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.