CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/rex/proto/kerberos/pac/krb5_pac.rb
Views: 11766
1
# frozen_string_literal: true
2
3
require 'bindata'
4
require 'ruby_smb/dcerpc'
5
require 'rex/proto/ms_dtyp'
6
7
# full MIDL spec for PAC
8
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/1d4912dd-5115-4124-94b6-fa414add575f
9
module Rex::Proto::Kerberos::Pac
10
# https://github.com/rapid7/metasploit-framework/blob/b2eb348d943af25adfc41e6fa689d9da00154685/lib/rex/proto/kerberos/crypto.rb#L37-L42
11
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/6e95edd3-af93-41d4-8303-6c7955297315
12
CHECKSUM_SIGNATURE_LENGTH = {
13
# Used by: modules/auxiliary/admin/kerberos/ms14_068_kerberos_checksum.rb.
14
# Not defined in the specification explicitly, but the exploit uses a weaker checksum to bypass Microsoft's PAC security methods
15
Rex::Proto::Kerberos::Crypto::Checksum::RSA_MD5 => 16,
16
Rex::Proto::Kerberos::Crypto::Checksum::SHA1_AES128 => 12,
17
Rex::Proto::Kerberos::Crypto::Checksum::SHA1_AES256 => 12,
18
Rex::Proto::Kerberos::Crypto::Checksum::HMAC_MD5 => 16,
19
0xffffff76 => 16 # Negative 138 two's complement (HMAC_MD5)
20
}.freeze
21
22
class CypherBlock < RubySMB::Dcerpc::Ndr::NdrStruct
23
default_parameter byte_align: 1
24
ndr_fixed_byte_array :data, initial_length: 8
25
end
26
27
class UserSessionKey < RubySMB::Dcerpc::Ndr::NdrStruct
28
default_parameter byte_align: 1
29
endian :little
30
31
# @!attribute [rw] session_key
32
# @return [Integer]
33
ndr_fix_array :session_key, initial_length: 2, type: :cypher_block
34
end
35
36
class Krb5SidAndAttributes < RubySMB::Dcerpc::Ndr::NdrStruct
37
default_parameters byte_align: 4
38
prpc_sid :sid
39
ndr_uint32 :attributes
40
end
41
42
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/311aab27-ebdf-47f7-b939-13dc99b15341
43
#
44
# [SIDATT] https://learn.microsoft.com/en-gb/windows/win32/api/winnt/ns-winnt-token_groups?redirectedfrom=MSDN
45
class GroupAttributes < BinData::Record
46
endian :big
47
48
2.times do
49
bit1 :"_reserved_#{self.fields.length}"
50
end
51
52
# @!attribute [rw] resource
53
# @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].
54
bit1 :resource
55
56
25.times do
57
bit1 :"_reserved_#{self.fields.length}"
58
end
59
60
# @!attribute [rw] owner
61
# @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].
62
bit1 :owner
63
64
# @!attribute [rw] enabled
65
# @return [BinData::Bit1] This setting means that the group is enabled for use. Corresponds to SE_GROUP_ENABLED. For more information, see [SIDATT].
66
bit1 :enabled
67
68
# @!attribute [rw] enabled_by_default
69
# @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].
70
bit1 :enabled_by_default
71
72
# @!attribute [rw] mandatory
73
# @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].
74
bit1 :mandatory
75
end
76
77
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/69e86ccc-85e3-41b9-b514-7d969cd0ed73
78
class UserFlagAttributes < BinData::Record
79
endian :big
80
81
18.times do
82
bit1 :"_reserved_#{self.fields.length}"
83
end
84
85
# @!attribute [rw] used_lmv2_auth_and_ntlmv2_session_key
86
# @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.
87
bit1 :used_lmv2_auth_and_ntlmv2_session_key
88
89
# @!attribute [rw] used_lmv2_auth_and_session_key
90
# @return [BinData::Bit1] The LMv2 response from the LmChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used for authentication and session key generation.
91
bit1 :used_lmv2_auth_and_session_key
92
93
# @!attribute [rw] used_ntlmv2_auth_and_session_key
94
# @return [BinData::Bit1] The NTLMv2 response from the NtChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used for authentication and session key generation.
95
bit1 :used_ntlmv2_auth_and_session_key
96
97
# @!attribute [rw] profile_path_populated
98
# @return [BinData::Bit1] Indicates that ProfilePath is populated.
99
bit1 :profile_path_populated
100
101
# @!attribute [rw] resource_group_ids
102
# @return [BinData::Bit1] Indicates that the ResourceGroupIds field is populated.
103
bit1 :resource_group_ids
104
105
# @!attribute [rw] accepts_ntlmv2
106
# @return [BinData::Bit1] Indicates that the domain controller understands NTLMv2.
107
bit1 :accepts_ntlmv2
108
109
# @!attribute [rw] machine_account
110
# @return [BinData::Bit1] Indicates that the account is a machine account.
111
bit1 :machine_account
112
113
# @!attribute [rw] sub_authentication
114
# @return [BinData::Bit1] Sub-authentication used; session key came from the sub-authentication package.
115
bit1 :sub_authentication
116
117
# @!attribute [rw] extra_sids
118
# @return [BinData::Bit1] Indicates that the ExtraSids field is populated and contains additional SIDs.
119
bit1 :extra_sids
120
121
1.times do
122
bit1 :"_reserved_#{self.fields.length}"
123
end
124
125
# @!attribute [rw] lan_manager
126
# @return [BinData::Bit1] LAN Manager key was used for authentication.
127
bit1 :lan_manager
128
129
1.times do
130
bit1 :"_reserved_#{self.fields.length}"
131
end
132
133
# @!attribute [rw] no_encryption
134
# @return [BinData::Bit1] No encryption is available.
135
bit1 :no_encryption
136
137
# @!attribute [rw] guest
138
# @return [BinData::Bit1] Authentication was done via the GUEST account; no password was used
139
bit1 :guest
140
end
141
142
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/b10cfda1-f24f-441b-8f43-80cb93e786ec
143
#
144
# [RFC4120] https://www.rfc-editor.org/rfc/rfc4120
145
# [RFC3961] https://www.ietf.org/rfc/rfc3961.txt
146
# [MS-KILE] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/2a32282e-dd48-4ad9-a542-609804b02cc9
147
# [MS-LSAD] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lsad/1b5471ef-4c33-4a91-b079-dfcbb82f05cc
148
class UserAccountAttributes < BinData::Record
149
endian :big
150
151
10.times do
152
bit1 :"_reserved_#{self.fields.length}"
153
end
154
155
# @!attribute [rw] use_aes_keys
156
# @return [BinData::Bit1] This bit is ignored by clients and servers.
157
bit1 :use_aes_keys
158
159
# @!attribute [rw] partial_secrets_account
160
# @return [BinData::Bit1] Specifies that the object is a read-only domain controller (RODC).
161
bit1 :partial_secrets_account
162
163
# @!attribute [rw] no_auth_data_required
164
# @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].
165
bit1 :no_auth_data_required
166
167
# @!attribute [rw] trusted_to_authenticate_for_delegation
168
# @return [BinData::Bit1] This bit is used by the Kerberos protocol, as specified in [MS-KILE] section 3.3.1.1.
169
bit1 :trusted_to_authenticate_for_delegation
170
171
# @!attribute [rw] password_expired
172
# @return [BinData::Bit1] Specifies that the password age on the user has exceeded the maximum password age policy.
173
bit1 :password_expired
174
175
# @!attribute [rw] dont_require_preauth
176
# @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.
177
bit1 :dont_require_preauth
178
179
# @!attribute [rw] use_des_key_only
180
# @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.
181
bit1 :use_des_key_only
182
183
# @!attribute [rw] not_delegated
184
# @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].
185
bit1 :not_delegated
186
187
# @!attribute [rw] trusted_for_delegation
188
# @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.
189
bit1 :trusted_for_delegation
190
191
# @!attribute [rw] smartcard_required
192
# @return [BinData::Bit1] Specifies that the user can authenticate only with a smart card.
193
bit1 :smartcard_required
194
195
# @!attribute [rw] encrypted_test_password_allowed
196
# @return [BinData::Bit1] Specifies that the cleartext password is to be persisted.
197
bit1 :encrypted_test_password_allowed
198
199
# @!attribute [rw] account_auto_lock
200
# @return [BinData::Bit1] Specifies that the account has been locked out.
201
bit1 :account_auto_lock
202
203
# @!attribute [rw] dont_expire_password
204
# @return [BinData::Bit1] Specifies that the maximum-password-age policy does not apply to this user.
205
bit1 :dont_expire_password
206
207
# @!attribute [rw] server_trust_account
208
# @return [BinData::Bit1] Specifies that the object is a DC.
209
bit1 :server_trust_account
210
211
# @!attribute [rw] workstation_trust_account
212
# @return [BinData::Bit1] Specifies that the object is a member workstation or server.
213
bit1 :workstation_trust_account
214
215
# @!attribute [rw] interdomain_trust_account
216
# @return [BinData::Bit1] Specifies that the object represents a trust object. For more information about trust objects, see [MS-LSAD].
217
bit1 :interdomain_trust_account
218
219
# @!attribute [rw] mns_logon_account
220
# @return [BinData::Bit1] This bit is ignored by clients and servers.
221
bit1 :mns_logon_account
222
223
# @!attribute [rw] normal_account
224
# @return [BinData::Bit1] Specifies that the user is not a computer object.
225
bit1 :normal_account
226
227
# @!attribute [rw] temp_duplicate_account
228
# @return [BinData::Bit1] This bit is ignored by clients and servers.
229
bit1 :temp_duplicate_account
230
231
# @!attribute [rw] password_not_required
232
# @return [BinData::Bit1] Specifies that the password-length policy does not apply to this user.
233
bit1 :password_not_required
234
235
# @!attribute [rw] home_directory_required
236
# @return [BinData::Bit1] Specifies that the homeDirectory attribute is required.
237
bit1 :home_directory_required
238
239
# @!attribute [rw] account_disabled
240
# @return [BinData::Bit1] Specifies that the account is not enabled for authentication.
241
bit1 :account_disabled
242
end
243
244
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/1c0d6e11-6443-4846-b744-f9f810a504eb
245
#
246
# [MS-ADA3] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-ada3/4517e835-3ee6-44d4-bb95-a94b6966bfb0
247
class UpnDnsInfoAttributes < BinData::Record
248
endian :big
249
250
30.times do
251
bit1 :"_reserved_#{self.fields.length}"
252
end
253
254
# @!attribute [rw] sam_name_and_sid
255
# @return [BinData::Bit1] The UPN_DNS_INFO structure has been extended with the user account’s SAM Name and SID.
256
bit1 :sam_name_and_sid
257
258
# @!attribute [rw] upn_name_constructed
259
# @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.
260
bit1 :upn_name_constructed
261
end
262
263
class Krb5SidAndAttributesPtr < RubySMB::Dcerpc::Ndr::NdrConfArray
264
default_parameters byte_align: 1, type: :krb5_sid_and_attributes
265
266
extend RubySMB::Dcerpc::Ndr::PointerClassPlugin
267
end
268
269
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/e465cb27-4bc1-4173-8be0-b5fd64dc9ff7
270
class Krb5ClientInfo < BinData::Record
271
endian :little
272
# @!attribute [r] ul_type
273
# @return [Integer] Describes the type of data present in the buffer
274
virtual :ul_type, value: Krb5PacElementType::CLIENT_INFORMATION
275
276
# @!attribute [rw] client_id
277
# @return [FileTime] Kerberos initial ticket-granting ticket (TGT) authentication time
278
file_time :client_id
279
280
# @!attribute [rw] name_length
281
# @return [Integer]
282
uint16 :name_length, initial_value: -> { name.num_bytes }
283
284
# @!attribute [rw] name
285
# @return [String]
286
string16 :name, read_length: :name_length
287
end
288
289
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/c34adc61-80e1-4920-8923-22ef5054c4b2
290
class Krb5PacRequestor < BinData::Record
291
endian :little
292
# @!attribute [r] ul_type
293
# @return [Integer] Describes the type of data present in the buffer
294
virtual :ul_type, value: Krb5PacElementType::PAC_REQUESTOR
295
296
# @!attribute [rw] user_sid
297
# @return [RPC_SID] SID of the requesting user
298
ms_dtyp_sid :user_sid
299
end
300
301
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/1c7aeadb-8ca4-4050-ae98-0e9834bdd81d
302
class Krb5PacAttributes < BinData::Record
303
endian :little
304
# @!attribute [r] ul_type
305
# @return [Integer] Describes the type of data present in the buffer
306
virtual :ul_type, value: Krb5PacElementType::PAC_ATTRIBUTES
307
308
# @!attribute [rw] flags_length
309
# @return [Integer] Length of the flags field, in bits
310
uint32 :flags_length, initial_value: 2
311
312
# @!attribute [rw] flags
313
# @return [Integer] Attribute flags
314
uint32 :flags, initial_value: PAC_WAS_GIVEN_IMPLICITLY
315
end
316
317
class PacAttributesFlags < BinData::Record
318
endian :big
319
320
30.times do
321
bit1 :"_reserved_#{self.fields.length}"
322
end
323
324
# @!attribute [rw] pac_was_requested
325
# @return [BinData::Bit1] The client requested the PAC
326
bit1 :pac_was_requested
327
328
# @!attribute [rw] pac_was_given_implicitly
329
# @return [BinData::Bit1] The client did not request or decline a PAC and was given one implicitly
330
bit1 :pac_was_given_implicitly
331
end
332
333
class Krb5SignatureType < BinData::Uint32le
334
# @param [Integer] val The checksum value
335
# @see Rex::Proto::Kerberos::Crypto::Checksum
336
def assign(val)
337
# Handle the scenario of users setting the signature type to a negative value such as -138 for HMAC_RC4
338
# Convert it to two's complement representation explicitly to bypass bindata's clamping logic in the super method:
339
if val < 0
340
val &= 0xffffffff
341
end
342
343
super(val)
344
end
345
end
346
347
class Krb5PacSignatureData < BinData::Record
348
endian :little
349
350
# @!attribute [rw] signature_type
351
# @return [Integer] Defines the cryptographic system used to calculate the checksum
352
# @see Rex::Proto::Kerberos::Crypto::Checksum
353
krb5_signature_type :signature_type
354
355
# @!attribute [rw] signature
356
# @return [String]
357
string :signature, length: -> { CHECKSUM_SIGNATURE_LENGTH.fetch(signature_type) }
358
end
359
360
class Krb5PacServerChecksum < Krb5PacSignatureData
361
# @!attribute [r] ul_type
362
# @return [Integer] Describes the type of data present in the buffer
363
virtual :ul_type, value: Krb5PacElementType::SERVER_CHECKSUM
364
end
365
366
class Krb5PacPrivServerChecksum < Krb5PacSignatureData
367
# @!attribute [r] ul_type
368
# @return [Integer] Describes the type of data present in the buffer
369
virtual :ul_type, value: Krb5PacElementType::PRIVILEGE_SERVER_CHECKSUM
370
end
371
372
class Krb5TicketChecksum < Krb5PacSignatureData
373
# @!attribute [r] ul_type
374
# @return [Integer] Describes the type of data present in the buffer
375
virtual :ul_type, value: Krb5PacElementType::TICKET_CHECKSUM
376
end
377
378
class Krb5FullPacChecksum < Krb5PacSignatureData
379
# @!attribute [r] ul_type
380
# @return [Integer] Describes the type of data present in the buffer
381
virtual :ul_type, value: Krb5PacElementType::FULL_PAC_CHECKSUM
382
end
383
384
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/69e86ccc-85e3-41b9-b514-7d969cd0ed73
385
class Krb5ValidationInfo < RubySMB::Dcerpc::Ndr::NdrStruct
386
default_parameters byte_align: 8
387
388
endian :little
389
390
# @!attribute [rw] logon_time
391
# @return [FileTime] User account's lastLogon attributeÏ
392
ndr_file_time :logon_time
393
394
# @!attribute [rw] logoff_time
395
# @return [FileTime] Time the client's logon session is set to expire
396
ndr_file_time :logoff_time, initial_value: NEVER_EXPIRE
397
398
# @!attribute [rw] kick_off_time
399
# @return [FileTime] logoff_time minus the user account's forceLogoff attribute
400
ndr_file_time :kick_off_time, initial_value: NEVER_EXPIRE
401
402
# @!attribute [rw] password_last_set
403
# @return [FileTime] User account's pwdLastSet attribute
404
ndr_file_time :password_last_set
405
406
# @!attribute [rw] password_can_change
407
# @return [FileTime] Time at which the client's password is allowed to change
408
ndr_file_time :password_can_change
409
410
# @!attribute [rw] password_must_change
411
# @return [FileTime] Time at which the client's password expires
412
ndr_file_time :password_must_change, initial_value: NEVER_EXPIRE
413
414
# @!attribute [rw] effective_name
415
# @return [RpcUnicodeString] User account's samAccountName attribute
416
rpc_unicode_string :effective_name
417
418
# @!attribute [rw] full_name
419
# @return [RpcUnicodeString] User account's full name for interactive logon
420
rpc_unicode_string :full_name
421
422
# @!attribute [rw] logon_script
423
# @return [RpcUnicodeString] User account's scriptPath attribute
424
rpc_unicode_string :logon_script
425
426
# @!attribute [rw] profile_path
427
# @return [RpcUnicodeString] User account's profilePath attribute
428
rpc_unicode_string :profile_path
429
430
# @!attribute [rw] home_directory
431
# @return [RpcUnicodeString] User account's HomeDirectory attribute
432
rpc_unicode_string :home_directory
433
434
# @!attribute [rw] home_directory_drive
435
# @return [RpcUnicodeString] User account's HomeDrive attribute
436
rpc_unicode_string :home_directory_drive
437
438
# @!attribute [rw] logon_count
439
# @return [Integer] User account's LogonCount attribute
440
ndr_uint16 :logon_count
441
442
# @!attribute [rw] bad_password_count
443
# @return [Integer] User account's badPwdCount attribute
444
ndr_uint16 :bad_password_count
445
446
# @!attribute [rw] user_id
447
# @return [Integer] RID of the account
448
ndr_uint32 :user_id
449
450
# @!attribute [rw] primary_group_id
451
# @return [Integer] RID for the primary group to which this account belongs
452
ndr_uint32 :primary_group_id
453
454
# @!attribute [rw] group_count
455
# @return [Integer] Number of groups within the account domain to which the account belongs
456
ndr_uint32 :group_count, initial_value: -> { group_memberships.length }
457
458
# @!attribute [rw] group_memberships
459
# @return [Integer] List of GROUP_MEMBERSHIP structures that contains the groups to which the account belongs in the account domain
460
pgroup_membership_array :group_memberships, type: [:group_membership, { byte_align: 4 }]
461
462
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/69e86ccc-85e3-41b9-b514-7d969cd0ed73
463
# @!attribute [rw] user_flags
464
# @return [Integer] A set of bit flags that describe the user's logon information
465
ndr_uint32 :user_flags,
466
initial_value: -> do
467
value = 0
468
# Bit D: Indicates that the ExtraSids field is populated and contains additional SIDs.
469
value |= (1 << 5) if self.sid_count > 0
470
# Bit H: Indicates that the ResourceGroupIds field is populated.
471
value |= (1 << 9) if self.resource_group_count > 0
472
value
473
end
474
475
# @!attribute [rw] user_session_key
476
# @return [Integer] A session key that is used for cryptographic operations on a session
477
user_session_key :user_session_key
478
479
# @!attribute [rw] logon_server
480
# @return [RpcUnicodeString] NetBIOS name of the Kerberos KDC that performed the authentication server (AS) ticket request
481
rpc_unicode_string :logon_server
482
483
# @!attribute [rw] logon_domain_name
484
# @return [RpcUnicodeString] NetBIOS name of the domain to which this account belongs
485
rpc_unicode_string :logon_domain_name
486
487
# @!attribute [rw] logon_domain_id
488
# @return [Integer] SID for the domain specified in LogonDomainName
489
prpc_sid :logon_domain_id
490
491
# @!attribute [rw] reserved_1
492
# @return [Integer] This member is reserved
493
# ndr_uint64 :reserved_1
494
ndr_fix_array :reserved_1, initial_length: 2, type: :ndr_uint32
495
496
# @!attribute [rw] user_account_control
497
# @return [Integer] Set of bit flags that represent information about this account
498
ndr_uint32 :user_account_control, initial_value: USER_NORMAL_ACCOUNT | USER_DONT_EXPIRE_PASSWORD
499
500
# @!attribute [rw] sub_auth_status
501
# @return [Integer] Subauthentication package's status code
502
ndr_uint32 :sub_auth_status
503
504
# @!attribute [rw] last_successful_i_logon
505
# @return [FileTime] User account's msDS-LastSuccessfulInteractiveLogonTime
506
ndr_file_time :last_successful_i_logon
507
508
# @!attribute [rw] last_failed_i_logon
509
# @return [FileTime] User account's msDS-LastFailedInteractiveLogonTime
510
ndr_file_time :last_failed_i_logon
511
512
# @!attribute [rw] failed_i_logon_count
513
# @return [Integer] User account's msDS-FailedInteractiveLogonCountAtLastSuccessfulLogon
514
ndr_uint32 :failed_i_logon_count
515
516
# @!attribute [rw] reserved_3
517
# @return [Integer] This member is reserved
518
ndr_uint32 :reserved_3
519
520
# @!attribute [rw] sid_count
521
# @return [Integer] Total number of SIDs present in the ExtraSids member
522
ndr_uint32 :sid_count, initial_value: -> { extra_sids.length }
523
524
# @!attribute [rw] extra_sids
525
# @return [Integer] A list of KERB_SID_AND_ATTRIBUTES structures that contain a list of SIDs
526
# corresponding to groups in domains other than the account domain to which the principal belongs
527
krb5_sid_and_attributes_ptr :extra_sids # :extra_sids_ptr
528
529
# @!attribute [rw] resource_group_domain_sid
530
# @return [Integer] SID of the domain for the server whose resources the client is authenticating to
531
prpc_sid :resource_group_domain_sid # :resource_group_domain_sid_ptr
532
533
# @!attribute [rw] resource_group_count
534
# @return [Integer] Number of resource group identifiers stored in ResourceGroupIds
535
ndr_uint32 :resource_group_count
536
537
# @!attribute [rw] resource_group_ids_ptr
538
# @return [Integer] Pointer to list of GROUP_MEMBERSHIP structures that contain the RIDs and attributes of the
539
# account's groups in the resource domain
540
pgroup_membership_array :resource_group_ids_ptr, type: [:group_membership, { byte_align: 4 }]
541
542
def group_ids=(group_ids)
543
self.group_memberships = group_ids.map do |id|
544
{ relative_id: id, attributes: SE_GROUP_ALL }
545
end
546
end
547
end
548
549
class Krb5ValidationInfoPtr < Krb5ValidationInfo
550
default_parameters byte_align: 8
551
extend RubySMB::Dcerpc::Ndr::PointerClassPlugin
552
end
553
554
class Krb5LogonInformation < RubySMB::Dcerpc::Ndr::TypeSerialization1
555
endian :little
556
# @!attribute [r] ul_type
557
# @return [Integer] Describes the type of data present
558
virtual :ul_type, value: Krb5PacElementType::LOGON_INFORMATION
559
560
krb5_validation_info_ptr :data
561
end
562
563
class UnknownPacElement < BinData::Record
564
mandatory_parameter :data_length, :selection
565
endian :little
566
567
# @!attribute [rw] ul_type
568
# @return [Integer] Describes the type of data present in the buffer
569
virtual :ul_type, value: :selection
570
571
# @!attribute [rw] unknown_element
572
# @return [String] The contents of an unknown Pac Element
573
string :unknown_element, read_length: :data_length
574
end
575
576
# See [2.6.4 NTLM_SUPPLEMENTAL_CREDENTIAL](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/39f588d6-21e3-4e09-a9f2-d8f7b9b998bf)
577
class Krb5NtlmSupplementalCredential < RubySMB::Dcerpc::Ndr::NdrStruct
578
# The only package name that Microsoft KDCs use is `NTLM`
579
# See https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/a1c36b00-1fca-415c-a4ca-e66e98844760#Appendix_A_16
580
PACKAGE_NAME = 'NTLM'.encode('utf-16le').freeze
581
582
default_parameter byte_align: 4
583
endian :little
584
585
ndr_uint32 :version
586
ndr_uint32 :flags
587
ndr_fixed_byte_array :lm_password, initial_length: 16
588
ndr_fixed_byte_array :nt_password, initial_length: 16
589
end
590
591
class Krb5SecpkgSupplementalCredByteArrayPtr < RubySMB::Dcerpc::Ndr::NdrConfArray
592
default_parameters type: :ndr_uint8
593
extend RubySMB::Dcerpc::Ndr::PointerClassPlugin
594
end
595
596
# See [2.6.3 SECPKG_SUPPLEMENTAL_CRED](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/50974dc7-6bce-4db5-805b-8dca924ad5a4)
597
class Krb5SecpkgSupplementalCred < RubySMB::Dcerpc::Ndr::NdrStruct
598
default_parameter byte_align: 4
599
endian :little
600
601
rpc_unicode_string :package_name
602
ndr_uint32 :credential_size
603
krb5_secpkg_supplemental_cred_byte_array_ptr :credentials
604
end
605
606
# See [2.6.2 PAC_CREDENTIAL_DATA](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/4927158e-c9d5-493d-a3f6-1826b88d22ba)
607
class Krb5PacCredentialData < RubySMB::Dcerpc::Ndr::NdrStruct
608
default_parameter byte_align: 4
609
endian :little
610
611
ndr_uint32 :credential_count
612
ndr_conf_array :credentials, type: :krb5_secpkg_supplemental_cred
613
614
# Extract the NTLM hash from the credentials array if present
615
#
616
# @return [String, nil] The NTLM hash as "LMHASH:NTHASH" or `nil` if the
617
# credentials array does not contain any NTLM hash
618
def extract_ntlm_hash
619
credential = credentials.find do |credential|
620
credential.package_name.to_s == Krb5NtlmSupplementalCredential::PACKAGE_NAME
621
end
622
return unless credential
623
624
ntlm_creds_raw = credential.credentials.to_ary.pack('C*')
625
ntlm_creds = Krb5NtlmSupplementalCredential.read(ntlm_creds_raw)
626
if ntlm_creds.lm_password.any? {|elem| elem != 0}
627
lm_hash = ntlm_creds.lm_password.to_hex
628
else
629
# Empty LMHash
630
lm_hash = 'aad3b435b51404eeaad3b435b51404ee'
631
end
632
nt_hash = ntlm_creds.nt_password.to_hex
633
634
return "#{lm_hash}:#{nt_hash}"
635
end
636
end
637
638
class Krb5PacCredentialDataPtr < Krb5PacCredentialData
639
extend RubySMB::Dcerpc::Ndr::PointerClassPlugin
640
end
641
642
class Krb5SerializedPacCredentialData < RubySMB::Dcerpc::Ndr::TypeSerialization1
643
endian :little
644
645
krb5_pac_credential_data_ptr :data
646
end
647
648
# See [2.6.1 PAC_CREDENTIAL_INFO](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/cc919d0c-f2eb-4f21-b487-080c486d85fe)
649
class Krb5PacCredentialInfo < BinData::Record
650
mandatory_parameter :data_length
651
endian :little
652
# @!attribute [r] ul_type
653
# @return [Integer] Describes the type of data present
654
virtual :ul_type, value: Krb5PacElementType::CREDENTIAL_INFORMATION
655
656
uint32 :version
657
uint32 :encryption_type
658
array :serialized_data, type: :uint8, read_until: -> { index == data_length - version.num_bytes - encryption_type.num_bytes - 1 }
659
660
def decrypt_serialized_data(key)
661
encryptor = Rex::Proto::Kerberos::Crypto::Encryption::from_etype(self.encryption_type)
662
decrypted_serialized_data = encryptor.decrypt(
663
self.serialized_data.to_binary_s,
664
key,
665
Rex::Proto::Kerberos::Crypto::KeyUsage::KERB_NON_KERB_SALT
666
)
667
Krb5SerializedPacCredentialData.read(decrypted_serialized_data)
668
end
669
end
670
671
# See [2.10 UPN_DNS_INFO](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/1c0d6e11-6443-4846-b744-f9f810a504eb)
672
class Krb5UpnDnsInfo < BinData::Record
673
auto_call_delayed_io
674
endian :little
675
# @!attribute [r] ul_type
676
# @return [Integer] Describes the type of data present in the buffer
677
virtual :ul_type, value: Krb5PacElementType::USER_PRINCIPAL_NAME_AND_DNS_INFORMATION
678
679
# @!attribute [rw] upn_length
680
# @return [Integer] The length of the UPN
681
uint16 :upn_length, value: -> { upn.num_bytes }
682
683
# @!attribute [rw] upn_offset
684
# @return [Integer] The relative offset of the UPN from the beginning of this structure
685
uint16 :upn_offset
686
687
# @!attribute [rw] dns_domain_name_length
688
# @return [Integer] The length of the DNS domain name
689
uint16 :dns_domain_name_length, value: -> { dns_domain_name.num_bytes }
690
691
# @!attribute [rw] dns_domain_name_offset
692
# @return [Integer] The relative offset of the DNS domain name from the beginning of this structure
693
uint16 :dns_domain_name_offset
694
695
# @!attribute [rw] flags
696
# @return [Integer]
697
# U flag (bit 0) The user account object does not have the userPrincipalName attribute.
698
# S flag (bit 1) The structure has been extended with the user account’s SAM Name and SID.
699
# The remaining bits are ignored.
700
uint32 :flags
701
702
# @!attribute [rw] sam_name_length
703
# @return [Integer] The length of the SAM name
704
# Only available if the S flag is set
705
uint16 :sam_name_length, value: -> { sam_name.num_bytes }, onlyif: :has_s_flag?
706
707
# @!attribute [rw] sam_name_offset
708
# @return [Integer] The relative offset of the SAM name from the beginning of this structure
709
# Only available if the S flag is set
710
uint16 :sam_name_offset, onlyif: :has_s_flag?
711
712
# @!attribute [rw] sid_length
713
# @return [Integer] The length of the SID
714
# Only available if the S flag is set
715
uint16 :sid_length, value: -> { sid.num_bytes }, onlyif: :has_s_flag?
716
717
# @!attribute [rw] sid_offset
718
# @return [Integer] The relative offset of the SID from the beginning of this structure
719
# Only available if the S flag is set
720
uint16 :sid_offset, onlyif: :has_s_flag?
721
722
# @!attribute [rw] upn
723
# @return [String] The UPN (User Principal Name) (e.g. [email protected])
724
delayed_io :upn, read_abs_offset: -> { self.abs_offset + upn_offset } do
725
string16 read_length: :upn_length
726
end
727
728
# @!attribute [rw] dns_domain_name
729
# @return [String] The DNS Domain Name (e.g. WINDOMAIN.LOCAL)
730
delayed_io :dns_domain_name, read_abs_offset: -> { self.abs_offset + dns_domain_name_offset } do
731
string16 read_length: :dns_domain_name_length
732
end
733
734
# @!attribute [rw] sam_name
735
# @return [String] The SAM Name (e.g. test)
736
delayed_io :sam_name, read_abs_offset: -> { self.abs_offset + sam_name_offset }, onlyif: -> { has_s_flag? } do
737
string16 read_length: :sam_name_length
738
end
739
740
# @!attribute [rw] sid
741
# @return [MsDtypSid] The SID (e.g. S-1-5-32-544)
742
delayed_io :sid, read_abs_offset: -> { self.abs_offset + sid_offset }, onlyif: -> { has_s_flag? } do
743
ms_dtyp_sid
744
end
745
746
def do_num_bytes
747
if has_s_flag?
748
result = sid_offset + sid_length
749
else
750
result = dns_domain_name_offset + dns_domain_name_length
751
end
752
result
753
end
754
755
# def initialize_instance(*args)
756
# super
757
# set_offsets!
758
# end
759
# @return [Boolean] Returns the value of the S flag
760
def has_s_flag?
761
flags.anybits?(0b10)
762
end
763
764
# @param [Boolean] bool The value to set the S flag to
765
# @return [void]
766
def set_s_flag(bool)
767
set_flag_bit(1, bool)
768
end
769
770
# @return [Boolean] Returns the value of the U flag
771
def has_u_flag?
772
flags.anybits?(0b01)
773
end
774
775
# @param [Boolean] bool The value to set the U flag to
776
# @return [void]
777
def set_u_flag(bool)
778
set_flag_bit(0, bool)
779
end
780
781
# @param [Integer] upn The relative offset for the upn
782
# @param [Integer] dns_domain_name The relative offset for the dns_domain_name
783
# @param [Integer] sam_name The relative offset for the sam_name
784
# @param [Integer] sid The relative offset for the sid
785
# @return [void]
786
#
787
# Allows you to specify the offsets for the contents, otherwise defaults them
788
def set_offsets!(upn: nil, dns_domain_name: nil, sam_name: nil, sid: nil)
789
self.upn_offset = upn || calc_upn_offset
790
self.dns_domain_name_offset = dns_domain_name || calc_dns_domain_name_offset
791
self.sam_name_offset = sam_name || calc_sam_name_offset
792
self.sid_offset = sid || calc_sid_offset
793
end
794
795
private
796
797
def set_flag_bit(position, bool)
798
if bool
799
self.flags |= (1 << position)
800
else
801
self.flags &= ~(1 << position)
802
end
803
end
804
805
def calc_upn_offset
806
has_s_flag? ? 24 : 16
807
end
808
809
def calc_dns_domain_name_offset
810
upn_offset + upn_length
811
end
812
813
def calc_sam_name_offset
814
dns_domain_name_offset + dns_domain_name_length
815
end
816
817
def calc_sid_offset
818
sam_name_offset + sam_name_length
819
end
820
end
821
822
class Krb5PacElement < BinData::Choice
823
mandatory_parameter :data_length
824
825
krb5_logon_information Krb5PacElementType::LOGON_INFORMATION
826
krb5_client_info Krb5PacElementType::CLIENT_INFORMATION
827
krb5_pac_server_checksum Krb5PacElementType::SERVER_CHECKSUM
828
krb5_pac_priv_server_checksum Krb5PacElementType::PRIVILEGE_SERVER_CHECKSUM
829
krb5_ticket_checksum Krb5PacElementType::TICKET_CHECKSUM
830
krb5_full_pac_checksum Krb5PacElementType::FULL_PAC_CHECKSUM
831
krb5_pac_credential_info Krb5PacElementType::CREDENTIAL_INFORMATION, data_length: :data_length
832
krb5_upn_dns_info Krb5PacElementType::USER_PRINCIPAL_NAME_AND_DNS_INFORMATION
833
krb5_pac_requestor Krb5PacElementType::PAC_REQUESTOR
834
krb5_pac_attributes Krb5PacElementType::PAC_ATTRIBUTES
835
unknown_pac_element :default, data_length: :data_length, selection: :selection
836
end
837
838
class Krb5PacInfoBuffer < BinData::Record
839
endian :little
840
841
# @!attribute [rw] ul_type
842
# @return [Integer] Describes the type of data present in the buffer
843
uint32 :ul_type
844
845
# @!attribute [rw] cb_buffer_size
846
# @return [Integer]
847
uint32 :cb_buffer_size, initial_value: -> { buffer.pac_element.num_bytes }
848
849
# @!attribute [rw] offset
850
# @return [Integer]
851
uint64 :offset
852
853
delayed_io :buffer, read_abs_offset: :offset do
854
# @!attribute [rw] pac_element
855
# @return [Krb5PacElement]
856
krb5_pac_element :pac_element, selection: -> { ul_type }, data_length: :cb_buffer_size
857
string :padding, length: -> { num_bytes_to_align(pac_element.num_bytes) }
858
end
859
end
860
861
class Krb5Pac < BinData::Record
862
endian :little
863
auto_call_delayed_io
864
865
# @!attribute [rw] c_buffers
866
# @return [Integer]
867
uint32 :c_buffers, asserted_value: -> { pac_info_buffers.length }
868
869
# @!attribute [r] version
870
# @return [Integer]
871
uint32 :version, asserted_value: 0x00000000
872
873
# @!attribute [rw] pac_info_buffers
874
# @return [Array<Krb5PacInfoBuffer>]
875
array :pac_info_buffers, type: :krb5_pac_info_buffer, initial_length: :c_buffers
876
877
def assign(val)
878
case val
879
when Hash
880
pac_infos = val[:pac_elements].map do |pac_element|
881
{ ul_type: pac_element.ul_type, buffer: { pac_element: pac_element } }
882
end
883
new_val = val.merge(pac_info_buffers: pac_infos)
884
super(new_val)
885
else
886
super
887
end
888
end
889
890
# Calculates the checksums, can only be done after all other fields are set
891
# @param [String] service_key Service key to calculate the server checksum
892
# @param [String] krbtgt_key key to calculate priv server (KDC) and full checksums with, if not specified `service_key` is used
893
#
894
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/962edb93-fa3c-48ea-a0a6-062f760eb69c
895
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/37bf87b9-1d56-475a-b2b2-e3948b29194d
896
def calculate_checksums!(service_key: nil, krbtgt_key: nil)
897
server_checksum = nil
898
priv_server_checksum = nil
899
full_pac_checksum = nil
900
pac_info_buffers.each do |info_buffer|
901
pac_element = info_buffer.buffer.pac_element
902
case pac_element.ul_type
903
when Krb5PacElementType::SERVER_CHECKSUM
904
server_checksum = pac_element
905
when Krb5PacElementType::PRIVILEGE_SERVER_CHECKSUM
906
priv_server_checksum = pac_element
907
when Krb5PacElementType::FULL_PAC_CHECKSUM
908
full_pac_checksum = pac_element
909
else
910
next
911
end
912
end
913
914
missing_checksums = []
915
missing_checksums << Krb5PacElementType::SERVER_CHECKSUM if server_checksum.nil?
916
missing_checksums << Krb5PacElementType::PRIVILEGE_SERVER_CHECKSUM if priv_server_checksum.nil?
917
raise Rex::Proto::Kerberos::Pac::Error::MissingInfoBuffer.new(ul_types: missing_checksums) unless missing_checksums.empty?
918
919
if krbtgt_key.nil?
920
krbtgt_key = service_key
921
end
922
923
# https://bugzilla.samba.org/show_bug.cgi?id=15231
924
# https://i.blackhat.com/EU-22/Thursday-Briefings/EU-22-Tervoort-Breaking-Kerberos-RC4-Cipher-and-Spoofing-Windows-PACs-wp.pdf
925
if full_pac_checksum
926
full_pac_checksum.signature = calculate_checksum(full_pac_checksum.signature_type, krbtgt_key, to_binary_s)
927
end
928
929
server_checksum.signature = calculate_checksum(server_checksum.signature_type, service_key, to_binary_s)
930
931
priv_server_checksum.signature = calculate_checksum(priv_server_checksum.signature_type, krbtgt_key, server_checksum.signature)
932
end
933
934
# Calculates the offsets for pac_elements if they haven't yet been set
935
def calculate_offsets!
936
offset = pac_info_buffers.abs_offset + pac_info_buffers.num_bytes
937
pac_info_buffers.each do |pac_info|
938
next unless pac_info.offset == 0
939
940
pac_info.offset = offset
941
offset += pac_info.cb_buffer_size
942
offset += num_bytes_to_align(offset)
943
end
944
end
945
946
# Call this when you are done setting fields in the object
947
# in order to finalise the data
948
def sign!(service_key: nil, krbtgt_key: nil)
949
calculate_offsets!
950
calculate_checksums!(service_key: service_key, krbtgt_key: krbtgt_key)
951
end
952
953
def calculate_checksum(signature_type, key, data)
954
checksummer = Rex::Proto::Kerberos::Crypto::Checksum.from_checksum_type(signature_type)
955
checksummer.checksum(key, Rex::Proto::Kerberos::Crypto::KeyUsage::KERB_NON_KERB_CKSUM_SALT, data)
956
end
957
958
private
959
960
def num_bytes_to_align(n, align: 8)
961
(align - (n % align)) % align
962
end
963
end
964
end
965
966