Introduction
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.
Proof-of-concept
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:
%!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
Let's suppose this file bypasses our file-upload filter, due to it's .png
extension, 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 convert
this 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
The Solution: policy.xml
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,
<policymap>
<policy domain="resource" name="area" value="100MP"></policy>
<policy domain="resource" name="width" value="8KP"></policy>
<policy domain="resource" name="time" value="300"></policy>
</policymap>
The first policy declaration will limit the maximum image size in memory to 100MP, larger images are cached to disk. The
width
policy specifies a max image width in pixels, if this is exceeded an exception is thrown. The time
policy 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:
<policymap>
…
<policy domain="coder" rights="none" pattern="PS"></policy>
<policy domain="coder" rights="none" pattern="PS2"></policy>
<policy domain="coder" rights="none" pattern="PS3"></policy>
<policy domain="coder" rights="none" pattern="EPS"></policy>
<policy domain="coder" rights="none" pattern="PDF"></policy>
<policy domain="coder" rights="none" pattern="XPS"></policy>
…
</policymap>
Finding the policy.xml
OK so now we know what we want to add, how do we find this policy.xml
file?
There may be other ways, but here is one recipe that worked for us. Run the following command and inspect the output:
> convert -debug configure logo: null:
…
2022-02-23T11:29:21+00:00 0:00.110 0.000u 6.9.6 Configure convert[10650]: 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[10650]: magic.c/LoadMagicCache/794/Configure
Loading magic configure file "/etc/ImageMagick-6/magic.xml"
If you then inspect each of the paths where ImageMagick is looking for the magic.xml
file and you should find your
policy.xml
file.
Once you have identified a candidate file you can run the following command to list the active policy directives:
> convert -list policy
Path: [built-in]
Policy: Undefined
rights: None
Now make your changes to the candidate policy.xml
file and re-run the convert -list policy
command:
> 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
If you see your changes listed here, then you have updated the correct file and you are good to go.
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.
Summary
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.
Thanks
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.
Comments
There are no existing comments
Got your own view or feedback? Share it with us below …