Most web developers take security very seriously. It is such an essential aspect of the final product that is practically baked into every decision that we make. So when someone hands you a proof-of-concept (PoC) that demonstrates a remote-code execution (RCE) vulnerability, those long-suppressed feelings of inadequacy, buried deep under decades of experience, suddenly burst forth once more. This was my initial emotional response when security researcher Jens Muller reached out to share his PoC showing how a simple image upload feature could be exploited to execute arbitrary commands on the server. Ouch.
The Problem: Unpatched software
As always, the moral of the story is going to be keep your flipping software up-to-date. But let's not waste time beating ourselves up about why we are running out-of-date software, let's try to understand the nature of the vulnerability and, more importantly, how we can plug the hole.
The particular vulnerability exploited in our case was CVE-2019-14811, which applies to Ghostscript versions before 9.50. Ghostscript is an interpreter for Postscript (PS) and PDF files. This vulnerability allows specially crafted Postscript files to disable the sandbox protections to which the Ghostscript interpreter is usually subject. With these security protections disabled the script then has access to the file system, or can execute arbitrary commands.
On Linux-based systems you can determine what version of Ghostscript you are running as follows:
> gs -v GPL Ghostscript 9.15 (2014-09-22) Copyright (C) 2014 Artifex Software, Inc. All rights reserved.
We don't process Postscript files. Do we?
In our case we did not intentionally process Postscript files. This ability was baked into ImageMagick (IM), and for the old version of ImageMagick which we were running, this delegation to the Ghostscript interpreter was available by default. Newer versions of ImageMagick included in modern distributions will disable this functionality out of the box, but if you are running on an old version (or depending on where you have installed from) this Ghostscript delegation may still be enabled.
To exploit the vulnerability in this unpatched software we just need to trick the application into passing a Postscript file over to ImageMagick for processing.
We do not intend to publish the actual PoC file in this article. However, we will look at a stripped back example which should illustrate some of the principles, and will allow you to check if you are potentially at risk.
By masquerading a Postscript file as a PNG we can trick any simple file-uploader that relies on an extension filter. After being uploaded,
if this file is passed over to ImageMagick then it, instead, will see the magic bytes at the start of the file and will recognise this as a Postscript file.
As such it will delegate to Ghostscript, provided this has not been disabled. If Ghostscript is running on a version < 9.50 then you will be susceptible
to CVE-2019-14811. As a simple demonstration, consider a file named
poc.png as follows:
Let's suppose this file bypasses our file-upload filter, due to it's
%!PS-Adobe-2.0 EPSF-1.2 /outf (/tmp/ouch.txt) (w) file def outf (This is NOT good news) writestring outf closefile showpage quit %%EOF
.pngextension, and that the file is passed off to ImageMagick where we intend to do some image manipulation. If we run our vulnerable version of ImageMagick, and try to
convertthis faux PNG file, the by-product will be a new file created at
/tmp/ouch.txt. Go ahead and do this now, if a new file is created in your
/tmp/folder there is a good chance you have a problem:
> convert poc.png poc.gif
If you can upgrade to the most recent versions of ImageMagick and Ghostscript the vulnerability being discussed here will likely disappear. Go ahead and do that now if you can. But even if you can upgrade, it may still be prudent to read on.
Vulnerabilities in your dependencies will come and go; as soon as one vulnerability is buried, a new one will arise. Keeping our dependencies up-to-date is how we keep ourselves safe. But as we have seen, this can be difficult, exhausting and a real time-sink. The best way to reduce this workload is to reduce the number of dependencies. As an application developer this is a real practical step towards reducing the attack surface of your application. Just ripping out a dependency is not always easy (or possible), but in this case reducing our attack surface is pretty straightforward, with the help of some ImageMagick configuration.
As ImageMagick offers a ton of functionality, it is pretty common that people only want to expose a subset of that functionality, or to
restrict the operation of ImageMagick in other ways, including its memory allocation and file access rights. The
policy.xml file is used to configure the functionality that
ImageMagick exposes, and to apply limits to help manage its impact on the system upon which it runs. For example,
The first policy declaration will limit the maximum image size in memory to 100MP, larger images are cached to disk. The
widthpolicy specifies a max image width in pixels, if this is exceeded an exception is thrown. The
timepolicy sets a maximum time for a given task to run.
In our case we are interested in restricting the coders that ImageMagick will employ. To prevent any delegation to Ghostscript we can add the following policy directives:
OK so now we know what we want to add, how do we find this
There may be other ways, but here is one recipe that worked for us. Run the following command and inspect the output:
If you then inspect each of the paths where ImageMagick is looking for the
> convert -debug configure logo: null: … 2022-02-23T11:29:21+00:00 0:00.110 0.000u 6.9.6 Configure convert: configure.c/GetConfigureOptions/685/Configure Searching for configure file: "/home/deploy/.magick/magic.xml" 2022-02-23T11:29:21+00:00 0:00.110 0.000u 6.9.6 Configure convert: magic.c/LoadMagicCache/794/Configure Loading magic configure file "/etc/ImageMagick-6/magic.xml"
magic.xmlfile and you should find your
policy.xmlfile. Once you have identified a candidate file you can run the following command to list the active policy directives:
Now make your changes to the candidate
> convert -list policy Path: [built-in] Policy: Undefined rights: None
policy.xmlfile and re-run the
convert -list policycommand:
If you see your changes listed here, then you have updated the correct file and you are good to go.
> convert -list policy Path: /etc/ImageMagick-6/policy.xml … Policy: Coder rights: None pattern: PS Policy: Coder rights: None pattern: PS2 Policy: Coder rights: None pattern: PS3 Policy: Coder rights: None pattern: EPS Policy: Coder rights: None pattern: PDF Policy: Coder rights: None pattern: XPS Path: [built-in] Policy: Undefined rights: None
One final note before we wrap up. If, like us, you have been running with this vulnerability for some time then, after patching, it is recommended that you check your system for backdoors in the event that the vulnerability has already been exploited.
By running old versions of ImageMagick and Ghostscript we left ourselves open to an RCE vulnerability through file uploads. Experience
would suggest that we are probably not alone in this. If you are processing image uploads through ImageMagick, and your
policy.xml has not explicitly disabled delegation to Ghostscript, there is a possibility that you may be vulnerable to RCE
from specially crafted fileuploads. Asides from this particular vulnerability, taking the time to review your
policy.xml is a
simple action that could very well save you some serious headaches down the line.
A very special work of thanks to Jens Muller for the responsible disclosure of this issue, and subsequent help in identfying, recreating and resolving the vulnerability.