GitHub Advisoryadvisory · NuGet
GHSA-cjc8-g9w8-chfw imagemagick: heap-buffer overflow read in MNG magnification with alpha
HIGH
Official page ↗
- Aliases
- CVE-2025-55004
- Ecosystem
- NuGet
- Packages
- Magick.NET-Q16-AnyCPU, Magick.NET-Q16-HDRI-AnyCPU, Magick.NET-Q16-HDRI-OpenMP-arm64, Magick.NET-Q16-HDRI-OpenMP-x64, Magick.NET-Q16-HDRI-arm64, Magick.NET-Q16-HDRI-x64, Magick.NET-Q16-HDRI-x86, Magick.NET-Q16-OpenMP-arm64, Magick.NET-Q16-OpenMP-x64, Magick.NET-Q16-arm64, Magick.NET-Q16-x64, Magick.NET-Q16-x86, Magick.NET-Q8-AnyCPU, Magick.NET-Q8-OpenMP-arm64, Magick.NET-Q8-OpenMP-x64, Magick.NET-Q8-arm64, Magick.NET-Q8-x64, Magick.NET-Q8-x86
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:L
- Weaknesses
- CWE-122
- Reviewed
- 2025-08-25
Affected packages
• NuGet Magick.NET-Q16-AnyCPU: < 14.8.0 → fixed in 14.8.0
• NuGet Magick.NET-Q16-HDRI-AnyCPU: < 14.8.0 → fixed in 14.8.0
• NuGet Magick.NET-Q16-HDRI-OpenMP-arm64: < 14.8.0 → fixed in 14.8.0
• NuGet Magick.NET-Q16-HDRI-OpenMP-x64: < 14.8.0 → fixed in 14.8.0
• NuGet Magick.NET-Q16-HDRI-arm64: < 14.8.0 → fixed in 14.8.0
• NuGet Magick.NET-Q16-HDRI-x64: < 14.8.0 → fixed in 14.8.0
• NuGet Magick.NET-Q16-HDRI-x86: < 14.8.0 → fixed in 14.8.0
• NuGet Magick.NET-Q16-OpenMP-arm64: < 14.8.0 → fixed in 14.8.0
• NuGet Magick.NET-Q16-OpenMP-x64: < 14.8.0 → fixed in 14.8.0
• NuGet Magick.NET-Q16-arm64: < 14.8.0 → fixed in 14.8.0
• NuGet Magick.NET-Q16-x64: < 14.8.0 → fixed in 14.8.0
• NuGet Magick.NET-Q16-x86: < 14.8.0 → fixed in 14.8.0
• NuGet Magick.NET-Q8-AnyCPU: < 14.8.0 → fixed in 14.8.0
• NuGet Magick.NET-Q8-OpenMP-arm64: < 14.8.0 → fixed in 14.8.0
• NuGet Magick.NET-Q8-OpenMP-x64: < 14.8.0 → fixed in 14.8.0
• NuGet Magick.NET-Q8-arm64: < 14.8.0 → fixed in 14.8.0
• NuGet Magick.NET-Q8-x64: < 14.8.0 → fixed in 14.8.0
• NuGet Magick.NET-Q8-x86: < 14.8.0 → fixed in 14.8.0
Description
Vulnerability Details
When performing image magnification in `ReadOneMNGIMage` (in `coders/png.c`), there is an issue around the handling of images with separate alpha channels.
When loading an image with a color type that implies a separate alpha channel (ie. `jng_color_type >= 12`), we will load the alpha pixels in this loop:
```c
if (logging != MagickFalse)
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Reading alpha from alpha_blob.");
jng_image=ReadImage(alpha_image_info,exception);
if (jng_image != (Image *) NULL)
for (y=0; y rows; y++)
{
s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
q=GetAuthenticPixels(image,0,y,image->columns,1,exception); // [0]
if ((s == (const Quantum *) NULL) || (q == (Quantum *) NULL))
break;
if (image->alpha_trait != UndefinedPixelTrait)
for (x=(ssize_t) image->columns; x != 0; x--)
{
SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
q+=(ptrdiff_t) GetPixelChannels(image);
s+=(ptrdiff_t) GetPixelChannels(jng_image);
}
else
for (x=(ssize_t) image->columns; x != 0; x--)
{
Quantum
alpha;
alpha=GetPixelRed(jng_image,s);
SetPixelAlpha(image,alpha,q);
if (alpha != OpaqueAlpha)
image->alpha_trait=BlendPixelTrait; // [1]
q+=(ptrdiff_t) GetPixelChannels(image);
s+=(ptrdiff_t) GetPixelChannels(jng_image);
}
if (SyncAuthenticPixels(image,exception) == MagickFalse)
break;
}
```
Note that at \[1\] we update `image->alpha_trait`, but if our alpha image only contains non-opaque pixels in the last row, we do not call `GetAuthenticPixels` (at \[0\]) after this change has been made.
The next call to `GetAuthenticPixels` will then call down into `ResetPixelChannelMap` which adds the new alpha channel to the image channel mappings and metadata.
If we then pass this image into the `MAGN` chunk type, we can see that at \[2\] we calculate the sizes for intermediate buffers `next` and `prev`, before calling `GetAuthenticPixels` at \[4\].
After the call at \[4\], the `image->num_channels` has increased to include the new alpha channel, and now `length` and the previously allocated `next` and `prev` buffers are too small. Fortunately `length` is always used when copying into the buffers, but when reading pixels from the buffers, we call `GetPixelXXX` which assumes the layout of the current image, which requires a larger allocation.
The pixel copying loop will subsequently read beyond the end of the allocation at \[5\].
```c
/* magnify the rows into the right side of the large image */
if (logging != MagickFalse)
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Magnify the rows to %.20g",
(double) large_image->rows);
m=(ssize_t) mng_info->magn_mt;
yy=0;
length=(size_t) GetPixelChannels(image)*image->columns; // [2]
next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
if ((prev == (Quantum *) NULL) ||
(next == (Quantum *) NULL))
{
if (prev != (Quantum *) NULL)
prev=(Quantum *) RelinquishMagickMemory(prev);
if (next != (Quantum *) NULL)
next=(Quantum *) RelinquishMagickMemory(next);
image=DestroyImageList(image);
ThrowReaderException(ResourceLimitError,
"MemoryAllocationFailed");
}
n=GetAuthenticPixels(image,0,0,image->columns,1,exception); // [4]
(void) memcpy(next,n,length);
for (y=0; y rows; y++)
{
if (y == 0)
m=(ssize_t) mng_info->magn_mt;
else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
m=(ssize_t) mng_info->magn_mb;
else if (magn_methy rows-1)
m=(ssize_t) mng_info->magn_mb;
else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
m=1;
else
m=(ssize_t) mng_info->magn_my;
n=prev;
prev=next;
next=n;
if (y rows-1)
{
n=GetAuthenticPixels(image,0,y+1,image->columns,1,
exception);
(void) memcpy(next,n,length);
}
for (i=0; i rows);
pixels=prev;
n=next;
q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
1,exception);
if (q == (Quantum *) NULL)
break;
q+=(ptrdiff_t) (large_image->columns-image->columns)*
GetPixelChannels(large_image);
for (x=(ssize_t) image->columns-1; x >= 0; x--)
{
/* To do: get color as function of indexes[x] */
/*
if (image->storage_class == PseudoClass)
{
}
*/
if (magn_methy I', len(data)) + tag + data + struct.pack('>I', crc)
Simple 128x1 RGB jpeg
jpeg = bytes([
0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
0x01, 0x01, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0xff, 0xdb, 0x00, 0x43,
0x00, 0x03, 0x02, 0x02, 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04,
0x03, 0x03, 0x04, 0x05, 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0a, 0x07,
0x07, 0x06, 0x08, 0x0c, 0x0a, 0x0c, 0x0c, 0x0b, 0x0a, 0x0b, 0x0b, 0x0d,
0x0e, 0x12, 0x10, 0x0d, 0x0e, 0x11, 0x0e, 0x0b, 0x0b, 0x10, 0x16, 0x10,
0x11, 0x13, 0x14…
Related
Alias · CVE (1)
- CVE-2025-55004 ImageMagick is free and open-source software used for editing and manipulating digital images. vulnerability
Weakness · CWE (1)
- CWE-122 Heap-based Buffer Overflow weakness