Any Exploit Code For "CVE-2018-7600"
#1
Hello There , 
I wonder if there's any exploit code for this CVE .
- if there's , please post it here .
Ty .
Heart
Reply
#2
If you want, you could try work this out yourself, maybe?

I'm not a Drupal expert, so I know very little about how the code is structured. However, I have heard the following things:

1. This is a code injection attack
2. It works due to flaws in the sanitization methods applied to URL endpoints (so I hear)

In any case, there is a patch, and the patch is very small. Even without a public exploit, we can reverse-engineer their patch by discovering what vulnerability it fixes, and then write our own code to exploit it.

I'll leave this thread alone for a while whilst I try to do the same thing, but I encourage everyone who's interested to try the same exercise!

--- EDIT

Might have a good thread going here, so adding some SEO keywords so maybe we can attract high quality users who know this topic Smile

CVE-2018-7600
CVE-2018-7600 POC exploit
CVE-2018-7600 Drupal 7
CVE-2018-7600 hacking
Reply
#3
Okay... Soooo, here's what we know:

Quote:"The problem is that Drupal core accepts request parameters as array objects. While this is functionally no different than many other frameworks, a malicious user can pass an array object to the application with a keynote containing a payload. Drupal will process this without sanitization. A successful exploit attempt results in the potential compromise of the application and even the underlying operating system." (Source: https://www.tenable.com/blog/critical-dr...ed-to-know)

G0rx demonstrates what the above means, when he proposes that a potential exploit would like like:
Code:
index.php?page['#payload']=home.php
(Source: https://github.com/g0rx/CVE-2018-7600-Drupal-RCE)

So now let's look at the code... First we have to decide what version to focus on: 7 or 8? Shodan seems to think there are 81,067 Drupal 7's walking around, and 108,993 Drupal 8's walking around.

Now, if I take a peak on Twitter, I do note that most people are looking at Drupal 7 code (Example: https://twitter.com/bendiken/status/979076199875006465). I can only assume this is because Drupal 7 has been around for longer, and Drupal 8 substantially restructures how he platform operations. In my case, because I don't know anything about either, I'm going to focus on Drupal 8; and as you can see, it holds the most potentially for successful exploits and so is objectively the most lucrative target.

According to GitHub, Dupal 8.5.1 contains 363 commits. Luckily, narrowing them down is easily done. By looking at the file changes, I simply search for the word "sanitize", as I know this is at the crux of the vulnerability. When you know what you're looking for, the relevant changes are easy to spot:

1. New file created: core/lib/Drupal/Core/Security/RequestSanitizer.php
2. Changes to core/lib/Drupal/Core/DrupalKernel.php to basically include a reference to the library above

As far as I can tell, that is the extent of the changes. Also note that I've linked in the PHP files for the current version, as we will need to look at what they're filtering for in order to work out what our exploit will look like.

Also bear in mind that it's been some 4 days since this was published, and there isn't yet a publicly available exploit... So the answer is possibly very difficult.
Reply
#4
Hmm. I now also think that another reason other people are looking at Drupal 7 is because the attack surface on Drupal 8 is a lot smaller.

Consider this function in DupalKernal.php:

Code:
public function prepareLegacyRequest(Request $request) {
 $this->boot();
 $this->preHandle($request);
 // Setup services which are normally initialized from within stack
 // middleware or during the request kernel event.
 if (PHP_SAPI !== 'cli') {
   $request->setSession($this->container->get('session'));
 }
 $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('<none>'));
 $request->attributes->set(RouteObjectInterface::ROUTE_NAME, '<none>');
 $this->container->get('request_stack')->push($request);
 $this->container->get('router.request_context')->fromRequest($request);
 return $this;
}

The preHandle() function links to the RequestSanitizer.php library, but note the language for this function "LegacyRequest". When I search for this function on GitHub, I get very few results and it only appears to be called during installation, etc., where there aren't user-provided variables in play.

Ah, in fact I have now found this:

Code:
  /**
   * Prepare the kernel for handling a request without handling the request.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return $this
   *
   * @deprecated in Drupal 8.0.x and will be removed before 9.0.0. Only used by
   *   legacy front-controller scripts.
   */
  public function prepareLegacyRequest(Request $request);

...This is what I mean when I say I'm not a Drupal developer, or I would know about these things! Time to focus on Drupal 7 instead...

So now that I've taken the long way round (sorry about that, but it's all part of the problem solving problem - if you don't go down at least one dead-end, you're doing it wrong... Either that or you're just way smarter than me :/ ) , here's what you need to focus on:

1. includes/bootstrap.inc modified
2. Added includes/request-sanitizer.inc

This is very very similar to what changed for the Drupal 8 patch. It's a little easier to understand in Drupal 7, because the code structure is more 'naturalistic'.

Basically, bootstrap.inc just does this:

Code:
  // Sanitize unsafe keys from the request.
  require_once DRUPAL_ROOT . '/includes/request-sanitizer.inc';
  DrupalRequestSanitizer::sanitize();

From the sanitization library, this is the juicy part that governs the logic for whether a given key should be dropped from the $_GET or $_POST arrays (the variable $key here is derived from the $_GET array passed through a foreach loop to iterate each set of keys and values).

Code:
 if ($key !== '' && $key[0] === '#' && !in_array($key, $whitelist, TRUE))

SO, the the $key is not blank, and the $key IS a # and the $key is not part of the whitelisted values... Then drop the key.

In other words, keys that === # are bad, according to Drupal.

This presumably means that an exploit has to take the form of:

Code:
www.example.org/index.php?#={exploit}

Now, I'm completely unsure what to do next.

Sorry if I'm spamming this forum, I'm just emptying the things in my head right now. If people don't find it helpful/entertaining/informing then please tell me and I'll stop Smile
Reply
#5
Getting closer now...

I focus particularly on this:
Code:
if ($key !== '' && $key[0] === '#' && !in_array($key, $whitelist, TRUE))

Note the $key[0], this means we are explicitly not looking at $key[1..2..3, etc]. This is interesting because we can actually stack $_GET requests like this into multidimensional arrays:

Code:
www.example.org/index.php?value[]=hello&value[]=goodbye

This will create a $_GET requests structured like the below:

Code:
[
    'value' =>
       [
            '0'  => 'hello',
            '1'  => 'goodbye'
       [
]

And as you can see, Drupal is only concerned with this when the FIRST ARRAY KEY = 0 (in our example, where the value is 'hello'). If it finds a hash key as the first key, it will not only drop that particular array, but will unset the whole $_GET parameter.

Now, it didn't take much Googling to figure out that the # key is a special Drupal character reserved for use in their form API (I've never come across the # key being special in requests before, so I figured it must be an implementation unique to Drupal). Refer to: https://api.drupal.org/api/drupal/develo...e.html/7.x

So my new guess is that an exploit will look like this:

Code:
www.example.com/{api}?#[]={api_endpoint}&#[]={exploit_code}

I could be so far off the mark and might look like an idiot when the exploit code eventually comes out... But my basic theory is that is makes use of stacking the parameters into multidimensional arrays and the # key used in the Drupal API.

Anyway, the point is that I am approaching the problem in a certain way and based off a limited understanding, so hopefully when the answer does become public I can learn from it some more Smile
Reply
#6
Hi,
I'm a Drupal developer, and although I know Drupal's eco system very good, I am still not sure how to exploit this one.

I am not an experienced penetration tester, I'm only doing it for fun, but maybe more hacking experience is what I need here, so I am willing to bring my Drupal knowledge if you'll bring the rest.

@EnigmaCookie, I think that you are pretty close.
Drupal is using # for attributes and properties of "renderable" arrays and form API.
Renderable array is being used by the theme layer, and looks something like:
Code:
array(
  "#type" => "markup", // could also be link, table and lot of other options
  "value" => "...some html"
)

Form API is the more interesting here, since by looking at includes/forms.inc you'll see that it takes the value from the inputs (from $_GET or $_POST) and add it to a variable called $form_state["input"].
form state is a variable that being used after a form is submitted during the validation or submission process, but every Drupal beginner knows not to trust $form_state["input"].
The one to use for the real stuff is $form_state["values"], so maybe we should look there, where Drupal prepares the values after the sanitation (that we now can assume that isn't that perfect).

Another thing that you should know is that Drupal has some attributes in FAPI (Form API) like #process, #submit, #validate, #pre_render (and more) that receives an array of callback (function names) and executes those function on the right place (e.g #validate when validating the element / form).
My last paragraph sounds really important, but again - I'm still didn't find the place where Drupal will use something an attacker may inject in $form_state["input"]["{input name}"]["process"].

I really hopes that helps, and I also really wants to find a POC so any help is appreciated.
Reply
#7
(03-31-2018, 08:36 PM)rreiss Wrote: Hi,
I'm a Drupal developer, and although I know Drupal's eco system very good, I am still not sure how to exploit this one.

I am not an experienced penetration tester, I'm only doing it for fun, but maybe more hacking experience is what I need here, so I am willing to bring my Drupal knowledge if you'll bring the rest.

@EnigmaCookie, I think that you are pretty close.
Drupal is using # for attributes and properties of "renderable" arrays and form API.
Renderable array is being used by the theme layer, and looks something like:
array(
 "#type" => "markup", // could also be link, table and lot of other options
 "value" => "...some html"
)

Form API is the more interesting here, since by looking at includes/forms.inc you'll see that it takes the value from the inputs (from $_GET or $_POST) and add it to a variable called $form_state["input"].
form state is a variable that being used after a form is submitted during the validation or submission process, but every Drupal beginner knows not to trust $form_state["input"].
The one to use for the real stuff is $form_state["values"], so maybe we should look there, where Drupal prepares the values after the sanitation (that we now can assume that isn't that perfect).

Another thing that you should know is that Drupal has some attributes in FAPI (Form API) like #process, #submit, #validate, #pre_render (and more) that receives an array of callback (function names) and executes those function on the right place (e.g #validate when validating the element / form).
My last paragraph sounds really important, but again - I'm still didn't find the place where Drupal will use something an attacker may inject in $form_state["input"]["{input name}"]["process"].

I really hopes that helps, and I also really wants to find a POC so any help is appreciated.

Awesome, thanks for contributing. I will look at the code for the Form API tomorrow morning.

The thing I don't get is, as you mention, there are "action" words beginning with hash keys, like #type, #process, etc. However, the code I've looked at wouldn't touch those sorts of values, unless they were in the format:

Code:
www.example.org/api/?#=process

In this case it would drop the key because of the presence of the hash key, and literally everything else is let through, unless the $key[0] is a hash. But because it drops the hash, we have to assume that the value associated with the hash much be processed somewhere and it could have malicious results... But even saying that, the value of the key is never touched in the sanitization script! In fact, this function will only ever block a key that === '#', so '#process' will be let through...

So I will have to look at the API specifications and see how the GET and POST requests need to be formatted, cause I feel like I'm missing something at the moment. It just doesn't make sense!
Reply
#8
I think that one of us missed a basic thing (I hope it's not me Smile )
The new sanitation code is:
$key[0] === '#' ,and the function is recursive, so the new sanitation function works on $ key which is a string, and therefore index 0 is #, but the whole key isn't necessarily only #.
Reply
#9
Code:
protected static function stripDangerousValues($input, array $whitelist, array &$sanitized_keys) {
   if (is_array($input)) {
     foreach ($input as $key => $value) {
       if ($key !== '' && $key[0] === '#' && !in_array($key, $whitelist, TRUE)) {
         unset($input[$key]);
         $sanitized_keys[] = $key;
       }
       else {
         $input[$key] = self::stripDangerousValues($input[$key], $whitelist, $sanitized_keys);
       }
     }
   }
   return $input;
 }

The function is taking each $_GET request ($input) in to a foreach{} loop with each iteration of the array being assigned to $key => $value

This means you can have 100 GET pairs values (test=1, test2=3, test3=4), and it checks the key of each one to see if it === '#'.

As I was discussing before, the presence of the $key[0] is to specify the root $key for the iteration of the $_GET parameter, to avoid stacked requests (see a few of my posts up). That's why I think the vulnerability is a combination of the form API and parameter stacking.

My problem is that the value corresponding to the key in the array is never sanitized, and the "whitelist" that they've included appears useless, as it will never ever get used, as the only thing that will trigger sanitisation is if $key == '#'  -so what is there to whitelist?

I would expect to see values like #progress, #submit, etc, in this whitelist, but these values will already go through the function no problem. If this function checked to see if the FIRST LETTER of the key was a hash, it would make perfect sense, as you could then do /?#action=data and make all the actions starting with # in your whitelist... But that's not what this function does, so I'm very confused.
Reply
#10
Dam it, now I've got myself tangled up. I don't know how this happened, as I wan't even aware that PHP had this functionality (I thought it was a python thing), but the $key[0] isn't choosing the first array within that key (so if you had /index.php?array[]=value1&array[]=value2&array[]=value3 then $key[0] should choose the key corresponding to "value1", or so I thought) it's choosing the first letter of the key value.

I think I just had a massive brain meltdown because I was adamant that PHP can't do this sort of string manipulate. Someone please correct me or provide context, but either way, I have done a test (below) to verify that this is the case.

This of course changes everything, and means the whitelist now makes sense.

The other thing I've realised, is that it's impossible to pass a # character as a GET request, it needs to be a POST parameter. I've tried using # in a GET in the command line with no luck, but POST works easily. It's odd that they still use the same saniziation for GET requests, but I'm not sure if this is part of HTTP or some sort of client implementation, so it's possibly just to be extra-certain.

Here is my testing script for this function, I've been throwing POST parameters to understand how it works a bit better.

Code:
<?php

$input = $_POST;
$whitelist = ['test', 'test2', 'test3'];
$sanitized_keys = [];

$output = stripDangerousValues($input, $whitelist, $sanitized_keys);

function stripDangerousValues($input, array $whitelist, array $sanitized_keys)
{
    if(is_array($input))
    {
        foreach($input as $key => $value)
        {

            if($key !== '' && $key[0] === '#' && !in_array($key, $whitelist, TRUE))
            {
                unset($input[$key]);
                $sanitized_keys[] = $key;
            }

            else
            {
                $input[$key] = stripDangerousValues($input[$key], $whitelist, $sanitized_keys);
            }
        }
    }

    return $input;
}



?>

Input was:
<pre>
<?php print_r($input); ?>
</pre>

Output was:
<pre>
<?php print_r($output); ?>
</pre>

$key[0] was:
<pre>
<?php
foreach($input as $key => $value)
{
    echo $key . " => " . $key[0] . "\n";
}
?>
</pre>
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Is there any link for SANS SEC488 Xterminat0r 0 53 Yesterday, 11:09 AM
Last Post: Xterminat0r
  Exploit Tutorials Insider 15 51,130 07-08-2020, 04:38 PM
Last Post: rektard
  [Podcast] D0rkerDevil talks about bug bounties & cve's LaZr4us 0 4,959 06-13-2020, 05:24 AM
Last Post: LaZr4us
  GICSP SANS 410 Pdf & mp3 2018 syh4ck 0 4,905 04-13-2020, 10:31 AM
Last Post: syh4ck