ImageMagick
A tool/library for converting and editing images of many formats, with some older versions having known vulnerabilities
Last updated
A tool/library for converting and editing images of many formats, with some older versions having known vulnerabilities
Last updated
convert -version
to check version
Release Archive for testing
When providing convert
with an input, it is possible to use a URL that will be fetched by it. It does so using the following command template:
Here, %M
is the input URL, which can be command-injected by either command substitution or simply escaping the "
quotes:
By forcing ImageMagick to request this URL, we can execute arbitrary commands:
While the above trick is useful if you have control over the input URL, this is not always the case. To make this more exploitable we can trigger the same behavior through MVG syntax. This has a url()
function that will fetch with the same command injection vulnerability:
Simply uploading and converting this file alone will now trigger the vulnerability:
profile
File Read - CVE-2022-44268 (< 7.1.0-50 = Oct 2022)This writeup explains a vulnerability in ImageMagick that allowed an input file to contain malicious metadata including a filename, and the output file would contain the content of that file on the remote server. Exploiting it is very simple:
Create the file: Take any PNG file, and add a profile
tEXt
chunk to it with a filename that is the file you wish to read:
Upload the file to your target so ImageMagick will parse it
During the conversion, the following warning should appear confirming the vulnerability:
Download the result and extract the data: With the file downloaded, another tEXt
or zTXt
(compressed) chunk is added containing the content as data.
vid:msl:
path RCE (< 7.1.0-40 = Jul 2022)While researching Arbitrary Objection Instantiations researchers found an interesting trick in ImageMagick that allowed writing files when parsing an image with a specific path. To be able to perform this attack you require control over the start of the path that convert
tries to parse:
Magick Scripting Language (MSL) is a special schema and file that ImageMagick supports to script certain actions while converting. By prefixing a path with msl:
, the file is interpreted as this scripting language which performs sensitive actions like reading and writing files:
Using caption:<?php @eval(@$_REQUEST['a']); ?>
as the filename
for <read>
here would even bypass the need for a server hosting content, as will be used in the Exploitation phase:
We need to somehow get this file on the target so we can reference it. Luckily PHP has a default configuration of saving all uploaded files temporarily in the /tmp
folder so the code can handle it. It does not matter if the code actually does something with $_FILES
, PHP will always save files from any request that has files.
The name for these files is generated, like /tmp/php3r1Y4p
, which should be random so you can't guess its path. The first problem is that with enough attempts this path can still be guessed in a reasonable time. But ImageMagick takes this one step further and allows wildcards using VID that enable it to match all files with the known pattern, eliminating the need for guessing. That makes the final path: vid:msl:/tmp/php*
One last caveat is the fact that the <read>
image in the MSL file needs to be in a valid image format that ImageMagick can parse, otherwise, it won't write. This is easily circumvented however by just putting code in the metadata for the file to remain valid:
To exploit this behavior in a standard PHP application that throws our input into the new Imagick(...)
constructor, we need the following things:
If you are unsure about the file path, use vid:
together with *
wildcards to match the file
Prefix the path with msl:
to let convert
interpret it as a script
For PHP, use Content-Type: multipart/form-data
and add a file with the malicious content to create a temporary file
Sending this all in one go may look something like this:
Before going directory to exploitation, you can also test if it would be possible to control the start of the path by providing a common Linux path like:
If this accesses the Ubuntu logo, and the ImageMagick version is old enough, the above is very likely to work
When a server runs convert
on your input, you may have control over some of its parameters. It might keep the filename as you had input it, or allow you to specify modifiers like crop
or resize
. These can all lead to injecting more malicious arguments to leak data or even take over the server.
TEXT:
If you have control over the start of the file path and you can include /
slashes in your input path, the TEXT:
prefix can read the target file and show its contents in the output as text on white:
Note: This path can also be relative, so it may be used with ../
or simply another filename to read its contents as text
-write
The special -write [filename]
argument to convert
will write the image with its options up until the argument to a path. For this, you need to be able to inject a single option and value.
If this specific string is blocked, or if the -
dash is not allowed, +write [filename]
will essentially do the same thing and can work as an alternative!
One big caveat is that the file must be a valid image. Otherwise, ImageMagick will refuse to write the output.
Luckily, it understands many formats with useful quirks, like BMP which easily allows you to insert any content in RGB data while remaining valid. A useful polyglot to make with this is writing to the ~/.ssh/authorized_keys
file in order to log in via SSH. This file is surprisingly error-tolerant and will only split it with \n
newlines and interpret every chunk as a possible key. This means that if we have our key "\nssh-rsa AAAAB3NzaC1yc2E...uipd2wIDAQAB\n"
somewhere in it, we are able to log in.
The script below creates a file with an SSH public key embedded as plain text, so that if it takes authorized_keys
's place, it will allow an attacker to log in:
If we then use this file to inject arguments, ImageMagick will happily parse and write the file:
= output.png