Analysis of a WordPress Remote Code Execution Attack
May 21, 2019 • Razvan Costin Ionescu
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:
- Target configuration
- What is the root cause for CVE-2019-8942?
- HTTP requests for exploitation
- Existing exploits
- How can you mitigate the vulnerability?
- Detection using Pentest-Tools.com
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:
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.
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.
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
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:
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:
Now we have to create a new user with Author role:
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
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.
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 (
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:
Upload an image containing PHP code. The image is called image_with_php_code.jpg and it is 262x192 pixels in size.
The response contains the newly created
post_IDfor the image:
Edit the image by changing its metadata,
We crop the image (new sizes: 262x192). This is a fake crop, because the size of the image does not change.
Because the image file now comes with modified metadata, a copy is automatically saved in the
Now we publish a post using the template `cropped-shell.jpg`
The following response is received:
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:
And this is what it looks in the web browser:
At the moment, there are two public exploits implementing this attack.
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.
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:
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:
When the payload is executed, the attacker can run any command using this URL:
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:
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.
In the end, we obtained the following string:
UEQ5d2FIQWdjR0Z6YzNSb2NuVW9KRjlIUlZSYk1GMHBPejgr; then we included the encoded content in the URL and obtained this string:
?0=echo UEQ5d2FIQWdjR0Z6YzNSb2NuVW9KRjlIUlZSYk1GMHBPejgr|base64 -d|base64 -d > shell.php
The newly created backdoor can be called directly from the root folder of WordPress.
Metasploit Module -
The Metasploit module is straightforward and requires credentials to authenticate as an Author to a vulnerable instance of WordPress.
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:
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.
php-imagick you can run the following command in the terminal:
$ sudo apt-get install php-imagick
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.
Short answer: always update your WordPress installation to its latest version.
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
guid fields from the post data array.
_wp_get_allowed_postdata is later called as:
wp_update_post function takes as argument the newly sanitized variable
To better understand the difference in content between
$translated variables, we inserted two hooks in the
post.php file; this redirects the content of both variables to
error_log(“DEBUG - post_data variable: “.var_export($post_data,TRUE));
error_log(“DEBUG - translated variable:”.var_export($translated,TRUE));
It is easy to see that the
$translated variable does not contain the
meta_input field highlighted above.
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.
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.
- Exploiting Magento SQL Injection with Sqlmap
- How to do a Basic Website Vulnerability Assessment with Pentest-Tools.com
- Analysis of a WordPress Remote Code Execution Attack
- Common SQL Injection Attacks
- Exploiting OGNL Injection in Apache Struts
- Inside Nmap, the world’s most famous port scanner
- Pentest report writing in 5 minutes (Defcamp 2018 talk)
- Essential HTTP Headers for Securing Your Web Server
- 5 Practical Scenarios for XSS Attacks
- All posts ...