lib/msf/core/exploit/remote/kerberos/client/pac.rb
# -*- coding: binary -*-
require 'rex/proto/kerberos/pac/krb5_pac'
module Msf
class Exploit
class Remote
module Kerberos
module Client
module Pac
# Builds a kerberos PA-PAC-REQUEST pre authenticated structure
#
# @param opts [Hash{Symbol => Boolean}]
# @option opts [Boolean] :pac_request_value
# @return [Rex::Proto::Kerberos::Model::Field::PreAuthDataEntry]
# @see Rex::Proto::Kerberos::Model::PreAuthPacRequest
# @see Rex::Proto::Kerberos::Model::PreAuthDataEntry
def build_pa_pac_request(opts = {})
value = opts[:pac_request_value] || false
pac_request = Rex::Proto::Kerberos::Model::PreAuthPacRequest.new(value: value)
pa_pac_request = Rex::Proto::Kerberos::Model::PreAuthDataEntry.new(
type: Rex::Proto::Kerberos::Model::PreAuthType::PA_PAC_REQUEST,
value: pac_request.encode
)
pa_pac_request
end
# Builds a kerberos PACTYPE structure
#
# @param opts [Hash{Symbol => <String, Integer, Array, Time>}]
# @option opts [String] :client_name
# @option opts [Integer] :user_id the user SID Ex: 1000
# @option opts [Integer] :group_id Ex: 513 for 'Domain Users'
# @option opts [Array<Integer>] :group_ids
# @option opts [Array<String>] :extra_sids An array of extra sids, Ex: `['S-1-5-etc-etc-519']`
# @option opts [String] :realm
# @option opts [String] :domain_id the domain SID Ex: S-1-5-21-1755879683-3641577184-3486455962
# @option opts [Time] :auth_time
# @option opts[String] :checksum_enc_key Encryption key for calculating the checksum
# @option opts[Boolean] :is_golden Include requestor and pac attributes in the PAC (needed for golden tickets; not for silver)
# @return [Rex::Proto::Kerberos::Pac::Krb5Pac]
# @see Rex::Proto::Kerberos::Pac::Krb5PacLogonInfo
# @see Rex::Proto::Kerberos::Pac::Krb5PacClientInfo
# @see Rex::Proto::Kerberos::Pac::Krb5PacServerChecksum
# @see Rex::Proto::Kerberos::Pac::Krb5PacPrivSvrChecksum
# @see Rex::Proto::Kerberos::Pac::Krb5Pac
def build_pac(opts = {})
user_name = opts[:client_name] || ''
user_id = opts[:user_id] || Rex::Proto::Kerberos::Pac::DEFAULT_ADMIN_RID
primary_group_id = opts[:group_id] || Rex::Proto::Kerberos::Pac::DOMAIN_USERS
group_ids = opts[:group_ids] || [Rex::Proto::Kerberos::Pac::DOMAIN_USERS]
extra_sids = opts[:extra_sids] || []
logon_domain_name = opts[:logon_domain_name] || opts[:realm] || ''
logon_count = opts.fetch(:logon_count) { 0 }
password_last_set = opts.fetch(:password_last_set) { nil }
domain_id = opts[:domain_id] || Rex::Proto::Kerberos::Pac::NT_AUTHORITY_SID
auth_time = opts[:auth_time] || Time.now
checksum_type = opts[:checksum_type] || Rex::Proto::Kerberos::Crypto::Checksum::RSA_MD5
ticket_checksum = opts[:ticket_checksum] || nil
is_golden = opts.fetch(:is_golden) { true }
base_vi = opts[:base_verification_info]
upn_dns_info_pac_element = opts[:upn_dns_info_pac_element]
obj_opts = {
logon_time: auth_time,
effective_name: user_name,
user_id: user_id,
primary_group_id: primary_group_id,
logon_domain_name: logon_domain_name,
logon_domain_id: domain_id,
logon_count: logon_count,
full_name: '',
logon_script: '',
profile_path: '',
home_directory: '',
home_directory_drive: '',
logon_server: '',
password_last_set: password_last_set
}
unless base_vi.nil?
obj_opts.merge({
full_name: base_vi.full_name,
logon_script: base_vi.logon_script,
profile_path: base_vi.profile_path,
home_directory: base_vi.home_directory,
home_directory_drive: base_vi.home_directory_drive,
logon_server: base_vi.logon_server,
logon_count: base_vi.logon_count,
bad_password_count: base_vi.bad_password_count,
user_account_control: base_vi.user_account_control,
sub_auth_status: base_vi.sub_auth_status,
last_successful_i_logon: base_vi.last_successful_i_logon,
last_failed_i_logon: base_vi.last_failed_i_logon,
failed_i_logon_count: base_vi.failed_i_logon_count
})
end
validation_info = Rex::Proto::Kerberos::Pac::Krb5ValidationInfo.new(**obj_opts)
validation_info.group_ids = group_ids
if extra_sids && extra_sids.length > 0
validation_info.extra_sids = extra_sids.map do |sid|
{ sid: sid, attributes: Rex::Proto::Kerberos::Pac::SE_GROUP_ALL }
end
end
logon_info = Rex::Proto::Kerberos::Pac::Krb5LogonInformation.new(
data: validation_info
)
client_info = Rex::Proto::Kerberos::Pac::Krb5ClientInfo.new(
client_id: auth_time,
name: user_name
)
if is_golden
pac_requestor = Rex::Proto::Kerberos::Pac::Krb5PacRequestor.new(
user_sid: "#{domain_id}-#{user_id}"
)
pac_attributes = Rex::Proto::Kerberos::Pac::Krb5PacAttributes.new
end
server_checksum = Rex::Proto::Kerberos::Pac::Krb5PacServerChecksum.new(
signature_type: checksum_type
)
priv_srv_checksum = Rex::Proto::Kerberos::Pac::Krb5PacPrivServerChecksum.new(
signature_type: checksum_type
)
pac_elements = [
logon_info,
client_info
]
unless upn_dns_info_pac_element.nil?
pac_elements.append(upn_dns_info_pac_element)
end
if is_golden
# These PAC elements are required for golden tickets in post-October 2022 systems
pac_elements.append(
pac_requestor,
pac_attributes
)
end
pac_elements.append(
server_checksum,
priv_srv_checksum
)
pac_elements << ticket_checksum unless ticket_checksum.nil?
pac_type = Rex::Proto::Kerberos::Pac::Krb5Pac.new
pac_type.assign(pac_elements: pac_elements)
pac_type.sign!(service_key: opts[:checksum_enc_key])
pac_type
end
# Builds an kerberos AuthorizationData structure containing a PACTYPE
#
# @param opts [Hash{Symbol => Rex::Proto::Kerberos::Pac::Type}]
# @option opts [Rex::Proto::Kerberos::Pac::Type] :pac
# @return [Rex::Proto::Kerberos::Model::AuthorizationData]
# @see Rex::Proto::Kerberos::Model::AuthorizationData
def build_pac_authorization_data(opts = {})
pac = opts[:pac] || build_pac(opts)
pac_auth_data = Rex::Proto::Kerberos::Model::AuthorizationData.new(
elements: [{ type: Rex::Proto::Kerberos::Pac::AD_WIN2K_PAC, data: pac.to_binary_s}]
)
authorization_data = Rex::Proto::Kerberos::Model::AuthorizationData.new(
elements: [{ type: Rex::Proto::Kerberos::Model::AuthorizationDataType::AD_IF_RELEVANT, data: pac_auth_data.encode }]
)
authorization_data
end
def build_empty_auth_data
pac_auth_data = Rex::Proto::Kerberos::Model::AuthorizationData.new(
elements: [{ type: Rex::Proto::Kerberos::Pac::AD_WIN2K_PAC, data: "\x00" }]
)
Rex::Proto::Kerberos::Model::AuthorizationData.new(
elements: [{ type: Rex::Proto::Kerberos::Model::AuthorizationDataType::AD_IF_RELEVANT, data: pac_auth_data.encode }]
)
end
end
end
end
end
end
end