Описание
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 execution
Executive Summary
For memory-backed blobs (BlobStream), SeekBlob() permits advancing the stream offset beyond the current end without increasing capacity. The subsequent WriteBlob() 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 LP64
-
Paths: MagickCore blob subsystem — BlobStream (
SeekBlob()andWriteBlob()). -
Not required: External delegates; special policies; integer wraparound
Technical Root Cause
Types (LP64):
offset: MagickOffsetType (signed 64-bit)
extent/length/quantum: size_t (unsigned 64-bit)
data: unsigned char*
Contract mismatch:
-
SeekBlob()(BlobStream) updatesoffsetto arbitrary positions, including past end, without capacity adjustment. -
WriteBlob()testsoffset + length >= extentand grows bylength + quantum, doublesquantum, reallocates toextent + 1, then:q = data + (size_t)offset; memmove(q, src, length);There is no guarantee that
extent ≥ offset + lengthpost-growth. Withoffset ≫ extent,qis 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
lengthbytes atoffset, enforceextent ≥ offset + lengthwith overflow-checked arithmetic.
The BlobStream growth policy preserves amortized efficiency but fails to enforce this per-write safety invariant.
Remediation (Principle)
In WriteBlob() (BlobStream case):
-
Checked requirement:
need = (size_t)offset + length;→ ifneed < (size_t)offset, overflow → fail. -
Ensure capacity ≥ need:
target = MagickMax(extent + quantum + length, need);
(Optionally loop, doublingquantum, untilextent ≥ needto preserve amortization.) -
Reallocate to
target + 1before copying; then perform the move.
Companion hardening (recommended):
-
Document or restrict
SeekBlob()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 Checklist
-
Reproduce with a minimal in-memory BlobStream harness under ASan.
-
Apply fix; verify
extent ≥ offset + lengthat 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
needor clean failure.
PoC / Reproduction / Notes
Environment
-
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 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
Пакеты
Magick.NET-Q16-x64
< 14.8.2
14.8.2
Magick.NET-Q8-x64
< 14.8.2
14.8.2
Magick.NET-Q16-HDRI-x64
< 14.8.2
14.8.2
Magick.NET-Q8-OpenMP-x64
< 14.8.2
14.8.2
Magick.NET-Q16-HDRI-OpenMP-x64
< 14.8.2
14.8.2
Magick.NET-Q16-OpenMP-x64
< 14.8.2
14.8.2
Magick.NET-Q8-arm64
< 14.8.2
14.8.2
Magick.NET-Q16-arm64
< 14.8.2
14.8.2
Magick.NET-Q16-OpenMP-arm64
< 14.8.2
14.8.2
Magick.NET-Q8-OpenMP-arm64
< 14.8.2
14.8.2
Magick.NET-Q16-HDRI-OpenMP-arm64
< 14.8.2
14.8.2
Magick.NET-Q16-HDRI-arm64
< 14.8.2
14.8.2
Связанные уязвимости
ImageMagick is free and open-source software used for editing and manipulating digital images. ImageMagick versions lower than 14.8.2 include insecure functions: SeekBlob(), which permits advancing the stream offset beyond the current end without increasing capacity, and WriteBlob(), which 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. This is fixed in version 14.8.2.
ImageMagick is free and open-source software used for editing and manipulating digital images. ImageMagick versions lower than 14.8.2 include insecure functions: SeekBlob(), which permits advancing the stream offset beyond the current end without increasing capacity, and WriteBlob(), which 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. This is fixed in version 14.8.2.
ImageMagick is free and open-source software used for editing and manipulating digital images. ImageMagick versions lower than 14.8.2 include insecure functions: SeekBlob(), which permits advancing the stream offset beyond the current end without increasing capacity, and WriteBlob(), which 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. This is fixed in version 14.8.2.
ImageMagick is free and open-source software used for editing and mani ...