How to fix errors in the Security Policy for ImageMagick / Imagick Conversion Systems

botond published Jan. 2020, 09, 02:12 p.m. time

Content

 

Introductory

ImageMagick is a free and open source, cross-platform software package designed primarily for displaying, creating, converting, modifying, and editing raster images, but can also handle a variety of vector formats such as EPS (Encapsulated PostScript), AI (Adobe Illustrator) , PDF (Portable Document Format), etc. In total, it supports more than 200 image file formats. And Imagick is a native PHP an extension that allows you to manage different image formats in a web environment using the ImageMagick API. In this quick troubleshooter, we take a look at how to fix errors from the software security policy with a little intervention.

 

 

The symptom

Linux under command line a convert command, or we get an error while creating the Imagick object in PHP on our website. In the first case we get the error in the output of the command, and in the latter we get it with a try-catch structure:

try {
	$image = new Imagick($dimsloc);
} catch ( ImagickException $e ) {
	echo "Hiba: ".$e->getMessage();
	die();
}

And the error messages are like or similar, depending on the image format we wanted to process:

attempt to perform an operation not allowed by the security policy `PS' @ error/constitute.c/IsCoderAuthorized/408
attempt to perform an operation not allowed by the security policy `PS2' @ error/constitute.c/IsCoderAuthorized/408
attempt to perform an operation not allowed by the security policy `PS3' @ error/constitute.c/IsCoderAuthorized/408
attempt to perform an operation not allowed by the security policy `EPS' @ error/constitute.c/IsCoderAuthorized/408
attempt to perform an operation not allowed by the security policy `PDF' @ error/constitute.c/IsCoderAuthorized/408
attempt to perform an operation not allowed by the security policy `XPS' @ error/constitute.c/IsCoderAuthorized/408

etc. ...

 

The solution

We need to look for the solution in the ImageMagick security policy. There is an xml file for this that describes these rules. Depending on the version of ImageMagick, open this file for editing:

For ImageMagick 6 (eg Debian 10):

sudo nano /etc/ImageMagick-6/policy.xml

For ImageMagick 7 (eg Ubuntu 20.04 LTS):

sudo nano /etc/ImageMagick-7/policy.xml

Then scroll to the bottom where you will find:

<policymap>
  <!-- <policy domain="system" name="shred" value="2"/> -->
  <!-- <policy domain="system" name="precision" value="6"/> -->
  <!-- <policy domain="system" name="memory-map" value="anonymous"/> -->
  <!-- <policy domain="system" name="max-memory-request" value="256MiB"/> -->
  <!-- <policy domain="resource" name="temporary-path" value="/tmp"/> -->
  <policy domain="resource" name="memory" value="256MiB"/>
  <policy domain="resource" name="map" value="512MiB"/>
  <policy domain="resource" name="width" value="16KP"/>
  <policy domain="resource" name="height" value="16KP"/>
  <!-- <policy domain="resource" name="list-length" value="128"/> -->
  <policy domain="resource" name="area" value="128MB"/>
  <policy domain="resource" name="disk" value="1GiB"/>
  <!-- <policy domain="resource" name="file" value="768"/> -->
  <!-- <policy domain="resource" name="thread" value="4"/> -->
  <!-- <policy domain="resource" name="throttle" value="0"/> -->
  <!-- <policy domain="resource" name="time" value="3600"/> -->
  <!-- <policy domain="coder" rights="none" pattern="MVG" /> -->
  <!-- <policy domain="module" rights="none" pattern="{PS,PDF,XPS}" /> -->
  <!-- <policy domain="delegate" rights="none" pattern="HTTPS" /> -->
  <!-- <policy domain="path" rights="none" pattern="@*" /> -->
  <!-- <policy domain="cache" name="memory-map" value="anonymous"/> -->
  <!-- <policy domain="cache" name="synchronize" value="True"/> -->
  <!-- <policy domain="cache" name="shared-secret" value="passphrase" stealth="true"/> -->
  <!-- <policy domain="system" name="pixel-cache-memory" value="anonymous"/> -->
  <!-- <policy domain="system" name="shred" value="2"/> -->
  <!-- <policy domain="system" name="precision" value="6"/> -->
  <!-- not needed due to the need to use explicitly by mvg: -->
  <!-- <policy domain="delegate" rights="none" pattern="MVG" /> -->
  <!-- use curl -->
  <policy domain="delegate" rights="none" pattern="URL" />
  <policy domain="delegate" rights="none" pattern="HTTPS" />
  <policy domain="delegate" rights="none" pattern="HTTP" />
  <!-- in order to avoid to get image with password text -->
  <policy domain="path" rights="none" pattern="@*"/>
  <!-- disable ghostscript format types -->
  <policy domain="coder" rights="none" pattern="PS" />
  <policy domain="coder" rights="none" pattern="PS2" />
  <policy domain="coder" rights="none" pattern="PS3" />
  <policy domain="coder" rights="none" pattern="EPS" />
  <policy domain="coder" rights="none" pattern="PDF" />
  <policy domain="coder" rights="none" pattern="XPS" />
</policymap>

 

 

And modify the lower "disable ghostscript format types" section: delete this section or comment and then replace it as follows:

<policymap>
  <!-- <policy domain="system" name="shred" value="2"/> -->
  <!-- <policy domain="system" name="precision" value="6"/> -->
  <!-- <policy domain="system" name="memory-map" value="anonymous"/> -->
  <!-- <policy domain="system" name="max-memory-request" value="256MiB"/> -->
  <!-- <policy domain="resource" name="temporary-path" value="/tmp"/> -->
  <policy domain="resource" name="memory" value="256MiB"/>
  <policy domain="resource" name="map" value="512MiB"/>
  <policy domain="resource" name="width" value="16KP"/>
  <policy domain="resource" name="height" value="16KP"/>
  <!-- <policy domain="resource" name="list-length" value="128"/> -->
  <policy domain="resource" name="area" value="128MB"/>
  <policy domain="resource" name="disk" value="1GiB"/>
  <!-- <policy domain="resource" name="file" value="768"/> -->
  <!-- <policy domain="resource" name="thread" value="4"/> -->
  <!-- <policy domain="resource" name="throttle" value="0"/> -->
  <!-- <policy domain="resource" name="time" value="3600"/> -->
  <!-- <policy domain="coder" rights="none" pattern="MVG" /> -->
  <!-- <policy domain="module" rights="none" pattern="{PS,PDF,XPS}" /> -->
  <!-- <policy domain="delegate" rights="none" pattern="HTTPS" /> -->
  <!-- <policy domain="path" rights="none" pattern="@*" /> -->
  <!-- <policy domain="cache" name="memory-map" value="anonymous"/> -->
  <!-- <policy domain="cache" name="synchronize" value="True"/> -->
  <!-- <policy domain="cache" name="shared-secret" value="passphrase" stealth="true"/> -->
  <!-- <policy domain="system" name="pixel-cache-memory" value="anonymous"/> -->
  <!-- <policy domain="system" name="shred" value="2"/> -->
  <!-- <policy domain="system" name="precision" value="6"/> -->
  <!-- not needed due to the need to use explicitly by mvg: -->
  <!-- <policy domain="delegate" rights="none" pattern="MVG" /> -->
  <!-- use curl -->
  <policy domain="delegate" rights="none" pattern="URL" />
  <policy domain="delegate" rights="none" pattern="HTTPS" />
  <policy domain="delegate" rights="none" pattern="HTTP" />
  <!-- in order to avoid to get image with password text -->
  <policy domain="path" rights="none" pattern="@*"/>
  <!-- disable ghostscript format types -->
<!--
  <policy domain="coder" rights="none" pattern="PS" />
  <policy domain="coder" rights="none" pattern="PS2" />
  <policy domain="coder" rights="none" pattern="PS3" />
  <policy domain="coder" rights="none" pattern="EPS" />
  <policy domain="coder" rights="none" pattern="PDF" />
  <policy domain="coder" rights="none" pattern="XPS" />
-->
  <policy domain="coder" rights="read | write" pattern="PS" />
  <policy domain="coder" rights="read | write" pattern="PS2" />
  <policy domain="coder" rights="read | write" pattern="PS3" />
  <policy domain="coder" rights="read | write" pattern="EPS" />
  <policy domain="coder" rights="read | write" pattern="PDF" />
  <policy domain="coder" rights="read | write" pattern="XPS" />
</policymap>

I chose to comment.

Then, when using the command line, it immediately applies the fresh rules, which you can even query identify command:

identify -list policy
Path: /etc/ImageMagick-6/policy.xml
  Policy: Resource
    name: disk
    value: 1GiB
  Policy: Resource
    name: map
    value: 512MiB
  Policy: Resource
    name: memory
    value: 256MiB
  Policy: Resource
    name: area
    value: 128MB
  Policy: Resource
    name: height
    value: 16KP
  Policy: Resource
    name: width
    value: 16KP
  Policy: Delegate
    rights: None 
    pattern: URL
  Policy: Delegate
    rights: None 
    pattern: HTTPS
  Policy: Delegate
    rights: None 
    pattern: HTTP
  Policy: Path
    rights: None 
    pattern: @*
  Policy: Coder
    rights: Read Write 
    pattern: PS
  Policy: Coder
    rights: Read Write 
    pattern: PS2
  Policy: Coder
    rights: Read Write 
    pattern: PS3
  Policy: Coder
    rights: Read Write 
    pattern: EPS
  Policy: Coder
    rights: Read Write 
    pattern: PDF
  Policy: Coder
    rights: Read Write 
    pattern: XPS

Path: [built-in]
  Policy: Undefined
    rights: None

However, if you get the error in PHP, then after the modification, restart the page running PHP-FPMif the page works.

 

The cause of the problem

I’ll only describe this interestingly, what caused this to be that ImageMagick worked earlier and tossed bugs for a while. A vulnerability was found in the program, which was first remedied by disabling access to the file formats in question in the config file above. Later, the bug was fixed correctly, a security update was released, but the security rules were not restored. So it was necessary to make up for that.

Az imagemagick debian package bug tracking log can be found here. The related CVE kódok: CVE-2019-13300, CVE-2019-13304, CVE-2019-13306, CVE-2019-13307, CVE-2019-15140, CVE-2019-19948

 

 

Conclusion

For me, one plugin on a WordPress-based website uses ImageMagick, which handles vector files. However, the bug was not listed in the plugin, so I only saw the weird "side effects". Then when I started debugging the whole thing, I only came across the error message itself after a long debug, as the error message itself was not printed in the plugin. Then from here on with just a little read, but the solution was found. If I can use this to shorten the search for others - who are in a similar situation - then it’s worth making this description.