GHSA-23hg-53q6-hqfg ImageMagick BlobStream Forward-Seek Under-Allocation
**Reporter:** Lumina Mescuwa  
**Product:** ImageMagick 7 (MagickCore)  
**Component:** `MagickCore/blob.c` (Blob I/O - BlobStream)  
**Tested:** 7.1.2-0 (source tag) and 7.1.2-1 (Homebrew), macOS arm64, clang-17, Q16-HDRI  
**Impact:** Heap out-of-bounds **WRITE** (attacker-controlled bytes at attacker-chosen offset) → memory corruption; potential code executionExecutive SummaryFor memory-backed blobs (**BlobStream**), [`SeekBlob()`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5106-L5134) permits advancing the stream **offset** beyond the current end without increasing capacity. The subsequent [`WriteBlob()`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5915-L5938) then expands by **`quantum + length`** (amortized) instead of **`offset + length`**, and copies to `data + offset`. When `offset ≫ extent`, the copy targets memory beyond the allocation, producing a deterministic heap write on 64-bit builds. No 2⁶⁴ arithmetic wrap, external delegates, or policy settings are required.Affected Scope**Versions confirmed:** 7.1.2-0, 7.1.2-1**Architectures:** Observed on macOS arm64; architecture-agnostic on LP64Paths: MagickCore blob subsystem — **BlobStream** ([`SeekBlob()`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5106-L5134) and [`WriteBlob()`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5915-L5938)).**Not required:** External delegates; special policies; integer wraparoundTechnical Root Cause**Types (LP64):**  
`offset: MagickOffsetType` (signed 64-bit)  
`extent/length/quantum: size_t` (unsigned 64-bit)  
`data: unsigned char*`**Contract mismatch:**[`SeekBlob()`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5106-L5134) (BlobStream) updates `offset` to arbitrary positions, including past end, **without** capacity adjustment.[`WriteBlob()`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5915-L5938) tests `offset + length >= extent` and grows **by** `length + quantum`, doubles `quantum`, reallocates to `extent + 1`, then:There is **no guarantee** that `extent ≥ offset + length` post-growth. With `offset ≫ extent`, `q` is beyond the allocation.**Wrap-free demonstration:**  
Initialize `extent=1`, write one byte (`offset=1`), seek to `0x10000000` (256 MiB), then write 3–4 bytes. Growth remains << `offset + length`; the copy overruns the heap buffer.Exploitability & Reachability**Primitive:** Controlled bytes written at a controlled displacement from the buffer base.**Reachability:** Any encode-to-memory flow that forward-seeks prior to writing (e.g., header back-patching, reserved-space strategies). Even if current encoders/writers avoid this, the API contract **permits** it, thus creating a latent sink for first- or third-party encoders/writers.**Determinism:** Once a forward seek past end occurs, the first subsequent write reliably corrupts memory.Impact Assessment**Integrity:** High - adjacent object/metadata overwrite plausible.**Availability:** High - reliably crashable (ASan and non-ASan).**Confidentiality:** High - Successful exploitation to RCE allows the attacker to read all data accessible by the compromised process.**RCE plausibility:** Typical of heap OOB writes in long-lived image services; allocator/layout dependent.CVSS v3.1 Rationale (9.8)**AV:N / PR:N / UI:N** - server-side image processing is commonly network-reachable without auth or user action.**AC:L** - a single forward seek + write suffices; no races or specialized state.**S:U** - corruption localized to the ImageMagick process.**C:H / I:H / A:H** - A successful exploit leads to RCE, granting full control over the process. This results in a total loss of Confidentiality (reading sensitive data), Integrity (modifying files/data), and Availability (terminating the service)._Base scoring assumes successful exploitation; environmental mitigations are out of scope of Base metrics._Violated Invariant**Before copying `length` bytes at `offset`, enforce `extent ≥ offset + length` with overflow-checked arithmetic.**The BlobStream growth policy preserves amortized efficiency but fails to enforce this **per-write** safety invariant.Remediation (Principle)In [`WriteBlob()`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5915-L5938) (BlobStream case):**Checked requirement:**  
 `need = (size_t)offset + length;` → if `need < (size_t)offset`, overflow → fail.**Ensure capacity ≥ need:**  
 `target = MagickMax(extent + quantum + length, need);`  
 (Optionally loop, doubling `quantum`, until `extent ≥ need` to preserve amortization.)**Reallocate to `target + 1` before copying;** then perform the move.**Companion hardening (recommended):**Document or restrict [`SeekBlob()`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5106-L5134) on BlobStream so forward seeks either trigger explicit growth/zero-fill or require the subsequent write to meet the invariant.Centralize blob arithmetic in checked helpers.Unit tests: forward-seek-then-write (success and overflow-reject).Regression & Compatibility**Behavior change:** Forward-seeked writes will either allocate to required size or fail cleanly (overflow/alloc-fail).**Memory profile:** Single writes after very large seeks may allocate large buffers; callers requiring sparse behavior should use file-backed streams.Vendor Verification ChecklistReproduce with a minimal in-memory BlobStream harness under ASan.Apply fix; verify `extent ≥ offset + length` at all write sites.Add forward-seek test cases (positive/negative).Audit other growth sites (`SetBlobExtent`, stream helpers).Clarify BlobStream seek semantics in documentation.Unit test: forward seek to large offset on **BlobStream** followed by 1–8 byte writes; assert either growth to `need` or clean failure.PoC / Reproduction / NotesEnvironment**OS/Arch:** macOS 14 (arm64)**Compiler:** clang-17 with AddressSanitizer**ImageMagick:** Q16-HDRI**Prefix:** `~/opt/im-7.1.2-0`**`pkg-config`:** from PATH (no hard-coded `/usr/local/...`)Build ImageMagick 7.1.2-0 (static, minimal)Build & Run the PoC (memory-backed BlobStream)**`poc.c`:**  
_Uses private headers (`blob-private.h`) to exercise blob internals; a public-API variant (custom streams) is feasible but unnecessary for triage._`run:`**Expected markers prior to the fault:**An ASan **WRITE** crash in [`WriteBlob`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5915-L5938) follows (top frames: `WriteBlob blob.c:<line>`, then `_platform_memmove` / `__sanitizer_internal_memmove`).Debugger Verification (manual)LLDB can be used to snapshot the invariants; ASan alone is sufficient.**Expected at second stop:**  
`type = BlobStream` · `offset ≈ 0x10000000` (256 MiB) · `length ≈ 3–4` · `extent ≈ 64 KiB` (≪ `offset + length`) · `quantum ≈ 128 KiB` · `mapped = MagickFalse` · `data + offset` far beyond base; next `continue` crashes in `_platform_memmove`.Credits**Reported by:** Lumina Mescuwa
CVSS v3.1 score: 3.8