Drupal is the second most popular Content Management System (CMS) in the world. According to BuiltWith, more than 637,360 websites currently use Drupal.
On 28 March, the Drupal Security Team announced they identified and patched a critical Remote Code Execution vulnerability (
CVE-2018-7600) affecting all Drupal releases to date. As a matter of urgency, they recommended clients update their Drupal websites to the latest version immediately.
At the moment, Drupalgeddon2 exists in all versions prior to
8.5.1. The Drupal Security Team stated that the risk of
CVE-2018-7600 is scored
24/25 based on the NIST Common Misuse Scoring System, and it is considered highly critical for the following reasons:
- The vulnerability can be triggered by simply sending a
POSTrequest, therefore it is straightforward to detect and exploit
- The attack can be leveraged by an unauthenticated user and it does not require any level of privilege
- There is a high likelihood of attack since this vulnerability exists in default and common module configurations and it is easy to automate
- The impact is critical.
Soon after the announcement of the vulnerability, proof of concept code (POC) was made publicly available on Github by a Russian security researcher. Quickly after that, threat intelligence services started to notice exploitation attempts in the wild. Hackers used this vulnerability mainly to mine cryptocurrencies on visitor's computers, install ransomware, and steal private data such as PII or credentials of the users from affected servers.
The root cause of this vulnerability is related to the Drupal theme rendering system. To create all of its UI elements, Drupal uses
Form API, a powerful tool allowing developers to create forms and handle form submissions quickly and easily. To achieve this, the API uses a hierarchical associative array (Render Array) containing the data that will be rendered, as well as some properties which establish how the data should be rendered.
Let's look at an example. Below is a Render Array:
$output = array(
'first_para' => array(
'#type' => 'markup',
'#markup' => '<p>A paragraph about some stuff…</p>'
'second_para' => array(
'#items' => array('first item', 'second item', 'third item'),
'#theme' => 'item_list',
You can see the associative array. It contains two elements (firstpara and secondpara), both have several parameters. A parameter key can be identified as it always starts with the hashtag
# symbol. The
#type parameter specifies the type of the HTML element (checkbox, textarea, etc.) and the
#markup parameter is used to set HTML that will be output on the form.
The array in the example above is recursively parsed afterward by the Render API and converted into HTML, as shown below.
<div id="block-bartik-content" class="block block-system block-system-main-block">
<p>A paragraph about some stuff…</p>
There are many other parameters that can be used with forms. Some of them provide a way to post-process the rendered output by re-parsing it through a user-supplied function. According to Drupal API documentation, this can be used to cache a view and still have some level of dynamic output.
In an ideal world, the actual output will include HTML comment based tokens, and then the post process can replace those tokens. However, if the user-supplied callback function is not properly validated, a potential attacker might be able to insert malicious functions such as exec, system, eval, etc. to execute system commands, and take over the server. The following four Form API parameters support callback functions and can be leveraged to exploit the CVE-2018–7600 vulnerability:
First, find out what version of Drupal is used by your target. This will help understand whether the target is vulnerable or not and what exploit you should use. The exploit methods differ between Drupal 7 and Drupal 8 as they are using different APIs.
Identify the version
Below are a few methods to identify the version:
Check the HTML HEAD tag
View Source button to analyze the HTML source code of the Target Application. In some cases you will find the version in a meta tag. Do you see the version in this application? It will look like:
<meta name="Generator" content="Drupal 7 (http://drupal.org)">
Check HTTP headers
Use the Proxy to intercept any request to the Target Application and analyze the HTTP response. See if you see the X-Generator header:
X-generator: Drupal 7 (http://drupal.org)
Check if your target has CHANGELOG.txt file
If the developer did not delete CHANGELOG.txt file you should be able to view it by sending a simple get request.
Try going to
http://drupal.com/CHANGELOG.txt to see if it exists and what version it is running.
Other files may disclose the version:
Identify Unauthenticated Forms
The next step is to identify unauthenticated forms (e.g. login/register form, password reset form) since those paths can be used to exploit the vulnerability. To demonstrate the vulnerability, you can use /?q=user/password path which corresponds to the password reset form.
Triggering the vulnerability requires two steps:
- Injecting the malicious data through a POST request.
- Use the built-in caching mechanism of Drupal to retrieve the output.
The following diagram illustrates how Drupal handle the form rendering:
First, the API checks if the form exists in the cache and if it has a unique id it exists and it is unnecessary to prepare the form. Next it goes to the build phase and at this point, the form structured array is complete. If there are no errors on the validation phase it will redirect the user to a page and display a success message. Otherwise, it will redirect the user back to the form and remove only failed items.
For example, let's suppose you are trying to register, you fill in all of the form inputs (username, first name, last name, email, phone, etc.), and you hit Submit. You then get an error message saying that the username is already taken. At this point all valid inputs are accepted and completed so you only have to submit the username again.
This happens because Drupal is using a cache mechanism to temporary save the forms into a database. During the next step of the form submission those cached values are retrieved and processed. As mentioned above, we will take advantage of this mechanism to leverage the exploit.
The initial POST request generates an error and the form containing the malicious code is saved to the database. We then retrieve the output through another POST request.