Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/rex/proto/kerberos/pac/krb5_pac.rb
Views: 11766
# frozen_string_literal: true12require 'bindata'3require 'ruby_smb/dcerpc'4require 'rex/proto/ms_dtyp'56# full MIDL spec for PAC7# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/1d4912dd-5115-4124-94b6-fa414add575f8module Rex::Proto::Kerberos::Pac9# https://github.com/rapid7/metasploit-framework/blob/b2eb348d943af25adfc41e6fa689d9da00154685/lib/rex/proto/kerberos/crypto.rb#L37-L4210# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/6e95edd3-af93-41d4-8303-6c795529731511CHECKSUM_SIGNATURE_LENGTH = {12# Used by: modules/auxiliary/admin/kerberos/ms14_068_kerberos_checksum.rb.13# Not defined in the specification explicitly, but the exploit uses a weaker checksum to bypass Microsoft's PAC security methods14Rex::Proto::Kerberos::Crypto::Checksum::RSA_MD5 => 16,15Rex::Proto::Kerberos::Crypto::Checksum::SHA1_AES128 => 12,16Rex::Proto::Kerberos::Crypto::Checksum::SHA1_AES256 => 12,17Rex::Proto::Kerberos::Crypto::Checksum::HMAC_MD5 => 16,180xffffff76 => 16 # Negative 138 two's complement (HMAC_MD5)19}.freeze2021class CypherBlock < RubySMB::Dcerpc::Ndr::NdrStruct22default_parameter byte_align: 123ndr_fixed_byte_array :data, initial_length: 824end2526class UserSessionKey < RubySMB::Dcerpc::Ndr::NdrStruct27default_parameter byte_align: 128endian :little2930# @!attribute [rw] session_key31# @return [Integer]32ndr_fix_array :session_key, initial_length: 2, type: :cypher_block33end3435class Krb5SidAndAttributes < RubySMB::Dcerpc::Ndr::NdrStruct36default_parameters byte_align: 437prpc_sid :sid38ndr_uint32 :attributes39end4041# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/311aab27-ebdf-47f7-b939-13dc99b1534142#43# [SIDATT] https://learn.microsoft.com/en-gb/windows/win32/api/winnt/ns-winnt-token_groups?redirectedfrom=MSDN44class GroupAttributes < BinData::Record45endian :big46472.times do48bit1 :"_reserved_#{self.fields.length}"49end5051# @!attribute [rw] resource52# @return [BinData::Bit1] This setting means that the group is a domain-local or resource group. Corresponds to SE_GROUP_RESOURCE. For more information, see [SIDATT].53bit1 :resource545525.times do56bit1 :"_reserved_#{self.fields.length}"57end5859# @!attribute [rw] owner60# @return [BinData::Bit1] This setting means that the group can be assigned as an owner of a resource. Corresponds to SE_GROUP_OWNER. For more information, see [SIDATT].61bit1 :owner6263# @!attribute [rw] enabled64# @return [BinData::Bit1] This setting means that the group is enabled for use. Corresponds to SE_GROUP_ENABLED. For more information, see [SIDATT].65bit1 :enabled6667# @!attribute [rw] enabled_by_default68# @return [BinData::Bit1] This setting means that the group is marked as enabled by default. Corresponds to SE_GROUP_ENABLED_BY_DEFAULT. For more information, see [SIDATT].69bit1 :enabled_by_default7071# @!attribute [rw] mandatory72# @return [BinData::Bit1] This setting means that the group is mandatory for the user and cannot be disabled. Corresponds to SE_GROUP_MANDATORY. For more information, see [SIDATT].73bit1 :mandatory74end7576# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/69e86ccc-85e3-41b9-b514-7d969cd0ed7377class UserFlagAttributes < BinData::Record78endian :big798018.times do81bit1 :"_reserved_#{self.fields.length}"82end8384# @!attribute [rw] used_lmv2_auth_and_ntlmv2_session_key85# @return [BinData::Bit1] The LMv2 response from the LmChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used for authentication and the NTLMv2 response from the NtChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used session key generation.86bit1 :used_lmv2_auth_and_ntlmv2_session_key8788# @!attribute [rw] used_lmv2_auth_and_session_key89# @return [BinData::Bit1] The LMv2 response from the LmChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used for authentication and session key generation.90bit1 :used_lmv2_auth_and_session_key9192# @!attribute [rw] used_ntlmv2_auth_and_session_key93# @return [BinData::Bit1] The NTLMv2 response from the NtChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used for authentication and session key generation.94bit1 :used_ntlmv2_auth_and_session_key9596# @!attribute [rw] profile_path_populated97# @return [BinData::Bit1] Indicates that ProfilePath is populated.98bit1 :profile_path_populated99100# @!attribute [rw] resource_group_ids101# @return [BinData::Bit1] Indicates that the ResourceGroupIds field is populated.102bit1 :resource_group_ids103104# @!attribute [rw] accepts_ntlmv2105# @return [BinData::Bit1] Indicates that the domain controller understands NTLMv2.106bit1 :accepts_ntlmv2107108# @!attribute [rw] machine_account109# @return [BinData::Bit1] Indicates that the account is a machine account.110bit1 :machine_account111112# @!attribute [rw] sub_authentication113# @return [BinData::Bit1] Sub-authentication used; session key came from the sub-authentication package.114bit1 :sub_authentication115116# @!attribute [rw] extra_sids117# @return [BinData::Bit1] Indicates that the ExtraSids field is populated and contains additional SIDs.118bit1 :extra_sids1191201.times do121bit1 :"_reserved_#{self.fields.length}"122end123124# @!attribute [rw] lan_manager125# @return [BinData::Bit1] LAN Manager key was used for authentication.126bit1 :lan_manager1271281.times do129bit1 :"_reserved_#{self.fields.length}"130end131132# @!attribute [rw] no_encryption133# @return [BinData::Bit1] No encryption is available.134bit1 :no_encryption135136# @!attribute [rw] guest137# @return [BinData::Bit1] Authentication was done via the GUEST account; no password was used138bit1 :guest139end140141# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/b10cfda1-f24f-441b-8f43-80cb93e786ec142#143# [RFC4120] https://www.rfc-editor.org/rfc/rfc4120144# [RFC3961] https://www.ietf.org/rfc/rfc3961.txt145# [MS-KILE] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/2a32282e-dd48-4ad9-a542-609804b02cc9146# [MS-LSAD] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lsad/1b5471ef-4c33-4a91-b079-dfcbb82f05cc147class UserAccountAttributes < BinData::Record148endian :big14915010.times do151bit1 :"_reserved_#{self.fields.length}"152end153154# @!attribute [rw] use_aes_keys155# @return [BinData::Bit1] This bit is ignored by clients and servers.156bit1 :use_aes_keys157158# @!attribute [rw] partial_secrets_account159# @return [BinData::Bit1] Specifies that the object is a read-only domain controller (RODC).160bit1 :partial_secrets_account161162# @!attribute [rw] no_auth_data_required163# @return [BinData::Bit1] This bit is used by the Kerberos protocol. It indicates that when the key distribution center (KDC) is issuing a service ticket for this account, the privilege attribute certificate (PAC) is not to be included. For more information, see [RFC4120].164bit1 :no_auth_data_required165166# @!attribute [rw] trusted_to_authenticate_for_delegation167# @return [BinData::Bit1] This bit is used by the Kerberos protocol, as specified in [MS-KILE] section 3.3.1.1.168bit1 :trusted_to_authenticate_for_delegation169170# @!attribute [rw] password_expired171# @return [BinData::Bit1] Specifies that the password age on the user has exceeded the maximum password age policy.172bit1 :password_expired173174# @!attribute [rw] dont_require_preauth175# @return [BinData::Bit1] This bit is used by the Kerberos protocol. It indicates that the account is not required to present valid preauthentication data, as described in [RFC4120] section 7.5.2.176bit1 :dont_require_preauth177178# @!attribute [rw] use_des_key_only179# @return [BinData::Bit1] This bit is used by the Kerberos protocol. It indicates that only des-cbc-md5 or des-cbc-crc keys (as defined in [RFC3961]) are used in the Kerberos protocol for this account.180bit1 :use_des_key_only181182# @!attribute [rw] not_delegated183# @return [BinData::Bit1] This bit is used by the Kerberos protocol. It indicates that the ticket-granting tickets (TGTs) of this account and the service tickets obtained by this account are not marked as forwardable or proxiable when the forwardable or proxiable ticket flags are requestedFor more information, see [RFC4120].184bit1 :not_delegated185186# @!attribute [rw] trusted_for_delegation187# @return [BinData::Bit1] This bit is used by the Kerberos protocol. It indicates that the "OK as Delegate" ticket flag (described in[RFC4120] section 2.8) is to be set.188bit1 :trusted_for_delegation189190# @!attribute [rw] smartcard_required191# @return [BinData::Bit1] Specifies that the user can authenticate only with a smart card.192bit1 :smartcard_required193194# @!attribute [rw] encrypted_test_password_allowed195# @return [BinData::Bit1] Specifies that the cleartext password is to be persisted.196bit1 :encrypted_test_password_allowed197198# @!attribute [rw] account_auto_lock199# @return [BinData::Bit1] Specifies that the account has been locked out.200bit1 :account_auto_lock201202# @!attribute [rw] dont_expire_password203# @return [BinData::Bit1] Specifies that the maximum-password-age policy does not apply to this user.204bit1 :dont_expire_password205206# @!attribute [rw] server_trust_account207# @return [BinData::Bit1] Specifies that the object is a DC.208bit1 :server_trust_account209210# @!attribute [rw] workstation_trust_account211# @return [BinData::Bit1] Specifies that the object is a member workstation or server.212bit1 :workstation_trust_account213214# @!attribute [rw] interdomain_trust_account215# @return [BinData::Bit1] Specifies that the object represents a trust object. For more information about trust objects, see [MS-LSAD].216bit1 :interdomain_trust_account217218# @!attribute [rw] mns_logon_account219# @return [BinData::Bit1] This bit is ignored by clients and servers.220bit1 :mns_logon_account221222# @!attribute [rw] normal_account223# @return [BinData::Bit1] Specifies that the user is not a computer object.224bit1 :normal_account225226# @!attribute [rw] temp_duplicate_account227# @return [BinData::Bit1] This bit is ignored by clients and servers.228bit1 :temp_duplicate_account229230# @!attribute [rw] password_not_required231# @return [BinData::Bit1] Specifies that the password-length policy does not apply to this user.232bit1 :password_not_required233234# @!attribute [rw] home_directory_required235# @return [BinData::Bit1] Specifies that the homeDirectory attribute is required.236bit1 :home_directory_required237238# @!attribute [rw] account_disabled239# @return [BinData::Bit1] Specifies that the account is not enabled for authentication.240bit1 :account_disabled241end242243# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/1c0d6e11-6443-4846-b744-f9f810a504eb244#245# [MS-ADA3] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-ada3/4517e835-3ee6-44d4-bb95-a94b6966bfb0246class UpnDnsInfoAttributes < BinData::Record247endian :big24824930.times do250bit1 :"_reserved_#{self.fields.length}"251end252253# @!attribute [rw] sam_name_and_sid254# @return [BinData::Bit1] The UPN_DNS_INFO structure has been extended with the user account’s SAM Name and SID.255bit1 :sam_name_and_sid256257# @!attribute [rw] upn_name_constructed258# @return [BinData::Bit1] The user account object does not have the userPrincipalName attribute ([MS-ADA3] section 2.349) set. A UPN constructed by concatenating the user name with the DNS domain name of the account domain is provided.259bit1 :upn_name_constructed260end261262class Krb5SidAndAttributesPtr < RubySMB::Dcerpc::Ndr::NdrConfArray263default_parameters byte_align: 1, type: :krb5_sid_and_attributes264265extend RubySMB::Dcerpc::Ndr::PointerClassPlugin266end267268# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/e465cb27-4bc1-4173-8be0-b5fd64dc9ff7269class Krb5ClientInfo < BinData::Record270endian :little271# @!attribute [r] ul_type272# @return [Integer] Describes the type of data present in the buffer273virtual :ul_type, value: Krb5PacElementType::CLIENT_INFORMATION274275# @!attribute [rw] client_id276# @return [FileTime] Kerberos initial ticket-granting ticket (TGT) authentication time277file_time :client_id278279# @!attribute [rw] name_length280# @return [Integer]281uint16 :name_length, initial_value: -> { name.num_bytes }282283# @!attribute [rw] name284# @return [String]285string16 :name, read_length: :name_length286end287288# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/c34adc61-80e1-4920-8923-22ef5054c4b2289class Krb5PacRequestor < BinData::Record290endian :little291# @!attribute [r] ul_type292# @return [Integer] Describes the type of data present in the buffer293virtual :ul_type, value: Krb5PacElementType::PAC_REQUESTOR294295# @!attribute [rw] user_sid296# @return [RPC_SID] SID of the requesting user297ms_dtyp_sid :user_sid298end299300# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/1c7aeadb-8ca4-4050-ae98-0e9834bdd81d301class Krb5PacAttributes < BinData::Record302endian :little303# @!attribute [r] ul_type304# @return [Integer] Describes the type of data present in the buffer305virtual :ul_type, value: Krb5PacElementType::PAC_ATTRIBUTES306307# @!attribute [rw] flags_length308# @return [Integer] Length of the flags field, in bits309uint32 :flags_length, initial_value: 2310311# @!attribute [rw] flags312# @return [Integer] Attribute flags313uint32 :flags, initial_value: PAC_WAS_GIVEN_IMPLICITLY314end315316class PacAttributesFlags < BinData::Record317endian :big31831930.times do320bit1 :"_reserved_#{self.fields.length}"321end322323# @!attribute [rw] pac_was_requested324# @return [BinData::Bit1] The client requested the PAC325bit1 :pac_was_requested326327# @!attribute [rw] pac_was_given_implicitly328# @return [BinData::Bit1] The client did not request or decline a PAC and was given one implicitly329bit1 :pac_was_given_implicitly330end331332class Krb5SignatureType < BinData::Uint32le333# @param [Integer] val The checksum value334# @see Rex::Proto::Kerberos::Crypto::Checksum335def assign(val)336# Handle the scenario of users setting the signature type to a negative value such as -138 for HMAC_RC4337# Convert it to two's complement representation explicitly to bypass bindata's clamping logic in the super method:338if val < 0339val &= 0xffffffff340end341342super(val)343end344end345346class Krb5PacSignatureData < BinData::Record347endian :little348349# @!attribute [rw] signature_type350# @return [Integer] Defines the cryptographic system used to calculate the checksum351# @see Rex::Proto::Kerberos::Crypto::Checksum352krb5_signature_type :signature_type353354# @!attribute [rw] signature355# @return [String]356string :signature, length: -> { CHECKSUM_SIGNATURE_LENGTH.fetch(signature_type) }357end358359class Krb5PacServerChecksum < Krb5PacSignatureData360# @!attribute [r] ul_type361# @return [Integer] Describes the type of data present in the buffer362virtual :ul_type, value: Krb5PacElementType::SERVER_CHECKSUM363end364365class Krb5PacPrivServerChecksum < Krb5PacSignatureData366# @!attribute [r] ul_type367# @return [Integer] Describes the type of data present in the buffer368virtual :ul_type, value: Krb5PacElementType::PRIVILEGE_SERVER_CHECKSUM369end370371class Krb5TicketChecksum < Krb5PacSignatureData372# @!attribute [r] ul_type373# @return [Integer] Describes the type of data present in the buffer374virtual :ul_type, value: Krb5PacElementType::TICKET_CHECKSUM375end376377class Krb5FullPacChecksum < Krb5PacSignatureData378# @!attribute [r] ul_type379# @return [Integer] Describes the type of data present in the buffer380virtual :ul_type, value: Krb5PacElementType::FULL_PAC_CHECKSUM381end382383# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/69e86ccc-85e3-41b9-b514-7d969cd0ed73384class Krb5ValidationInfo < RubySMB::Dcerpc::Ndr::NdrStruct385default_parameters byte_align: 8386387endian :little388389# @!attribute [rw] logon_time390# @return [FileTime] User account's lastLogon attributeÏ391ndr_file_time :logon_time392393# @!attribute [rw] logoff_time394# @return [FileTime] Time the client's logon session is set to expire395ndr_file_time :logoff_time, initial_value: NEVER_EXPIRE396397# @!attribute [rw] kick_off_time398# @return [FileTime] logoff_time minus the user account's forceLogoff attribute399ndr_file_time :kick_off_time, initial_value: NEVER_EXPIRE400401# @!attribute [rw] password_last_set402# @return [FileTime] User account's pwdLastSet attribute403ndr_file_time :password_last_set404405# @!attribute [rw] password_can_change406# @return [FileTime] Time at which the client's password is allowed to change407ndr_file_time :password_can_change408409# @!attribute [rw] password_must_change410# @return [FileTime] Time at which the client's password expires411ndr_file_time :password_must_change, initial_value: NEVER_EXPIRE412413# @!attribute [rw] effective_name414# @return [RpcUnicodeString] User account's samAccountName attribute415rpc_unicode_string :effective_name416417# @!attribute [rw] full_name418# @return [RpcUnicodeString] User account's full name for interactive logon419rpc_unicode_string :full_name420421# @!attribute [rw] logon_script422# @return [RpcUnicodeString] User account's scriptPath attribute423rpc_unicode_string :logon_script424425# @!attribute [rw] profile_path426# @return [RpcUnicodeString] User account's profilePath attribute427rpc_unicode_string :profile_path428429# @!attribute [rw] home_directory430# @return [RpcUnicodeString] User account's HomeDirectory attribute431rpc_unicode_string :home_directory432433# @!attribute [rw] home_directory_drive434# @return [RpcUnicodeString] User account's HomeDrive attribute435rpc_unicode_string :home_directory_drive436437# @!attribute [rw] logon_count438# @return [Integer] User account's LogonCount attribute439ndr_uint16 :logon_count440441# @!attribute [rw] bad_password_count442# @return [Integer] User account's badPwdCount attribute443ndr_uint16 :bad_password_count444445# @!attribute [rw] user_id446# @return [Integer] RID of the account447ndr_uint32 :user_id448449# @!attribute [rw] primary_group_id450# @return [Integer] RID for the primary group to which this account belongs451ndr_uint32 :primary_group_id452453# @!attribute [rw] group_count454# @return [Integer] Number of groups within the account domain to which the account belongs455ndr_uint32 :group_count, initial_value: -> { group_memberships.length }456457# @!attribute [rw] group_memberships458# @return [Integer] List of GROUP_MEMBERSHIP structures that contains the groups to which the account belongs in the account domain459pgroup_membership_array :group_memberships, type: [:group_membership, { byte_align: 4 }]460461# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/69e86ccc-85e3-41b9-b514-7d969cd0ed73462# @!attribute [rw] user_flags463# @return [Integer] A set of bit flags that describe the user's logon information464ndr_uint32 :user_flags,465initial_value: -> do466value = 0467# Bit D: Indicates that the ExtraSids field is populated and contains additional SIDs.468value |= (1 << 5) if self.sid_count > 0469# Bit H: Indicates that the ResourceGroupIds field is populated.470value |= (1 << 9) if self.resource_group_count > 0471value472end473474# @!attribute [rw] user_session_key475# @return [Integer] A session key that is used for cryptographic operations on a session476user_session_key :user_session_key477478# @!attribute [rw] logon_server479# @return [RpcUnicodeString] NetBIOS name of the Kerberos KDC that performed the authentication server (AS) ticket request480rpc_unicode_string :logon_server481482# @!attribute [rw] logon_domain_name483# @return [RpcUnicodeString] NetBIOS name of the domain to which this account belongs484rpc_unicode_string :logon_domain_name485486# @!attribute [rw] logon_domain_id487# @return [Integer] SID for the domain specified in LogonDomainName488prpc_sid :logon_domain_id489490# @!attribute [rw] reserved_1491# @return [Integer] This member is reserved492# ndr_uint64 :reserved_1493ndr_fix_array :reserved_1, initial_length: 2, type: :ndr_uint32494495# @!attribute [rw] user_account_control496# @return [Integer] Set of bit flags that represent information about this account497ndr_uint32 :user_account_control, initial_value: USER_NORMAL_ACCOUNT | USER_DONT_EXPIRE_PASSWORD498499# @!attribute [rw] sub_auth_status500# @return [Integer] Subauthentication package's status code501ndr_uint32 :sub_auth_status502503# @!attribute [rw] last_successful_i_logon504# @return [FileTime] User account's msDS-LastSuccessfulInteractiveLogonTime505ndr_file_time :last_successful_i_logon506507# @!attribute [rw] last_failed_i_logon508# @return [FileTime] User account's msDS-LastFailedInteractiveLogonTime509ndr_file_time :last_failed_i_logon510511# @!attribute [rw] failed_i_logon_count512# @return [Integer] User account's msDS-FailedInteractiveLogonCountAtLastSuccessfulLogon513ndr_uint32 :failed_i_logon_count514515# @!attribute [rw] reserved_3516# @return [Integer] This member is reserved517ndr_uint32 :reserved_3518519# @!attribute [rw] sid_count520# @return [Integer] Total number of SIDs present in the ExtraSids member521ndr_uint32 :sid_count, initial_value: -> { extra_sids.length }522523# @!attribute [rw] extra_sids524# @return [Integer] A list of KERB_SID_AND_ATTRIBUTES structures that contain a list of SIDs525# corresponding to groups in domains other than the account domain to which the principal belongs526krb5_sid_and_attributes_ptr :extra_sids # :extra_sids_ptr527528# @!attribute [rw] resource_group_domain_sid529# @return [Integer] SID of the domain for the server whose resources the client is authenticating to530prpc_sid :resource_group_domain_sid # :resource_group_domain_sid_ptr531532# @!attribute [rw] resource_group_count533# @return [Integer] Number of resource group identifiers stored in ResourceGroupIds534ndr_uint32 :resource_group_count535536# @!attribute [rw] resource_group_ids_ptr537# @return [Integer] Pointer to list of GROUP_MEMBERSHIP structures that contain the RIDs and attributes of the538# account's groups in the resource domain539pgroup_membership_array :resource_group_ids_ptr, type: [:group_membership, { byte_align: 4 }]540541def group_ids=(group_ids)542self.group_memberships = group_ids.map do |id|543{ relative_id: id, attributes: SE_GROUP_ALL }544end545end546end547548class Krb5ValidationInfoPtr < Krb5ValidationInfo549default_parameters byte_align: 8550extend RubySMB::Dcerpc::Ndr::PointerClassPlugin551end552553class Krb5LogonInformation < RubySMB::Dcerpc::Ndr::TypeSerialization1554endian :little555# @!attribute [r] ul_type556# @return [Integer] Describes the type of data present557virtual :ul_type, value: Krb5PacElementType::LOGON_INFORMATION558559krb5_validation_info_ptr :data560end561562class UnknownPacElement < BinData::Record563mandatory_parameter :data_length, :selection564endian :little565566# @!attribute [rw] ul_type567# @return [Integer] Describes the type of data present in the buffer568virtual :ul_type, value: :selection569570# @!attribute [rw] unknown_element571# @return [String] The contents of an unknown Pac Element572string :unknown_element, read_length: :data_length573end574575# See [2.6.4 NTLM_SUPPLEMENTAL_CREDENTIAL](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/39f588d6-21e3-4e09-a9f2-d8f7b9b998bf)576class Krb5NtlmSupplementalCredential < RubySMB::Dcerpc::Ndr::NdrStruct577# The only package name that Microsoft KDCs use is `NTLM`578# See https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/a1c36b00-1fca-415c-a4ca-e66e98844760#Appendix_A_16579PACKAGE_NAME = 'NTLM'.encode('utf-16le').freeze580581default_parameter byte_align: 4582endian :little583584ndr_uint32 :version585ndr_uint32 :flags586ndr_fixed_byte_array :lm_password, initial_length: 16587ndr_fixed_byte_array :nt_password, initial_length: 16588end589590class Krb5SecpkgSupplementalCredByteArrayPtr < RubySMB::Dcerpc::Ndr::NdrConfArray591default_parameters type: :ndr_uint8592extend RubySMB::Dcerpc::Ndr::PointerClassPlugin593end594595# See [2.6.3 SECPKG_SUPPLEMENTAL_CRED](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/50974dc7-6bce-4db5-805b-8dca924ad5a4)596class Krb5SecpkgSupplementalCred < RubySMB::Dcerpc::Ndr::NdrStruct597default_parameter byte_align: 4598endian :little599600rpc_unicode_string :package_name601ndr_uint32 :credential_size602krb5_secpkg_supplemental_cred_byte_array_ptr :credentials603end604605# See [2.6.2 PAC_CREDENTIAL_DATA](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/4927158e-c9d5-493d-a3f6-1826b88d22ba)606class Krb5PacCredentialData < RubySMB::Dcerpc::Ndr::NdrStruct607default_parameter byte_align: 4608endian :little609610ndr_uint32 :credential_count611ndr_conf_array :credentials, type: :krb5_secpkg_supplemental_cred612613# Extract the NTLM hash from the credentials array if present614#615# @return [String, nil] The NTLM hash as "LMHASH:NTHASH" or `nil` if the616# credentials array does not contain any NTLM hash617def extract_ntlm_hash618credential = credentials.find do |credential|619credential.package_name.to_s == Krb5NtlmSupplementalCredential::PACKAGE_NAME620end621return unless credential622623ntlm_creds_raw = credential.credentials.to_ary.pack('C*')624ntlm_creds = Krb5NtlmSupplementalCredential.read(ntlm_creds_raw)625if ntlm_creds.lm_password.any? {|elem| elem != 0}626lm_hash = ntlm_creds.lm_password.to_hex627else628# Empty LMHash629lm_hash = 'aad3b435b51404eeaad3b435b51404ee'630end631nt_hash = ntlm_creds.nt_password.to_hex632633return "#{lm_hash}:#{nt_hash}"634end635end636637class Krb5PacCredentialDataPtr < Krb5PacCredentialData638extend RubySMB::Dcerpc::Ndr::PointerClassPlugin639end640641class Krb5SerializedPacCredentialData < RubySMB::Dcerpc::Ndr::TypeSerialization1642endian :little643644krb5_pac_credential_data_ptr :data645end646647# See [2.6.1 PAC_CREDENTIAL_INFO](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/cc919d0c-f2eb-4f21-b487-080c486d85fe)648class Krb5PacCredentialInfo < BinData::Record649mandatory_parameter :data_length650endian :little651# @!attribute [r] ul_type652# @return [Integer] Describes the type of data present653virtual :ul_type, value: Krb5PacElementType::CREDENTIAL_INFORMATION654655uint32 :version656uint32 :encryption_type657array :serialized_data, type: :uint8, read_until: -> { index == data_length - version.num_bytes - encryption_type.num_bytes - 1 }658659def decrypt_serialized_data(key)660encryptor = Rex::Proto::Kerberos::Crypto::Encryption::from_etype(self.encryption_type)661decrypted_serialized_data = encryptor.decrypt(662self.serialized_data.to_binary_s,663key,664Rex::Proto::Kerberos::Crypto::KeyUsage::KERB_NON_KERB_SALT665)666Krb5SerializedPacCredentialData.read(decrypted_serialized_data)667end668end669670# See [2.10 UPN_DNS_INFO](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/1c0d6e11-6443-4846-b744-f9f810a504eb)671class Krb5UpnDnsInfo < BinData::Record672auto_call_delayed_io673endian :little674# @!attribute [r] ul_type675# @return [Integer] Describes the type of data present in the buffer676virtual :ul_type, value: Krb5PacElementType::USER_PRINCIPAL_NAME_AND_DNS_INFORMATION677678# @!attribute [rw] upn_length679# @return [Integer] The length of the UPN680uint16 :upn_length, value: -> { upn.num_bytes }681682# @!attribute [rw] upn_offset683# @return [Integer] The relative offset of the UPN from the beginning of this structure684uint16 :upn_offset685686# @!attribute [rw] dns_domain_name_length687# @return [Integer] The length of the DNS domain name688uint16 :dns_domain_name_length, value: -> { dns_domain_name.num_bytes }689690# @!attribute [rw] dns_domain_name_offset691# @return [Integer] The relative offset of the DNS domain name from the beginning of this structure692uint16 :dns_domain_name_offset693694# @!attribute [rw] flags695# @return [Integer]696# U flag (bit 0) The user account object does not have the userPrincipalName attribute.697# S flag (bit 1) The structure has been extended with the user account’s SAM Name and SID.698# The remaining bits are ignored.699uint32 :flags700701# @!attribute [rw] sam_name_length702# @return [Integer] The length of the SAM name703# Only available if the S flag is set704uint16 :sam_name_length, value: -> { sam_name.num_bytes }, onlyif: :has_s_flag?705706# @!attribute [rw] sam_name_offset707# @return [Integer] The relative offset of the SAM name from the beginning of this structure708# Only available if the S flag is set709uint16 :sam_name_offset, onlyif: :has_s_flag?710711# @!attribute [rw] sid_length712# @return [Integer] The length of the SID713# Only available if the S flag is set714uint16 :sid_length, value: -> { sid.num_bytes }, onlyif: :has_s_flag?715716# @!attribute [rw] sid_offset717# @return [Integer] The relative offset of the SID from the beginning of this structure718# Only available if the S flag is set719uint16 :sid_offset, onlyif: :has_s_flag?720721# @!attribute [rw] upn722# @return [String] The UPN (User Principal Name) (e.g. [email protected])723delayed_io :upn, read_abs_offset: -> { self.abs_offset + upn_offset } do724string16 read_length: :upn_length725end726727# @!attribute [rw] dns_domain_name728# @return [String] The DNS Domain Name (e.g. WINDOMAIN.LOCAL)729delayed_io :dns_domain_name, read_abs_offset: -> { self.abs_offset + dns_domain_name_offset } do730string16 read_length: :dns_domain_name_length731end732733# @!attribute [rw] sam_name734# @return [String] The SAM Name (e.g. test)735delayed_io :sam_name, read_abs_offset: -> { self.abs_offset + sam_name_offset }, onlyif: -> { has_s_flag? } do736string16 read_length: :sam_name_length737end738739# @!attribute [rw] sid740# @return [MsDtypSid] The SID (e.g. S-1-5-32-544)741delayed_io :sid, read_abs_offset: -> { self.abs_offset + sid_offset }, onlyif: -> { has_s_flag? } do742ms_dtyp_sid743end744745def do_num_bytes746if has_s_flag?747result = sid_offset + sid_length748else749result = dns_domain_name_offset + dns_domain_name_length750end751result752end753754# def initialize_instance(*args)755# super756# set_offsets!757# end758# @return [Boolean] Returns the value of the S flag759def has_s_flag?760flags.anybits?(0b10)761end762763# @param [Boolean] bool The value to set the S flag to764# @return [void]765def set_s_flag(bool)766set_flag_bit(1, bool)767end768769# @return [Boolean] Returns the value of the U flag770def has_u_flag?771flags.anybits?(0b01)772end773774# @param [Boolean] bool The value to set the U flag to775# @return [void]776def set_u_flag(bool)777set_flag_bit(0, bool)778end779780# @param [Integer] upn The relative offset for the upn781# @param [Integer] dns_domain_name The relative offset for the dns_domain_name782# @param [Integer] sam_name The relative offset for the sam_name783# @param [Integer] sid The relative offset for the sid784# @return [void]785#786# Allows you to specify the offsets for the contents, otherwise defaults them787def set_offsets!(upn: nil, dns_domain_name: nil, sam_name: nil, sid: nil)788self.upn_offset = upn || calc_upn_offset789self.dns_domain_name_offset = dns_domain_name || calc_dns_domain_name_offset790self.sam_name_offset = sam_name || calc_sam_name_offset791self.sid_offset = sid || calc_sid_offset792end793794private795796def set_flag_bit(position, bool)797if bool798self.flags |= (1 << position)799else800self.flags &= ~(1 << position)801end802end803804def calc_upn_offset805has_s_flag? ? 24 : 16806end807808def calc_dns_domain_name_offset809upn_offset + upn_length810end811812def calc_sam_name_offset813dns_domain_name_offset + dns_domain_name_length814end815816def calc_sid_offset817sam_name_offset + sam_name_length818end819end820821class Krb5PacElement < BinData::Choice822mandatory_parameter :data_length823824krb5_logon_information Krb5PacElementType::LOGON_INFORMATION825krb5_client_info Krb5PacElementType::CLIENT_INFORMATION826krb5_pac_server_checksum Krb5PacElementType::SERVER_CHECKSUM827krb5_pac_priv_server_checksum Krb5PacElementType::PRIVILEGE_SERVER_CHECKSUM828krb5_ticket_checksum Krb5PacElementType::TICKET_CHECKSUM829krb5_full_pac_checksum Krb5PacElementType::FULL_PAC_CHECKSUM830krb5_pac_credential_info Krb5PacElementType::CREDENTIAL_INFORMATION, data_length: :data_length831krb5_upn_dns_info Krb5PacElementType::USER_PRINCIPAL_NAME_AND_DNS_INFORMATION832krb5_pac_requestor Krb5PacElementType::PAC_REQUESTOR833krb5_pac_attributes Krb5PacElementType::PAC_ATTRIBUTES834unknown_pac_element :default, data_length: :data_length, selection: :selection835end836837class Krb5PacInfoBuffer < BinData::Record838endian :little839840# @!attribute [rw] ul_type841# @return [Integer] Describes the type of data present in the buffer842uint32 :ul_type843844# @!attribute [rw] cb_buffer_size845# @return [Integer]846uint32 :cb_buffer_size, initial_value: -> { buffer.pac_element.num_bytes }847848# @!attribute [rw] offset849# @return [Integer]850uint64 :offset851852delayed_io :buffer, read_abs_offset: :offset do853# @!attribute [rw] pac_element854# @return [Krb5PacElement]855krb5_pac_element :pac_element, selection: -> { ul_type }, data_length: :cb_buffer_size856string :padding, length: -> { num_bytes_to_align(pac_element.num_bytes) }857end858end859860class Krb5Pac < BinData::Record861endian :little862auto_call_delayed_io863864# @!attribute [rw] c_buffers865# @return [Integer]866uint32 :c_buffers, asserted_value: -> { pac_info_buffers.length }867868# @!attribute [r] version869# @return [Integer]870uint32 :version, asserted_value: 0x00000000871872# @!attribute [rw] pac_info_buffers873# @return [Array<Krb5PacInfoBuffer>]874array :pac_info_buffers, type: :krb5_pac_info_buffer, initial_length: :c_buffers875876def assign(val)877case val878when Hash879pac_infos = val[:pac_elements].map do |pac_element|880{ ul_type: pac_element.ul_type, buffer: { pac_element: pac_element } }881end882new_val = val.merge(pac_info_buffers: pac_infos)883super(new_val)884else885super886end887end888889# Calculates the checksums, can only be done after all other fields are set890# @param [String] service_key Service key to calculate the server checksum891# @param [String] krbtgt_key key to calculate priv server (KDC) and full checksums with, if not specified `service_key` is used892#893# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/962edb93-fa3c-48ea-a0a6-062f760eb69c894# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/37bf87b9-1d56-475a-b2b2-e3948b29194d895def calculate_checksums!(service_key: nil, krbtgt_key: nil)896server_checksum = nil897priv_server_checksum = nil898full_pac_checksum = nil899pac_info_buffers.each do |info_buffer|900pac_element = info_buffer.buffer.pac_element901case pac_element.ul_type902when Krb5PacElementType::SERVER_CHECKSUM903server_checksum = pac_element904when Krb5PacElementType::PRIVILEGE_SERVER_CHECKSUM905priv_server_checksum = pac_element906when Krb5PacElementType::FULL_PAC_CHECKSUM907full_pac_checksum = pac_element908else909next910end911end912913missing_checksums = []914missing_checksums << Krb5PacElementType::SERVER_CHECKSUM if server_checksum.nil?915missing_checksums << Krb5PacElementType::PRIVILEGE_SERVER_CHECKSUM if priv_server_checksum.nil?916raise Rex::Proto::Kerberos::Pac::Error::MissingInfoBuffer.new(ul_types: missing_checksums) unless missing_checksums.empty?917918if krbtgt_key.nil?919krbtgt_key = service_key920end921922# https://bugzilla.samba.org/show_bug.cgi?id=15231923# https://i.blackhat.com/EU-22/Thursday-Briefings/EU-22-Tervoort-Breaking-Kerberos-RC4-Cipher-and-Spoofing-Windows-PACs-wp.pdf924if full_pac_checksum925full_pac_checksum.signature = calculate_checksum(full_pac_checksum.signature_type, krbtgt_key, to_binary_s)926end927928server_checksum.signature = calculate_checksum(server_checksum.signature_type, service_key, to_binary_s)929930priv_server_checksum.signature = calculate_checksum(priv_server_checksum.signature_type, krbtgt_key, server_checksum.signature)931end932933# Calculates the offsets for pac_elements if they haven't yet been set934def calculate_offsets!935offset = pac_info_buffers.abs_offset + pac_info_buffers.num_bytes936pac_info_buffers.each do |pac_info|937next unless pac_info.offset == 0938939pac_info.offset = offset940offset += pac_info.cb_buffer_size941offset += num_bytes_to_align(offset)942end943end944945# Call this when you are done setting fields in the object946# in order to finalise the data947def sign!(service_key: nil, krbtgt_key: nil)948calculate_offsets!949calculate_checksums!(service_key: service_key, krbtgt_key: krbtgt_key)950end951952def calculate_checksum(signature_type, key, data)953checksummer = Rex::Proto::Kerberos::Crypto::Checksum.from_checksum_type(signature_type)954checksummer.checksum(key, Rex::Proto::Kerberos::Crypto::KeyUsage::KERB_NON_KERB_CKSUM_SALT, data)955end956957private958959def num_bytes_to_align(n, align: 8)960(align - (n % align)) % align961end962end963end964965966