lib/msf/core/post/windows/lsa.rb
# -*- coding: binary -*-
require 'rex/proto/ms_dtyp'
module Msf
class Post
module Windows
module Lsa
def initialize(info = {})
super(
update_info(
info,
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
stdapi_railgun_api
stdapi_railgun_memread
stdapi_railgun_memwrite
]
}
}
)
)
end
# [UNICODE_STRING structure (subauth.h)](https://learn.microsoft.com/en-us/windows/win32/api/subauth/ns-subauth-unicode_string)
class UNICODE_STRING_x64 < BinData::Record
endian :little
uint16 :len
uint16 :maximum_len
uint64 :buffer, byte_align: 8
end
# [UNICODE_STRING structure (subauth.h)](https://learn.microsoft.com/en-us/windows/win32/api/subauth/ns-subauth-unicode_string)
class UNICODE_STRING_x86 < BinData::Record
endian :little
uint16 :len
uint16 :maximum_len
uint32 :buffer, byte_align: 4
end
# [LSA_LAST_INTER_LOGON_INFO structure (ntsecapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-lsa_last_inter_logon_info)
class LSA_LAST_INTER_LOGON_INFO < BinData::Record
endian :little
search_prefix :ms_dtyp
large_integer :last_successful_logon
large_integer :last_failed_logon
uint32 :failed_attempt_count_since_last_successful_logon
end
# [LSA_STRING structure (lsalookup.h)](https://learn.microsoft.com/en-us/windows/win32/api/lsalookup/ns-lsalookup-lsa_string)
class LSA_STRING_x64 < BinData::Record
endian :little
uint16 :len
uint16 :maximum_len
uint64 :buffer, byte_align: 8
end
# [LSA_STRING structure (lsalookup.h)](https://learn.microsoft.com/en-us/windows/win32/api/lsalookup/ns-lsalookup-lsa_string)
class LSA_STRING_x86 < BinData::Record
endian :little
uint16 :len
uint16 :maximum_len
uint32 :buffer, byte_align: 4
end
# [LSA_UNICODE_STRING structure (lsalookup.h)](https://learn.microsoft.com/en-us/windows/win32/api/lsalookup/ns-lsalookup-lsa_unicode_string)
class LSA_UNICODE_STRING_x64 < BinData::Record
endian :little
uint16 :len
uint16 :maximum_len
uint64 :buffer, byte_align: 8
end
# [LSA_UNICODE_STRING structure (lsalookup.h)](https://learn.microsoft.com/en-us/windows/win32/api/lsalookup/ns-lsalookup-lsa_unicode_string)
class LSA_UNICODE_STRING_x86 < BinData::Record
endian :little
uint16 :len
uint16 :maximum_len
uint32 :buffer, byte_align: 4
end
# [KERB_CRYPTO_KEY structure (ntsecapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-kerb_crypto_key)
class KERB_CRYPTO_KEY_x64 < BinData::Record
endian :little
int32 :key_type
uint32 :len
uint64 :val
end
# [KERB_CRYPTO_KEY structure (ntsecapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-kerb_crypto_key)
class KERB_CRYPTO_KEY_x86 < BinData::Record
endian :little
int32 :key_type
uint32 :len
uint32 :val
end
# [KERB_EXTERNAL_TICKET](https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-kerb_external_ticket)
class KERB_EXTERNAL_TICKET_x64 < BinData::Record
endian :little
search_prefix :ms_dtyp
uint64 :service_name
uint64 :target_name
uint64 :client_name
unicode_string_x64 :domain_name
unicode_string_x64 :target_domain_name
unicode_string_x64 :alt_target_domain_name
kerb_crypto_key_x64 :session_key
uint32 :ticket_flags
uint32 :flags
large_integer :key_expiration_time
large_integer :start_time
large_integer :end_time
large_integer :renew_until
large_integer :time_skew
uint32 :encoded_ticket_size
uint64 :encoded_ticket, byte_align: 8
end
# [KERB_EXTERNAL_TICKET](https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-kerb_external_ticket)
class KERB_EXTERNAL_TICKET_x86 < BinData::Record
endian :little
search_prefix :ms_dtyp
uint32 :service_name
uint32 :target_name
uint32 :client_name
unicode_string_x86 :domain_name
unicode_string_x86 :target_domain_name
unicode_string_x86 :alt_target_domain_name
kerb_crypto_key_x86 :session_key
uint32 :ticket_flags
uint32 :flags
large_integer :key_expiration_time
large_integer :start_time
large_integer :end_time
large_integer :renew_until
large_integer :time_skew
uint32 :encoded_ticket_size
uint32 :encoded_ticket, byte_align: 4
end
# https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Security/Authentication/Identity/struct.KERB_TICKET_CACHE_INFO_EX.html
class KERB_TICKET_CACHE_INFO_EX_x64 < BinData::Record
endian :little
search_prefix :ms_dtyp
lsa_unicode_string_x64 :client_name
lsa_unicode_string_x64 :client_realm
lsa_unicode_string_x64 :server_name
lsa_unicode_string_x64 :server_realm
large_integer :start_time
large_integer :end_time
large_integer :renew_time
int32 :encryption_type
uint32 :ticket_flags
end
# https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Security/Authentication/Identity/struct.KERB_TICKET_CACHE_INFO_EX.html
class KERB_TICKET_CACHE_INFO_EX_x86 < BinData::Record
endian :little
search_prefix :ms_dtyp
lsa_unicode_string_x86 :client_name
lsa_unicode_string_x86 :client_realm
lsa_unicode_string_x86 :server_name
lsa_unicode_string_x86 :server_realm
large_integer :start_time
large_integer :end_time
large_integer :renew_time
int32 :encryption_type
uint32 :ticket_flags
end
# [KERB_QUERY_TKT_CACHE_REQUEST structure (ntsecapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-kerb_query_tkt_cache_request)
class KERB_QUERY_TKT_CACHE_REQUEST < BinData::Record
endian :little
search_prefix :ms_dtyp
uint32 :message_type
luid :logon_id
end
# [KERB_QUERY_TKT_CACHE_RESPONSE structure (ntsecapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-kerb_query_tkt_cache_response)
class KERB_QUERY_TKT_CACHE_RESPONSE_x64 < BinData::Record
endian :little
uint32 :message_type
uint32 :count_of_tickets
array :tickets, type: :kerb_ticket_cache_info_ex_x64, initial_length: :count_of_tickets
end
# [KERB_QUERY_TKT_CACHE_RESPONSE structure (ntsecapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-kerb_query_tkt_cache_response)
class KERB_QUERY_TKT_CACHE_RESPONSE_x86 < BinData::Record
endian :little
uint32 :message_type
uint32 :count_of_tickets
array :tickets, type: :kerb_ticket_cache_info_ex_x86, initial_length: :count_of_tickets
end
# [KERB_RETRIEVE_TKT_REQUEST structure (ntsecapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-kerb_retrieve_tkt_request)
class KERB_RETRIEVE_TKT_REQUEST_x64 < BinData::Record
endian :little
search_prefix :ms_dtyp
uint32 :message_type
luid :logon_id
lsa_unicode_string_x64 :target_name, byte_align: 8
uint32 :ticket_flags
uint32 :cache_options
int32 :encryption_type
struct :credentials_handle, byte_align: 8 do # SecHandle, see: https://learn.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-sechandle
uint64 :dw_lower
uint64 :dw_upper
end
end
# [KERB_RETRIEVE_TKT_REQUEST structure (ntsecapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-kerb_retrieve_tkt_request)
class KERB_RETRIEVE_TKT_REQUEST_x86 < BinData::Record
endian :little
search_prefix :ms_dtyp
uint32 :message_type
luid :logon_id
lsa_unicode_string_x86 :target_name, byte_align: 4
uint32 :ticket_flags
uint32 :cache_options
int32 :encryption_type
struct :credentials_handle, byte_align: 4 do # SecHandle, see: https://learn.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-sechandle
uint64 :dw_lower
uint64 :dw_upper
end
end
# [KERB_RETRIEVE_TKT_RESPONSE structure (ntsecapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-kerb_retrieve_tkt_response)
class KERB_RETRIEVE_TKT_RESPONSE_x64 < BinData::Record
endian :little
kerb_external_ticket_x64 :ticket
end
# [KERB_RETRIEVE_TKT_RESPONSE structure (ntsecapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-kerb_retrieve_tkt_response)
class KERB_RETRIEVE_TKT_RESPONSE_x86 < BinData::Record
endian :little
kerb_external_ticket_x86 :ticket
end
# [SECURITY_LOGON_SESSION_DATA structure (ntsecapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-security_logon_session_data)
class SECURITY_LOGON_SESSION_DATA_x64 < BinData::Record
endian :little
search_prefix :ms_dtyp
uint32 :len
luid :logon_id
lsa_unicode_string_x64 :user_name, byte_align: 8
lsa_unicode_string_x64 :logon_domain
lsa_unicode_string_x64 :authentication_package
uint32 :logon_type
uint32 :session
uint64 :psid
large_integer :logon_time
lsa_unicode_string_x64 :logon_server
lsa_unicode_string_x64 :dns_domain_name
lsa_unicode_string_x64 :upn
uint32 :user_flags
lsa_last_inter_logon_info :last_logon_info, byte_align: 8
lsa_unicode_string_x64 :logon_script
lsa_unicode_string_x64 :profile_path
lsa_unicode_string_x64 :home_directory
lsa_unicode_string_x64 :home_directory_drive
large_integer :logoff_time
large_integer :kick_off_time
large_integer :password_last_set
large_integer :password_can_change
large_integer :password_must_change
end
# [SECURITY_LOGON_SESSION_DATA structure (ntsecapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-security_logon_session_data)
class SECURITY_LOGON_SESSION_DATA_x86 < BinData::Record
endian :little
search_prefix :ms_dtyp
uint32 :len
luid :logon_id
lsa_unicode_string_x86 :user_name, byte_align: 4
lsa_unicode_string_x86 :logon_domain
lsa_unicode_string_x86 :authentication_package
uint32 :logon_type
uint32 :session
uint32 :psid
large_integer :logon_time
lsa_unicode_string_x86 :logon_server
lsa_unicode_string_x86 :dns_domain_name
lsa_unicode_string_x86 :upn
uint32 :user_flags
lsa_last_inter_logon_info :last_logon_info, byte_align: 4
lsa_unicode_string_x86 :logon_script
lsa_unicode_string_x86 :profile_path
lsa_unicode_string_x86 :home_directory
lsa_unicode_string_x86 :home_directory_drive
large_integer :logoff_time
large_integer :kick_off_time
large_integer :password_last_set
large_integer :password_can_change
large_integer :password_must_change
end
# [TOKEN_STATISTICS structure (winnt.h)](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-token_statistics)
class TOKEN_STATISTICS < BinData::Record
endian :little
search_prefix :ms_dtyp
luid :token_id
luid :authentication_id
large_integer :expiration_time
int32 :token_type
int32 :impersonation_level
uint32 :dynamic_charged
uint32 :dynamic_available
uint32 :group_count
uint32 :privilege_count
luid :modified_id
end
LsaPointer = Struct.new(:value, :contents)
# Initialize a new LSA_STRING instance in memory.
#
# @param [String] string The string value to place in memory.
def lsa_string(string)
case session.native_arch
when ARCH_X64
klass = LSA_STRING_x64
when ARCH_X86
klass = LSA_STRING_x86
else
raise NotImplementedError, "Unsupported session architecture: #{session.native_arch}"
end
ptr = session.railgun.util.alloc_and_write_string(string)
return nil if ptr.nil?
klass.new(len: string.length, maximum_len: string.length + 1, buffer: ptr)
end
# Initialize a new LSA_UNICODE_STRING instance in memory.
#
# @param [String] string The string value to place in memory.
def lsa_unicode_string(string)
case session.native_arch
when ARCH_X64
klass = LSA_UNICODE_STRING_x64
when ARCH_X86
klass = LSA_UNICODE_STRING_x86
else
raise NotImplementedError, "Unsupported session architecture: #{session.native_arch}"
end
ptr = session.railgun.util.alloc_and_write_string(string)
return nil if ptr.nil?
klass.new(len: string.length, maximum_len: string.length + 2, buffer: ptr)
end
# Read an LSA_UNICODE_STRING from memory.
#
# @param [LSA_UNICODE_STRING] str The LSA_UNICODE_STRING to read from memory.
def read_lsa_unicode_string(str)
return '' if str.len == 0
# the len field is in bytes, divide by two because #read_wstring takes chars
session.railgun.util.read_wstring(str.buffer, str.len / 2)
end
def lsa_call_authentication_package(handle, auth_package, submit_buffer, submit_buffer_length: nil)
if auth_package.is_a?(String)
auth_package = lsa_lookup_authentication_package(handle, auth_package)
return nil if auth_package.nil?
end
submit_buffer = submit_buffer.to_binary_s if submit_buffer.is_a?(BinData::Struct)
if submit_buffer_length.nil?
submit_buffer_length = submit_buffer.length
end
result = session.railgun.secur32.LsaCallAuthenticationPackage(
handle,
auth_package,
submit_buffer,
submit_buffer_length,
4,
4,
4
)
unless result['return'] == ::WindowsError::NTStatus::STATUS_SUCCESS
status = ::WindowsError::NTStatus.find_by_retval(result['return']).first
print_error("Failed to call the authentication package. LsaCallAuthenticationPackage failed with: #{status}")
return nil
end
unless result['ProtocolStatus'] == ::WindowsError::NTStatus::STATUS_SUCCESS
status = lsa_nt_status_to_win_error(result['ProtocolStatus'])
print_error("Failed to call the authentication package. LsaCallAuthenticationPackage authentication package failed with: #{status}")
return nil
end
return nil if result['ProtocolReturnBuffer'] == 0
LsaPointer.new(result['ProtocolReturnBuffer'], session.railgun.memread(result['ProtocolReturnBuffer'], result['ReturnBufferLength']))
end
def lsa_connect_untrusted
result = session.railgun.secur32.LsaConnectUntrusted(4)
unless result['return'] == ::WindowsError::NTStatus::STATUS_SUCCESS
status = ::WindowsError::NTStatus.find_by_retval(result['return']).first
print_error("Failed to obtain a handle to LSA. LsaConnectUntrusted failed with: #{status.to_s}")
return nil
end
result['LsaHandle']
end
def lsa_deregister_logon_process(handle)
result = session.railgun.secur32.LsaDeregisterLogonProcess(handle)
unless result['return'] == ::WindowsError::NTStatus::STATUS_SUCCESS
status = ::WindowsError::NTStatus.find_by_retval(result['return']).first
print_error("Failed to close the handle to LSA. LsaDeregisterLogonProcess failed with: #{status.to_s}")
return nil
end
true
end
def lsa_enumerate_logon_sessions
result = session.railgun.secur32.LsaEnumerateLogonSessions(4, 4)
unless result['return'] == ::WindowsError::NTStatus::STATUS_SUCCESS
status = ::WindowsError::NTStatus.find_by_retval(result['return']).first
print_error("Failed to enumerate logon sessions. LsaEnumerateLogonSessions failed with: #{status.to_s}")
return nil
end
return [] if result['LogonSessionCount'] == 0
luids = BinData::Array.new(type: :ms_dtyp_luid, initial_length: result['LogonSessionCount'])
luids.read(session.railgun.memread(result['LogonSessionList'], luids.num_bytes))
session.railgun.secur32.LsaFreeReturnBuffer(result['LogonSessionList'])
luids
end
def lsa_get_logon_session_data(luid)
case session.native_arch
when ARCH_X64
logon_session_data = SECURITY_LOGON_SESSION_DATA_x64.new
result = session.railgun.secur32.LsaGetLogonSessionData(luid, 8)
when ARCH_X86
logon_session_data = SECURITY_LOGON_SESSION_DATA_x86.new
result = session.railgun.secur32.LsaGetLogonSessionData(luid, 4)
else
raise NotImplementedError, "Unsupported session architecture: #{session.native_arch}"
end
unless result['return'] == ::WindowsError::NTStatus::STATUS_SUCCESS
status = ::WindowsError::NTStatus.find_by_retval(result['return']).first
print_error("Failed to obtain logon session data. LsaGetLogonSessionData failed with: #{status.to_s}")
return nil
end
logon_session_data.read(session.railgun.memread(result['ppLogonSessionData'], logon_session_data.num_bytes))
LsaPointer.new(result['ppLogonSessionData'], logon_session_data)
end
def lsa_lookup_authentication_package(handle, package_name)
package_name = lsa_string(package_name)
return nil if package_name.nil?
result = session.railgun.secur32.LsaLookupAuthenticationPackage(handle, package_name, 4)
session.railgun.util.free_string(package_name.buffer)
unless result['return'] == ::WindowsError::NTStatus::STATUS_SUCCESS
status = ::WindowsError::NTStatus.find_by_retval(result['return']).first
print_error("Failed to lookup the authentication package. LsaLookupAuthenticationPackage failed with: #{status.to_s}")
return nil
end
result['AuthenticationPackage']
end
def lsa_nt_status_to_win_error(nt_status)
::WindowsError::Win32.find_by_retval(session.railgun.advapi32.LsaNtStatusToWinError(nt_status)['return']).first
end
def lsa_register_logon_process
logon_process_name = lsa_string('Winlogon')
return nil if logon_process_name.nil?
result = session.railgun.secur32.LsaRegisterLogonProcess(logon_process_name.to_binary_s, 4, 4)
session.railgun.util.free_string(logon_process_name.buffer)
unless result['return'] == ::WindowsError::NTStatus::STATUS_SUCCESS
status = ::WindowsError::NTStatus.find_by_retval(result['return']).first
print_error("Failed to obtain a handle to LSA. LsaRegisterLogonProcess failed with: #{status.to_s}")
return nil
end
result['LsaHandle']
end
end
end
end
end