1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Gabriel Potter
5
6r"""
7Kerberos V5
8
9Implements parts of:
10
11- Kerberos Network Authentication Service (V5): RFC4120
12- Kerberos Version 5 GSS-API: RFC1964, RFC4121
13- Kerberos Pre-Authentication: RFC6113 (FAST)
14- Kerberos Principal Name Canonicalization and Cross-Realm Referrals: RFC6806
15- Microsoft Windows 2000 Kerberos Change Password and Set Password Protocols: RFC3244
16- PKINIT and its extensions: RFC4556, RFC8070, RFC8636 and [MS-PKCA]
17- User to User Kerberos Authentication: draft-ietf-cat-user2user-03
18- Public Key Cryptography Based User-to-User Authentication (PKU2U): draft-zhu-pku2u-09
19- Initial and Pass Through Authentication Using Kerberos V5 (IAKERB):
20 draft-ietf-kitten-iakerb-03
21- Kerberos Protocol Extensions: [MS-KILE]
22- Kerberos Protocol Extensions: Service for User: [MS-SFU]
23- Kerberos Key Distribution Center Proxy Protocol: [MS-KKDCP]
24
25
26.. note::
27 You will find more complete documentation for this layer over at
28 `Kerberos <https://scapy.readthedocs.io/en/latest/layers/kerberos.html>`_
29
30Example decryption::
31
32 >>> from scapy.libs.rfc3961 import Key, EncryptionType
33 >>> pkt = Ether(hex_bytes("525400695813525400216c2b08004500015da71840008006dc\
34 83c0a87a9cc0a87a11c209005854f6ab2392c25bd650182014b6e00000000001316a8201\
35 2d30820129a103020105a20302010aa3633061304ca103020102a24504433041a0030201\
36 12a23a043848484decb01c9b62a1cabfbc3f2d1ed85aa5e093ba8358a8cea34d4393af93\
37 bf211e274fa58e814878db9f0d7a28d94e7327660db4f3704b3011a10402020080a20904\
38 073005a0030101ffa481b73081b4a00703050040810010a1123010a003020101a1093007\
39 1b0577696e3124a20e1b0c444f4d41494e2e4c4f43414ca321301fa003020102a1183016\
40 1b066b72627467741b0c444f4d41494e2e4c4f43414ca511180f32303337303931333032\
41 343830355aa611180f32303337303931333032343830355aa7060204701cc5d1a8153013\
42 0201120201110201170201180202ff79020103a91d301b3019a003020114a11204105749\
43 4e31202020202020202020202020"))
44 >>> enc = pkt[Kerberos].root.padata[0].padataValue
45 >>> k = Key(enc.etype.val, key=hex_bytes("7fada4e566ae4fb270e2800a23a\
46 e87127a819d42e69b5e22de0ddc63da80096d"))
47 >>> enc.decrypt(k)
48"""
49
50from collections import namedtuple, deque
51from datetime import datetime, timedelta, timezone
52from enum import IntEnum
53
54import os
55import re
56import socket
57import struct
58
59from scapy.error import warning
60import scapy.asn1.mib # noqa: F401
61from scapy.asn1.ber import BER_id_dec, BER_Decoding_Error
62from scapy.asn1.asn1 import (
63 ASN1_BIT_STRING,
64 ASN1_BOOLEAN,
65 ASN1_Class,
66 ASN1_GENERAL_STRING,
67 ASN1_GENERALIZED_TIME,
68 ASN1_INTEGER,
69 ASN1_STRING,
70 ASN1_Codecs,
71)
72from scapy.asn1fields import (
73 ASN1F_BIT_STRING_ENCAPS,
74 ASN1F_BOOLEAN,
75 ASN1F_CHOICE,
76 ASN1F_enum_INTEGER,
77 ASN1F_FLAGS,
78 ASN1F_GENERAL_STRING,
79 ASN1F_GENERALIZED_TIME,
80 ASN1F_INTEGER,
81 ASN1F_OID,
82 ASN1F_optional,
83 ASN1F_PACKET,
84 ASN1F_SEQUENCE_OF,
85 ASN1F_SEQUENCE,
86 ASN1F_STRING_ENCAPS,
87 ASN1F_STRING_PacketField,
88 ASN1F_STRING,
89)
90from scapy.asn1packet import ASN1_Packet
91from scapy.automaton import Automaton, ATMT
92from scapy.config import conf
93from scapy.compat import bytes_encode
94from scapy.error import log_runtime
95from scapy.fields import (
96 ConditionalField,
97 FieldLenField,
98 FlagsField,
99 IntEnumField,
100 LEIntEnumField,
101 LenField,
102 LEShortEnumField,
103 LEShortField,
104 LongField,
105 MayEnd,
106 MultipleTypeField,
107 PacketField,
108 PacketLenField,
109 PacketListField,
110 PadField,
111 ShortEnumField,
112 ShortField,
113 StrField,
114 StrFieldUtf16,
115 StrFixedLenEnumField,
116 XByteField,
117 XLEIntEnumField,
118 XLEIntField,
119 XLEShortField,
120 XStrField,
121 XStrFixedLenField,
122 XStrLenField,
123)
124from scapy.packet import Packet, bind_bottom_up, bind_top_down, bind_layers
125from scapy.supersocket import StreamSocket, SuperSocket
126from scapy.utils import strrot, strxor
127from scapy.volatile import GeneralizedTime, RandNum, RandBin
128
129from scapy.layers.gssapi import (
130 GSSAPI_BLOB,
131 GSS_C_FLAGS,
132 GSS_C_NO_CHANNEL_BINDINGS,
133 GSS_S_BAD_BINDINGS,
134 GSS_S_BAD_MECH,
135 GSS_S_COMPLETE,
136 GSS_S_CONTINUE_NEEDED,
137 GSS_S_DEFECTIVE_TOKEN,
138 GSS_S_FAILURE,
139 GSS_S_FLAGS,
140 GssChannelBindings,
141 SSP,
142 _GSSAPI_OIDS,
143 _GSSAPI_SIGNATURE_OIDS,
144)
145from scapy.layers.inet import TCP, UDP
146from scapy.layers.smb import _NV_VERSION
147from scapy.layers.smb2 import STATUS_ERREF
148from scapy.layers.tls.cert import Cert, PrivKey
149from scapy.layers.x509 import (
150 _CMS_ENCAPSULATED,
151 CMS_ContentInfo,
152 CMS_IssuerAndSerialNumber,
153 DHPublicKey,
154 X509_AlgorithmIdentifier,
155 X509_DirectoryName,
156 X509_SubjectPublicKeyInfo,
157)
158
159# Redirect exports from RFC3961
160try:
161 from scapy.libs.rfc3961 import * # noqa: F401,F403
162except ImportError:
163 pass
164
165# Typing imports
166from typing import (
167 Optional,
168)
169
170
171# kerberos APPLICATION
172
173
174class ASN1_Class_KRB(ASN1_Class):
175 name = "Kerberos"
176 # APPLICATION + CONSTRUCTED = 0x40 | 0x20
177 Token = 0x60 | 0 # GSSAPI
178 Ticket = 0x60 | 1
179 Authenticator = 0x60 | 2
180 EncTicketPart = 0x60 | 3
181 AS_REQ = 0x60 | 10
182 AS_REP = 0x60 | 11
183 TGS_REQ = 0x60 | 12
184 TGS_REP = 0x60 | 13
185 AP_REQ = 0x60 | 14
186 AP_REP = 0x60 | 15
187 PRIV = 0x60 | 21
188 CRED = 0x60 | 22
189 EncASRepPart = 0x60 | 25
190 EncTGSRepPart = 0x60 | 26
191 EncAPRepPart = 0x60 | 27
192 EncKrbPrivPart = 0x60 | 28
193 EncKrbCredPart = 0x60 | 29
194 ERROR = 0x60 | 30
195
196
197# RFC4120 sect 5.2
198
199
200KerberosString = ASN1F_GENERAL_STRING
201Realm = KerberosString
202Int32 = ASN1F_INTEGER
203UInt32 = ASN1F_INTEGER
204
205_PRINCIPAL_NAME_TYPES = {
206 0: "NT-UNKNOWN",
207 1: "NT-PRINCIPAL",
208 2: "NT-SRV-INST",
209 3: "NT-SRV-HST",
210 4: "NT-SRV-XHST",
211 5: "NT-UID",
212 6: "NT-X500-PRINCIPAL",
213 7: "NT-SMTP-NAME",
214 10: "NT-ENTERPRISE",
215}
216
217
218class PrincipalName(ASN1_Packet):
219 ASN1_codec = ASN1_Codecs.BER
220 ASN1_root = ASN1F_SEQUENCE(
221 ASN1F_enum_INTEGER(
222 "nameType",
223 0,
224 _PRINCIPAL_NAME_TYPES,
225 explicit_tag=0xA0,
226 ),
227 ASN1F_SEQUENCE_OF("nameString", [], KerberosString, explicit_tag=0xA1),
228 )
229
230 def toString(self):
231 """
232 Convert a PrincipalName back into its string representation.
233 """
234 return "/".join(x.val.decode() for x in self.nameString)
235
236 @staticmethod
237 def fromUPN(upn: str):
238 """
239 Create a PrincipalName from a UPN string.
240 """
241 user, _ = _parse_upn(upn)
242 return PrincipalName(
243 nameString=[ASN1_GENERAL_STRING(user)],
244 nameType=ASN1_INTEGER(1), # NT-PRINCIPAL
245 )
246
247 @staticmethod
248 def fromSPN(spn: str):
249 """
250 Create a PrincipalName from a SPN string.
251 """
252 spn, _ = _parse_spn(spn)
253 if spn.startswith("krbtgt"):
254 return PrincipalName(
255 nameString=[ASN1_GENERAL_STRING(x) for x in spn.split("/")],
256 nameType=ASN1_INTEGER(2), # NT-SRV-INST
257 )
258 elif "/" in spn:
259 return PrincipalName(
260 nameString=[ASN1_GENERAL_STRING(x) for x in spn.split("/")],
261 nameType=ASN1_INTEGER(3), # NT-SRV-HST
262 )
263 else:
264 # In case of U2U
265 return PrincipalName(
266 nameString=[ASN1_GENERAL_STRING(spn)],
267 nameType=ASN1_INTEGER(1), # NT-PRINCIPAL
268 )
269
270
271KerberosTime = ASN1F_GENERALIZED_TIME
272Microseconds = ASN1F_INTEGER
273
274
275# https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-1
276
277_KRB_E_TYPES = {
278 1: "DES-CBC-CRC",
279 2: "DES-CBC-MD4",
280 3: "DES-CBC-MD5",
281 5: "DES3-CBC-MD5",
282 7: "DES3-CBC-SHA1",
283 9: "DSAWITHSHA1-CMSOID",
284 10: "MD5WITHRSAENCRYPTION-CMSOID",
285 11: "SHA1WITHRSAENCRYPTION-CMSOID",
286 12: "RC2CBC-ENVOID",
287 13: "RSAENCRYPTION-ENVOID",
288 14: "RSAES-OAEP-ENV-OID",
289 15: "DES-EDE3-CBC-ENV-OID",
290 16: "DES3-CBC-SHA1-KD",
291 17: "AES128-CTS-HMAC-SHA1-96",
292 18: "AES256-CTS-HMAC-SHA1-96",
293 19: "AES128-CTS-HMAC-SHA256-128",
294 20: "AES256-CTS-HMAC-SHA384-192",
295 23: "RC4-HMAC",
296 24: "RC4-HMAC-EXP",
297 25: "CAMELLIA128-CTS-CMAC",
298 26: "CAMELLIA256-CTS-CMAC",
299}
300
301# https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-2
302
303_KRB_S_TYPES = {
304 1: "CRC32",
305 2: "RSA-MD4",
306 3: "RSA-MD4-DES",
307 4: "DES-MAC",
308 5: "DES-MAC-K",
309 6: "RSA-MD4-DES-K",
310 7: "RSA-MD5",
311 8: "RSA-MD5-DES",
312 9: "RSA-MD5-DES3",
313 10: "SHA1",
314 12: "HMAC-SHA1-DES3-KD",
315 13: "HMAC-SHA1-DES3",
316 14: "SHA1",
317 15: "HMAC-SHA1-96-AES128",
318 16: "HMAC-SHA1-96-AES256",
319 17: "CMAC-CAMELLIA128",
320 18: "CMAC-CAMELLIA256",
321 19: "HMAC-SHA256-128-AES128",
322 20: "HMAC-SHA384-192-AES256",
323 # RFC 4121
324 0x8003: "KRB-AUTHENTICATOR",
325 # [MS-KILE]
326 0xFFFFFF76: "MD5",
327 -138: "MD5",
328}
329
330
331class EncryptedData(ASN1_Packet):
332 ASN1_codec = ASN1_Codecs.BER
333 ASN1_root = ASN1F_SEQUENCE(
334 ASN1F_enum_INTEGER("etype", 0x17, _KRB_E_TYPES, explicit_tag=0xA0),
335 ASN1F_optional(UInt32("kvno", None, explicit_tag=0xA1)),
336 ASN1F_STRING("cipher", "", explicit_tag=0xA2),
337 )
338
339 def get_usage(self):
340 """
341 Get current key usage number and encrypted class
342 """
343 # RFC 4120 sect 7.5.1
344 if self.underlayer:
345 if isinstance(self.underlayer, PADATA):
346 patype = self.underlayer.padataType
347 if patype == 2:
348 # AS-REQ PA-ENC-TIMESTAMP padata timestamp
349 return 1, PA_ENC_TS_ENC
350 elif patype == 138:
351 # RFC6113 PA-ENC-TS-ENC
352 return 54, PA_ENC_TS_ENC
353 elif isinstance(self.underlayer, KRB_Ticket):
354 # AS-REP Ticket and TGS-REP Ticket
355 return 2, EncTicketPart
356 elif isinstance(self.underlayer, KRB_AS_REP):
357 # AS-REP encrypted part
358 return 3, EncASRepPart
359 elif isinstance(self.underlayer, KRB_AP_REQ) and isinstance(
360 self.underlayer.underlayer, PADATA
361 ):
362 # TGS-REQ PA-TGS-REQ Authenticator
363 return 7, KRB_Authenticator
364 elif isinstance(self.underlayer, KRB_TGS_REP):
365 # TGS-REP encrypted part
366 return 8, EncTGSRepPart
367 elif isinstance(self.underlayer, KRB_AP_REQ):
368 # AP-REQ Authenticator
369 return 11, KRB_Authenticator
370 elif isinstance(self.underlayer, KRB_AP_REP):
371 # AP-REP encrypted part
372 return 12, EncAPRepPart
373 elif isinstance(self.underlayer, KRB_PRIV):
374 # KRB-PRIV encrypted part
375 return 13, EncKrbPrivPart
376 elif isinstance(self.underlayer, KRB_CRED):
377 # KRB-CRED encrypted part
378 return 14, EncKrbCredPart
379 elif isinstance(self.underlayer, KrbFastArmoredReq):
380 # KEY_USAGE_FAST_ENC
381 return 51, KrbFastReq
382 elif isinstance(self.underlayer, KrbFastArmoredRep):
383 # KEY_USAGE_FAST_REP
384 return 52, KrbFastResponse
385 raise ValueError(
386 "Could not guess key usage number. Please specify key_usage_number"
387 )
388
389 def decrypt(self, key, key_usage_number=None, cls=None):
390 """
391 Decrypt and return the data contained in cipher.
392
393 :param key: the key to use for decryption
394 :param key_usage_number: (optional) specify the key usage number.
395 Guessed otherwise
396 :param cls: (optional) the class of the decrypted payload
397 Guessed otherwise (or bytes)
398 """
399 if key_usage_number is None:
400 key_usage_number, cls = self.get_usage()
401 d = key.decrypt(key_usage_number, self.cipher.val)
402 if cls:
403 try:
404 return cls(d)
405 except BER_Decoding_Error:
406 if cls == EncASRepPart:
407 # https://datatracker.ietf.org/doc/html/rfc4120#section-5.4.2
408 # "Compatibility note: Some implementations unconditionally send an
409 # encrypted EncTGSRepPart (application tag number 26) in this field
410 # regardless of whether the reply is a AS-REP or a TGS-REP. In the
411 # interest of compatibility, implementors MAY relax the check on the
412 # tag number of the decrypted ENC-PART."
413 try:
414 res = EncTGSRepPart(d)
415 # https://github.com/krb5/krb5/blob/48ccd81656381522d1f9ccb8705c13f0266a46ab/src/lib/krb5/asn.1/asn1_k_encode.c#L1128
416 # This is a bug because as the RFC clearly says above, we're
417 # perfectly in our right to be strict on this. (MAY)
418 log_runtime.warning(
419 "Implementation bug detected. This looks like MIT Kerberos."
420 )
421 return res
422 except BER_Decoding_Error:
423 pass
424 raise
425 return d
426
427 def encrypt(self, key, text, confounder=None, key_usage_number=None):
428 """
429 Encrypt text and set it into cipher.
430
431 :param key: the key to use for encryption
432 :param text: the bytes value to encode
433 :param confounder: (optional) specify the confounder bytes. Random otherwise
434 :param key_usage_number: (optional) specify the key usage number.
435 Guessed otherwise
436 """
437 if key_usage_number is None:
438 key_usage_number = self.get_usage()[0]
439 self.etype = key.etype
440 self.cipher = ASN1_STRING(
441 key.encrypt(key_usage_number, text, confounder=confounder)
442 )
443
444
445class EncryptionKey(ASN1_Packet):
446 ASN1_codec = ASN1_Codecs.BER
447 ASN1_root = ASN1F_SEQUENCE(
448 ASN1F_enum_INTEGER("keytype", 0, _KRB_E_TYPES, explicit_tag=0xA0),
449 ASN1F_STRING("keyvalue", "", explicit_tag=0xA1),
450 )
451
452 def toKey(self):
453 from scapy.libs.rfc3961 import Key
454
455 return Key(
456 etype=self.keytype.val,
457 key=self.keyvalue.val,
458 )
459
460 @classmethod
461 def fromKey(self, key):
462 return EncryptionKey(
463 keytype=key.etype,
464 keyvalue=key.key,
465 )
466
467
468class _Checksum_Field(ASN1F_STRING_PacketField):
469 def m2i(self, pkt, s):
470 val = super(_Checksum_Field, self).m2i(pkt, s)
471 if not val[0].val:
472 return val
473 if pkt.cksumtype.val == 0x8003:
474 # Special case per RFC 4121
475 return KRB_AuthenticatorChecksum(val[0].val, _underlayer=pkt), val[1]
476 return val
477
478
479class Checksum(ASN1_Packet):
480 ASN1_codec = ASN1_Codecs.BER
481 ASN1_root = ASN1F_SEQUENCE(
482 ASN1F_enum_INTEGER(
483 "cksumtype",
484 0,
485 _KRB_S_TYPES,
486 explicit_tag=0xA0,
487 ),
488 _Checksum_Field("checksum", "", explicit_tag=0xA1),
489 )
490
491 def get_usage(self):
492 """
493 Get current key usage number
494 """
495 # RFC 4120 sect 7.5.1
496 if self.underlayer:
497 if isinstance(self.underlayer, KRB_Authenticator):
498 # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator cksum
499 # (n°10 should never happen as we use RFC4121)
500 return 6
501 elif isinstance(self.underlayer, PA_FOR_USER):
502 # [MS-SFU] sect 2.2.1
503 return 17
504 elif isinstance(self.underlayer, PA_S4U_X509_USER):
505 # [MS-SFU] sect 2.2.2
506 return 26
507 elif isinstance(self.underlayer, AD_KDCIssued):
508 # AD-KDC-ISSUED checksum
509 return 19
510 elif isinstance(self.underlayer, KrbFastArmoredReq):
511 # KEY_USAGE_FAST_REQ_CHKSUM
512 return 50
513 elif isinstance(self.underlayer, KrbFastFinished):
514 # KEY_USAGE_FAST_FINISHED
515 return 53
516 raise ValueError(
517 "Could not guess key usage number. Please specify key_usage_number"
518 )
519
520 def verify(self, key, text, key_usage_number=None):
521 """
522 Decrypt and return the data contained in cipher.
523
524 :param key: the key to use to check the checksum
525 :param text: the bytes to verify
526 :param key_usage_number: (optional) specify the key usage number.
527 Guessed otherwise
528 """
529 if key_usage_number is None:
530 key_usage_number = self.get_usage()
531 key.verify_checksum(key_usage_number, text, self.checksum.val)
532
533 def make(self, key, text, key_usage_number=None, cksumtype=None):
534 """
535 Encrypt text and set it into cipher.
536
537 :param key: the key to use to make the checksum
538 :param text: the bytes to make a checksum of
539 :param key_usage_number: (optional) specify the key usage number.
540 Guessed otherwise
541 """
542 if key_usage_number is None:
543 key_usage_number = self.get_usage()
544 self.cksumtype = cksumtype or key.cksumtype
545 self.checksum = ASN1_STRING(
546 key.make_checksum(
547 keyusage=key_usage_number,
548 text=text,
549 cksumtype=self.cksumtype,
550 )
551 )
552
553
554KerberosFlags = ASN1F_FLAGS
555
556_ADDR_TYPES = {
557 # RFC4120 sect 7.5.3
558 0x02: "IPv4",
559 0x03: "Directional",
560 0x05: "ChaosNet",
561 0x06: "XNS",
562 0x07: "ISO",
563 0x0C: "DECNET Phase IV",
564 0x10: "AppleTalk DDP",
565 0x14: "NetBios",
566 0x18: "IPv6",
567}
568
569
570class HostAddress(ASN1_Packet):
571 ASN1_codec = ASN1_Codecs.BER
572 ASN1_root = ASN1F_SEQUENCE(
573 ASN1F_enum_INTEGER(
574 "addrType",
575 0,
576 _ADDR_TYPES,
577 explicit_tag=0xA0,
578 ),
579 ASN1F_STRING("address", "", explicit_tag=0xA1),
580 )
581
582
583HostAddresses = lambda name, **kwargs: ASN1F_SEQUENCE_OF(
584 name, [], HostAddress, **kwargs
585)
586
587
588_AUTHORIZATIONDATA_VALUES = {
589 # Filled below
590}
591
592
593class _AuthorizationData_value_Field(ASN1F_STRING_PacketField):
594 def m2i(self, pkt, s):
595 val = super(_AuthorizationData_value_Field, self).m2i(pkt, s)
596 if not val[0].val:
597 return val
598 if pkt.adType.val in _AUTHORIZATIONDATA_VALUES:
599 return (
600 _AUTHORIZATIONDATA_VALUES[pkt.adType.val](val[0].val, _underlayer=pkt),
601 val[1],
602 )
603 return val
604
605
606_AD_TYPES = {
607 # RFC4120 sect 7.5.4
608 1: "AD-IF-RELEVANT",
609 2: "AD-INTENDED-FOR-SERVER",
610 3: "AD-INTENDED-FOR-APPLICATION-CLASS",
611 4: "AD-KDC-ISSUED",
612 5: "AD-AND-OR",
613 6: "AD-MANDATORY-TICKET-EXTENSIONS",
614 7: "AD-IN-TICKET-EXTENSIONS",
615 8: "AD-MANDATORY-FOR-KDC",
616 64: "OSF-DCE",
617 65: "SESAME",
618 66: "AD-OSD-DCE-PKI-CERTID",
619 128: "AD-WIN2K-PAC",
620 129: "AD-ETYPE-NEGOTIATION",
621 # [MS-KILE] additions
622 141: "KERB-AUTH-DATA-TOKEN-RESTRICTIONS",
623 142: "KERB-LOCAL",
624 143: "AD-AUTH-DATA-AP-OPTIONS",
625 144: "KERB-AUTH-DATA-CLIENT-TARGET",
626}
627
628
629class AuthorizationDataItem(ASN1_Packet):
630 ASN1_codec = ASN1_Codecs.BER
631 ASN1_root = ASN1F_SEQUENCE(
632 ASN1F_enum_INTEGER(
633 "adType",
634 0,
635 _AD_TYPES,
636 explicit_tag=0xA0,
637 ),
638 _AuthorizationData_value_Field("adData", "", explicit_tag=0xA1),
639 )
640
641
642class AuthorizationData(ASN1_Packet):
643 ASN1_codec = ASN1_Codecs.BER
644 ASN1_root = ASN1F_SEQUENCE_OF(
645 "seq", [AuthorizationDataItem()], AuthorizationDataItem
646 )
647
648 def getAuthData(self, adType):
649 return next((x.adData for x in self.seq if x.adType == adType), None)
650
651
652AD_IF_RELEVANT = AuthorizationData
653_AUTHORIZATIONDATA_VALUES[1] = AD_IF_RELEVANT
654
655
656class AD_KDCIssued(ASN1_Packet):
657 ASN1_codec = ASN1_Codecs.BER
658 ASN1_root = ASN1F_SEQUENCE(
659 ASN1F_PACKET("adChecksum", Checksum(), Checksum, explicit_tag=0xA0),
660 ASN1F_optional(
661 Realm("iRealm", "", explicit_tag=0xA1),
662 ),
663 ASN1F_optional(ASN1F_PACKET("iSname", None, PrincipalName, explicit_tag=0xA2)),
664 ASN1F_PACKET("elements", None, AuthorizationData, explicit_tag=0xA3),
665 )
666
667
668_AUTHORIZATIONDATA_VALUES[4] = AD_KDCIssued
669
670
671class AD_AND_OR(ASN1_Packet):
672 ASN1_codec = ASN1_Codecs.BER
673 ASN1_root = ASN1F_SEQUENCE(
674 Int32("conditionCount", 0, explicit_tag=0xA0),
675 ASN1F_PACKET("elements", None, AuthorizationData, explicit_tag=0xA1),
676 )
677
678
679_AUTHORIZATIONDATA_VALUES[5] = AD_AND_OR
680
681ADMANDATORYFORKDC = AuthorizationData
682_AUTHORIZATIONDATA_VALUES[8] = ADMANDATORYFORKDC
683
684
685# https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xml
686_PADATA_TYPES = {
687 1: "PA-TGS-REQ",
688 2: "PA-ENC-TIMESTAMP",
689 3: "PA-PW-SALT",
690 11: "PA-ETYPE-INFO",
691 14: "PA-PK-AS-REQ-OLD",
692 15: "PA-PK-AS-REP-OLD",
693 16: "PA-PK-AS-REQ",
694 17: "PA-PK-AS-REP",
695 19: "PA-ETYPE-INFO2",
696 20: "PA-SVR-REFERRAL-INFO",
697 111: "TD-CMS-DIGEST-ALGORITHMS",
698 128: "PA-PAC-REQUEST",
699 129: "PA-FOR-USER",
700 130: "PA-FOR-X509-USER",
701 131: "PA-FOR-CHECK_DUPS",
702 132: "PA-AS-CHECKSUM",
703 133: "PA-FX-COOKIE",
704 134: "PA-AUTHENTICATION-SET",
705 135: "PA-AUTH-SET-SELECTED",
706 136: "PA-FX-FAST",
707 137: "PA-FX-ERROR",
708 138: "PA-ENCRYPTED-CHALLENGE",
709 141: "PA-OTP-CHALLENGE",
710 142: "PA-OTP-REQUEST",
711 143: "PA-OTP-CONFIRM",
712 144: "PA-OTP-PIN-CHANGE",
713 145: "PA-EPAK-AS-REQ",
714 146: "PA-EPAK-AS-REP",
715 147: "PA-PKINIT-KX",
716 148: "PA-PKU2U-NAME",
717 149: "PA-REQ-ENC-PA-REP",
718 150: "PA-AS-FRESHNESS",
719 151: "PA-SPAKE",
720 161: "KERB-KEY-LIST-REQ",
721 162: "KERB-KEY-LIST-REP",
722 165: "PA-SUPPORTED-ENCTYPES",
723 166: "PA-EXTENDED-ERROR",
724 167: "PA-PAC-OPTIONS",
725 170: "KERB-SUPERSEDED-BY-USER",
726 171: "KERB-DMSA-KEY-PACKAGE",
727}
728
729_PADATA_CLASSES = {
730 # Filled elsewhere in this file
731}
732
733
734# RFC4120
735
736
737class _PADATA_value_Field(ASN1F_STRING_PacketField):
738 """
739 A special field that properly dispatches PA-DATA values according to
740 padata-type and if the paquet is a request or a response.
741 """
742
743 def m2i(self, pkt, s):
744 val = super(_PADATA_value_Field, self).m2i(pkt, s)
745 if pkt.padataType.val in _PADATA_CLASSES:
746 cls = _PADATA_CLASSES[pkt.padataType.val]
747 if isinstance(cls, tuple):
748 parent = pkt.underlayer or pkt.parent
749 is_reply = False
750 if parent is not None:
751 if isinstance(parent, (KRB_AS_REP, KRB_TGS_REP)):
752 is_reply = True
753 else:
754 parent = parent.underlayer or parent.parent
755 is_reply = isinstance(parent, KRB_ERROR)
756 cls = cls[is_reply]
757 if not val[0].val:
758 return val
759 return cls(val[0].val, _underlayer=pkt), val[1]
760 return val
761
762
763class PADATA(ASN1_Packet):
764 ASN1_codec = ASN1_Codecs.BER
765 ASN1_root = ASN1F_SEQUENCE(
766 ASN1F_enum_INTEGER("padataType", 0, _PADATA_TYPES, explicit_tag=0xA1),
767 _PADATA_value_Field(
768 "padataValue",
769 "",
770 explicit_tag=0xA2,
771 ),
772 )
773
774
775# RFC 4120 sect 5.2.7.2
776
777
778class PA_ENC_TS_ENC(ASN1_Packet):
779 ASN1_codec = ASN1_Codecs.BER
780 ASN1_root = ASN1F_SEQUENCE(
781 KerberosTime("patimestamp", GeneralizedTime(), explicit_tag=0xA0),
782 ASN1F_optional(Microseconds("pausec", 0, explicit_tag=0xA1)),
783 )
784
785
786_PADATA_CLASSES[2] = EncryptedData # PA-ENC-TIMESTAMP
787_PADATA_CLASSES[138] = EncryptedData # PA-ENCRYPTED-CHALLENGE
788
789
790# RFC 4120 sect 5.2.7.4
791
792
793class ETYPE_INFO_ENTRY(ASN1_Packet):
794 ASN1_codec = ASN1_Codecs.BER
795 ASN1_root = ASN1F_SEQUENCE(
796 ASN1F_enum_INTEGER("etype", 0x1, _KRB_E_TYPES, explicit_tag=0xA0),
797 ASN1F_optional(
798 ASN1F_STRING("salt", "", explicit_tag=0xA1),
799 ),
800 )
801
802
803class ETYPE_INFO(ASN1_Packet):
804 ASN1_codec = ASN1_Codecs.BER
805 ASN1_root = ASN1F_SEQUENCE_OF("seq", [ETYPE_INFO_ENTRY()], ETYPE_INFO_ENTRY)
806
807
808_PADATA_CLASSES[11] = ETYPE_INFO
809
810# RFC 4120 sect 5.2.7.5
811
812
813class ETYPE_INFO_ENTRY2(ASN1_Packet):
814 ASN1_codec = ASN1_Codecs.BER
815 ASN1_root = ASN1F_SEQUENCE(
816 ASN1F_enum_INTEGER("etype", 0x1, _KRB_E_TYPES, explicit_tag=0xA0),
817 ASN1F_optional(
818 KerberosString("salt", "", explicit_tag=0xA1),
819 ),
820 ASN1F_optional(
821 ASN1F_STRING("s2kparams", "", explicit_tag=0xA2),
822 ),
823 )
824
825
826class ETYPE_INFO2(ASN1_Packet):
827 ASN1_codec = ASN1_Codecs.BER
828 ASN1_root = ASN1F_SEQUENCE_OF("seq", [ETYPE_INFO_ENTRY2()], ETYPE_INFO_ENTRY2)
829
830
831_PADATA_CLASSES[19] = ETYPE_INFO2
832
833
834# RFC8636 - PKINIT Algorithm Agility
835
836
837class TD_CMS_DIGEST_ALGORITHMS(ASN1_Packet):
838 ASN1_codec = ASN1_Codecs.BER
839 ASN1_root = ASN1F_SEQUENCE_OF("seq", [], X509_AlgorithmIdentifier)
840
841
842_PADATA_CLASSES[111] = TD_CMS_DIGEST_ALGORITHMS
843
844
845# PADATA Extended with RFC6113
846
847
848class PA_AUTHENTICATION_SET_ELEM(ASN1_Packet):
849 ASN1_codec = ASN1_Codecs.BER
850 ASN1_root = ASN1F_SEQUENCE(
851 Int32("paType", 0, explicit_tag=0xA0),
852 ASN1F_optional(
853 ASN1F_STRING("paHint", "", explicit_tag=0xA1),
854 ),
855 ASN1F_optional(
856 ASN1F_STRING("paValue", "", explicit_tag=0xA2),
857 ),
858 )
859
860
861class PA_AUTHENTICATION_SET(ASN1_Packet):
862 ASN1_codec = ASN1_Codecs.BER
863 ASN1_root = ASN1F_SEQUENCE_OF(
864 "elems", [PA_AUTHENTICATION_SET_ELEM()], PA_AUTHENTICATION_SET_ELEM
865 )
866
867
868_PADATA_CLASSES[134] = PA_AUTHENTICATION_SET
869
870
871# [MS-KILE] sect 2.2.3
872
873
874class PA_PAC_REQUEST(ASN1_Packet):
875 ASN1_codec = ASN1_Codecs.BER
876 ASN1_root = ASN1F_SEQUENCE(
877 ASN1F_BOOLEAN("includePac", True, explicit_tag=0xA0),
878 )
879
880
881_PADATA_CLASSES[128] = PA_PAC_REQUEST
882
883
884# [MS-KILE] sect 2.2.5
885
886
887class LSAP_TOKEN_INFO_INTEGRITY(Packet):
888 fields_desc = [
889 FlagsField(
890 "Flags",
891 0,
892 -32,
893 {
894 0x00000001: "UAC-Restricted",
895 },
896 ),
897 LEIntEnumField(
898 "TokenIL",
899 0x00002000,
900 {
901 0x00000000: "Untrusted",
902 0x00001000: "Low",
903 0x00002000: "Medium",
904 0x00003000: "High",
905 0x00004000: "System",
906 0x00005000: "Protected process",
907 },
908 ),
909 MayEnd(XStrFixedLenField("MachineID", b"", length=32)),
910 # KB 5068222 - still waiting for [MS-KILE] update (oct. 2025)
911 XStrFixedLenField("PermanentMachineID", b"", length=32),
912 ]
913
914
915# [MS-KILE] sect 2.2.6
916
917
918class _KerbAdRestrictionEntry_Field(ASN1F_STRING_PacketField):
919 def m2i(self, pkt, s):
920 val = super(_KerbAdRestrictionEntry_Field, self).m2i(pkt, s)
921 if not val[0].val:
922 return val
923 if pkt.restrictionType.val == 0x0000: # LSAP_TOKEN_INFO_INTEGRITY
924 return LSAP_TOKEN_INFO_INTEGRITY(val[0].val, _underlayer=pkt), val[1]
925 return val
926
927
928class KERB_AD_RESTRICTION_ENTRY(ASN1_Packet):
929 name = "KERB-AD-RESTRICTION-ENTRY"
930 ASN1_codec = ASN1_Codecs.BER
931 ASN1_root = ASN1F_SEQUENCE(
932 ASN1F_SEQUENCE(
933 ASN1F_enum_INTEGER(
934 "restrictionType",
935 0,
936 {0: "LSAP_TOKEN_INFO_INTEGRITY"},
937 explicit_tag=0xA0,
938 ),
939 _KerbAdRestrictionEntry_Field("restriction", b"", explicit_tag=0xA1),
940 )
941 )
942
943
944_AUTHORIZATIONDATA_VALUES[141] = KERB_AD_RESTRICTION_ENTRY
945
946
947# [MS-KILE] sect 3.2.5.8
948
949
950class KERB_AUTH_DATA_AP_OPTIONS(Packet):
951 name = "KERB-AUTH-DATA-AP-OPTIONS"
952 fields_desc = [
953 LEIntEnumField(
954 "apOptions",
955 0x4000,
956 {
957 0x4000: "KERB_AP_OPTIONS_CBT",
958 0x8000: "KERB_AP_OPTIONS_UNVERIFIED_TARGET_NAME",
959 },
960 ),
961 ]
962
963
964_AUTHORIZATIONDATA_VALUES[143] = KERB_AUTH_DATA_AP_OPTIONS
965
966
967# This has no doc..? [MS-KILE] only mentions its name.
968
969
970class KERB_AUTH_DATA_CLIENT_TARGET(Packet):
971 name = "KERB-AD-TARGET-PRINCIPAL"
972 fields_desc = [
973 StrFieldUtf16("spn", ""),
974 ]
975
976
977_AUTHORIZATIONDATA_VALUES[144] = KERB_AUTH_DATA_CLIENT_TARGET
978
979
980# RFC6806 sect 6
981
982
983class KERB_AD_LOGIN_ALIAS(ASN1_Packet):
984 ASN1_codec = ASN1_Codecs.BER
985 ASN1_root = ASN1F_SEQUENCE(ASN1F_SEQUENCE_OF("loginAliases", [], PrincipalName))
986
987
988_AUTHORIZATIONDATA_VALUES[80] = KERB_AD_LOGIN_ALIAS
989
990
991# [MS-KILE] sect 2.2.8
992
993
994class PA_SUPPORTED_ENCTYPES(Packet):
995 fields_desc = [
996 FlagsField(
997 "flags",
998 0,
999 -32,
1000 [
1001 "DES-CBC-CRC",
1002 "DES-CBC-MD5",
1003 "RC4-HMAC",
1004 "AES128-CTS-HMAC-SHA1-96",
1005 "AES256-CTS-HMAC-SHA1-96",
1006 ]
1007 + ["bit_%d" % i for i in range(11)]
1008 + [
1009 "FAST-supported",
1010 "Compount-identity-supported",
1011 "Claims-supported",
1012 "Resource-SID-compression-disabled",
1013 ],
1014 )
1015 ]
1016
1017
1018_PADATA_CLASSES[165] = PA_SUPPORTED_ENCTYPES
1019
1020# [MS-KILE] sect 2.2.10
1021
1022
1023class PA_PAC_OPTIONS(ASN1_Packet):
1024 ASN1_codec = ASN1_Codecs.BER
1025 ASN1_root = ASN1F_SEQUENCE(
1026 KerberosFlags(
1027 "options",
1028 "",
1029 [
1030 "Claims",
1031 "Branch-Aware",
1032 "Forward-to-Full-DC",
1033 "Resource-based-constrained-delegation", # [MS-SFU] 2.2.5
1034 ],
1035 explicit_tag=0xA0,
1036 )
1037 )
1038
1039
1040_PADATA_CLASSES[167] = PA_PAC_OPTIONS
1041
1042# [MS-KILE] sect 2.2.11
1043
1044
1045class KERB_KEY_LIST_REQ(ASN1_Packet):
1046 ASN1_codec = ASN1_Codecs.BER
1047 ASN1_root = ASN1F_SEQUENCE_OF(
1048 "keytypes",
1049 [],
1050 ASN1F_enum_INTEGER("", 0, _KRB_E_TYPES),
1051 )
1052
1053
1054_PADATA_CLASSES[161] = KERB_KEY_LIST_REQ
1055
1056# [MS-KILE] sect 2.2.12
1057
1058
1059class KERB_KEY_LIST_REP(ASN1_Packet):
1060 ASN1_codec = ASN1_Codecs.BER
1061 ASN1_root = ASN1F_SEQUENCE_OF(
1062 "keys",
1063 [],
1064 ASN1F_PACKET("", None, EncryptionKey),
1065 )
1066
1067
1068_PADATA_CLASSES[162] = KERB_KEY_LIST_REP
1069
1070# [MS-KILE] sect 2.2.13
1071
1072
1073class KERB_SUPERSEDED_BY_USER(ASN1_Packet):
1074 ASN1_codec = ASN1_Codecs.BER
1075 ASN1_root = ASN1F_SEQUENCE(
1076 ASN1F_PACKET("name", None, PrincipalName, explicit_tag=0xA0),
1077 Realm("realm", None, explicit_tag=0xA1),
1078 )
1079
1080
1081_PADATA_CLASSES[170] = KERB_SUPERSEDED_BY_USER
1082
1083
1084# [MS-KILE] sect 2.2.14
1085
1086
1087class KERB_DMSA_KEY_PACKAGE(ASN1_Packet):
1088 ASN1_codec = ASN1_Codecs.BER
1089 ASN1_root = ASN1F_SEQUENCE(
1090 ASN1F_SEQUENCE_OF(
1091 "currentKeys",
1092 [],
1093 ASN1F_PACKET("", None, EncryptionKey),
1094 explicit_tag=0xA0,
1095 ),
1096 ASN1F_optional(
1097 ASN1F_SEQUENCE_OF(
1098 "previousKeys",
1099 [],
1100 ASN1F_PACKET("", None, EncryptionKey),
1101 explicit_tag=0xA1,
1102 ),
1103 ),
1104 KerberosTime("expirationInterval", GeneralizedTime(), explicit_tag=0xA2),
1105 KerberosTime("fetchInterval", GeneralizedTime(), explicit_tag=0xA4),
1106 )
1107
1108
1109_PADATA_CLASSES[171] = KERB_DMSA_KEY_PACKAGE
1110
1111
1112# RFC6113 sect 5.4.1
1113
1114
1115class _KrbFastArmor_value_Field(ASN1F_STRING_PacketField):
1116 def m2i(self, pkt, s):
1117 val = super(_KrbFastArmor_value_Field, self).m2i(pkt, s)
1118 if not val[0].val:
1119 return val
1120 if pkt.armorType.val == 1: # FX_FAST_ARMOR_AP_REQUEST
1121 return KRB_AP_REQ(val[0].val, _underlayer=pkt), val[1]
1122 return val
1123
1124
1125class KrbFastArmor(ASN1_Packet):
1126 ASN1_codec = ASN1_Codecs.BER
1127 ASN1_root = ASN1F_SEQUENCE(
1128 ASN1F_enum_INTEGER(
1129 "armorType", 1, {1: "FX_FAST_ARMOR_AP_REQUEST"}, explicit_tag=0xA0
1130 ),
1131 _KrbFastArmor_value_Field("armorValue", "", explicit_tag=0xA1),
1132 )
1133
1134
1135# RFC6113 sect 5.4.2
1136
1137
1138class KrbFastArmoredReq(ASN1_Packet):
1139 ASN1_codec = ASN1_Codecs.BER
1140 ASN1_root = ASN1F_SEQUENCE(
1141 ASN1F_SEQUENCE(
1142 ASN1F_optional(
1143 ASN1F_PACKET("armor", None, KrbFastArmor, explicit_tag=0xA0)
1144 ),
1145 ASN1F_PACKET("reqChecksum", Checksum(), Checksum, explicit_tag=0xA1),
1146 ASN1F_PACKET("encFastReq", None, EncryptedData, explicit_tag=0xA2),
1147 )
1148 )
1149
1150
1151class PA_FX_FAST_REQUEST(ASN1_Packet):
1152 ASN1_codec = ASN1_Codecs.BER
1153 ASN1_root = ASN1F_CHOICE(
1154 "armoredData",
1155 ASN1_STRING(""),
1156 ASN1F_PACKET("req", KrbFastArmoredReq, KrbFastArmoredReq, implicit_tag=0xA0),
1157 )
1158
1159
1160# RFC6113 sect 5.4.3
1161
1162
1163class KrbFastArmoredRep(ASN1_Packet):
1164 ASN1_codec = ASN1_Codecs.BER
1165 ASN1_root = ASN1F_SEQUENCE(
1166 ASN1F_SEQUENCE(
1167 ASN1F_PACKET("encFastRep", None, EncryptedData, explicit_tag=0xA0),
1168 )
1169 )
1170
1171
1172class PA_FX_FAST_REPLY(ASN1_Packet):
1173 ASN1_codec = ASN1_Codecs.BER
1174 ASN1_root = ASN1F_CHOICE(
1175 "armoredData",
1176 ASN1_STRING(""),
1177 ASN1F_PACKET("req", KrbFastArmoredRep, KrbFastArmoredRep, implicit_tag=0xA0),
1178 )
1179
1180
1181class KrbFastFinished(ASN1_Packet):
1182 ASN1_codec = ASN1_Codecs.BER
1183 ASN1_root = ASN1F_SEQUENCE(
1184 KerberosTime("timestamp", GeneralizedTime(), explicit_tag=0xA0),
1185 Microseconds("usec", 0, explicit_tag=0xA1),
1186 Realm("crealm", "", explicit_tag=0xA2),
1187 ASN1F_PACKET("cname", None, PrincipalName, explicit_tag=0xA3),
1188 ASN1F_PACKET("ticketChecksum", Checksum(), Checksum, explicit_tag=0xA4),
1189 )
1190
1191
1192class KrbFastResponse(ASN1_Packet):
1193 ASN1_codec = ASN1_Codecs.BER
1194 ASN1_root = ASN1F_SEQUENCE(
1195 ASN1F_SEQUENCE_OF("padata", [PADATA()], PADATA, explicit_tag=0xA0),
1196 ASN1F_optional(
1197 ASN1F_PACKET("strengthenKey", None, EncryptionKey, explicit_tag=0xA1)
1198 ),
1199 ASN1F_optional(
1200 ASN1F_PACKET(
1201 "finished", KrbFastFinished(), KrbFastFinished, explicit_tag=0xA2
1202 )
1203 ),
1204 UInt32("nonce", 0, explicit_tag=0xA3),
1205 )
1206
1207
1208_PADATA_CLASSES[136] = (PA_FX_FAST_REQUEST, PA_FX_FAST_REPLY)
1209
1210
1211# RFC 4556 - PKINIT
1212
1213
1214# sect 3.2.1
1215
1216
1217class ExternalPrincipalIdentifier(ASN1_Packet):
1218 ASN1_codec = ASN1_Codecs.BER
1219 ASN1_root = ASN1F_SEQUENCE(
1220 ASN1F_optional(
1221 ASN1F_STRING_ENCAPS(
1222 "subjectName", None, X509_DirectoryName, implicit_tag=0x80
1223 ),
1224 ),
1225 ASN1F_optional(
1226 ASN1F_STRING_ENCAPS(
1227 "issuerAndSerialNumber",
1228 None,
1229 CMS_IssuerAndSerialNumber,
1230 implicit_tag=0x81,
1231 ),
1232 ),
1233 ASN1F_optional(
1234 ASN1F_STRING("subjectKeyIdentifier", "", implicit_tag=0x82),
1235 ),
1236 )
1237
1238
1239class PA_PK_AS_REQ(ASN1_Packet):
1240 ASN1_codec = ASN1_Codecs.BER
1241 ASN1_root = ASN1F_SEQUENCE(
1242 ASN1F_STRING_ENCAPS(
1243 "signedAuthpack",
1244 CMS_ContentInfo(),
1245 CMS_ContentInfo,
1246 implicit_tag=0x80,
1247 ),
1248 ASN1F_optional(
1249 ASN1F_SEQUENCE_OF(
1250 "trustedCertifiers",
1251 [ExternalPrincipalIdentifier()],
1252 ExternalPrincipalIdentifier,
1253 explicit_tag=0xA1,
1254 ),
1255 ),
1256 ASN1F_optional(
1257 ASN1F_STRING("kdcPkId", "", implicit_tag=0xA2),
1258 ),
1259 )
1260
1261
1262_PADATA_CLASSES[16] = PA_PK_AS_REQ
1263
1264
1265# [MS-PKCA] sect 2.2.3
1266
1267
1268class PAChecksum2(ASN1_Packet):
1269 ASN1_codec = ASN1_Codecs.BER
1270 ASN1_root = ASN1F_SEQUENCE(
1271 ASN1F_STRING("checksum", "", explicit_tag=0xA0),
1272 ASN1F_PACKET(
1273 "algorithmIdentifier",
1274 X509_AlgorithmIdentifier(),
1275 X509_AlgorithmIdentifier,
1276 explicit_tag=0xA1,
1277 ),
1278 )
1279
1280
1281# still RFC 4556 sect 3.2.1
1282
1283
1284class PKAuthenticator(ASN1_Packet):
1285 ASN1_codec = ASN1_Codecs.BER
1286 ASN1_root = ASN1F_SEQUENCE(
1287 Microseconds("cusec", 0, explicit_tag=0xA0),
1288 KerberosTime("ctime", GeneralizedTime(), explicit_tag=0xA1),
1289 UInt32("nonce", 0, explicit_tag=0xA2),
1290 ASN1F_optional(
1291 ASN1F_STRING("paChecksum", "", explicit_tag=0xA3),
1292 ),
1293 # RFC8070 extension
1294 ASN1F_optional(
1295 ASN1F_STRING("freshnessToken", "", explicit_tag=0xA4),
1296 ),
1297 # [MS-PKCA] sect 2.2.3
1298 ASN1F_optional(
1299 ASN1F_PACKET("paChecksum2", None, PAChecksum2, explicit_tag=0xA5),
1300 ),
1301 )
1302
1303
1304# RFC8636 sect 6
1305
1306
1307class KDFAlgorithmId(ASN1_Packet):
1308 ASN1_codec = ASN1_Codecs.BER
1309 ASN1_root = ASN1F_SEQUENCE(
1310 ASN1F_OID("kdfId", "", explicit_tag=0xA0),
1311 )
1312
1313
1314# still RFC 4556 sect 3.2.1
1315
1316
1317class AuthPack(ASN1_Packet):
1318 ASN1_codec = ASN1_Codecs.BER
1319 ASN1_root = ASN1F_SEQUENCE(
1320 ASN1F_PACKET(
1321 "pkAuthenticator",
1322 PKAuthenticator(),
1323 PKAuthenticator,
1324 explicit_tag=0xA0,
1325 ),
1326 ASN1F_optional(
1327 ASN1F_PACKET(
1328 "clientPublicValue",
1329 X509_SubjectPublicKeyInfo(),
1330 X509_SubjectPublicKeyInfo,
1331 explicit_tag=0xA1,
1332 ),
1333 ),
1334 ASN1F_optional(
1335 ASN1F_SEQUENCE_OF(
1336 "supportedCMSTypes",
1337 [],
1338 X509_AlgorithmIdentifier,
1339 explicit_tag=0xA2,
1340 ),
1341 ),
1342 ASN1F_optional(
1343 ASN1F_STRING("clientDCNonce", None, explicit_tag=0xA3),
1344 ),
1345 # RFC8636 extension
1346 ASN1F_optional(
1347 ASN1F_SEQUENCE_OF("supportedKDFs", None, KDFAlgorithmId, explicit_tag=0xA4),
1348 ),
1349 )
1350
1351
1352_CMS_ENCAPSULATED["1.3.6.1.5.2.3.1"] = AuthPack
1353
1354# sect 3.2.3
1355
1356
1357class DHRepInfo(ASN1_Packet):
1358 ASN1_codec = ASN1_Codecs.BER
1359 ASN1_root = ASN1F_SEQUENCE(
1360 ASN1F_STRING_ENCAPS(
1361 "dhSignedData",
1362 CMS_ContentInfo(),
1363 CMS_ContentInfo,
1364 implicit_tag=0x80,
1365 ),
1366 ASN1F_optional(
1367 ASN1F_STRING("serverDHNonce", "", explicit_tag=0xA1),
1368 ),
1369 # RFC8636 extension
1370 ASN1F_optional(
1371 ASN1F_PACKET("kdf", None, KDFAlgorithmId, explicit_tag=0xA2),
1372 ),
1373 )
1374
1375
1376class EncKeyPack(ASN1_Packet):
1377 ASN1_codec = ASN1_Codecs.BER
1378 ASN1_root = ASN1F_STRING("encKeyPack", "")
1379
1380
1381class PA_PK_AS_REP(ASN1_Packet):
1382 ASN1_codec = ASN1_Codecs.BER
1383 ASN1_root = ASN1F_CHOICE(
1384 "rep",
1385 ASN1_STRING(""),
1386 ASN1F_PACKET("dhInfo", DHRepInfo(), DHRepInfo, explicit_tag=0xA0),
1387 ASN1F_PACKET("encKeyPack", EncKeyPack(), EncKeyPack, explicit_tag=0xA1),
1388 )
1389
1390
1391_PADATA_CLASSES[17] = PA_PK_AS_REP
1392
1393
1394class KDCDHKeyInfo(ASN1_Packet):
1395 ASN1_codec = ASN1_Codecs.BER
1396 ASN1_root = ASN1F_SEQUENCE(
1397 ASN1F_BIT_STRING_ENCAPS(
1398 "subjectPublicKey", DHPublicKey(), DHPublicKey, explicit_tag=0xA0
1399 ),
1400 UInt32("nonce", 0, explicit_tag=0xA1),
1401 ASN1F_optional(
1402 KerberosTime("dhKeyExpiration", None, explicit_tag=0xA2),
1403 ),
1404 )
1405
1406
1407_CMS_ENCAPSULATED["1.3.6.1.5.2.3.2"] = KDCDHKeyInfo
1408
1409# [MS-SFU]
1410
1411
1412# sect 2.2.1
1413class PA_FOR_USER(ASN1_Packet):
1414 ASN1_codec = ASN1_Codecs.BER
1415 ASN1_root = ASN1F_SEQUENCE(
1416 ASN1F_PACKET("userName", PrincipalName(), PrincipalName, explicit_tag=0xA0),
1417 Realm("userRealm", "", explicit_tag=0xA1),
1418 ASN1F_PACKET("cksum", Checksum(), Checksum, explicit_tag=0xA2),
1419 KerberosString("authPackage", "Kerberos", explicit_tag=0xA3),
1420 )
1421
1422
1423_PADATA_CLASSES[129] = PA_FOR_USER
1424
1425
1426# sect 2.2.2
1427
1428
1429class S4UUserID(ASN1_Packet):
1430 ASN1_codec = ASN1_Codecs.BER
1431 ASN1_root = ASN1F_SEQUENCE(
1432 UInt32("nonce", 0, explicit_tag=0xA0),
1433 ASN1F_optional(
1434 ASN1F_PACKET("cname", None, PrincipalName, explicit_tag=0xA1),
1435 ),
1436 Realm("crealm", "", explicit_tag=0xA2),
1437 ASN1F_optional(
1438 ASN1F_STRING("subjectCertificate", None, explicit_tag=0xA3),
1439 ),
1440 ASN1F_optional(
1441 ASN1F_FLAGS(
1442 "options",
1443 "",
1444 [
1445 "reserved",
1446 "KDC_CHECK_LOGON_HOUR_RESTRICTIONS",
1447 "USE_REPLY_KEY_USAGE",
1448 "NT_AUTH_POLICY_NOT_REQUIRED",
1449 "UNCONDITIONAL_DELEGATION",
1450 ],
1451 explicit_tag=0xA4,
1452 )
1453 ),
1454 )
1455
1456
1457class PA_S4U_X509_USER(ASN1_Packet):
1458 ASN1_codec = ASN1_Codecs.BER
1459 ASN1_root = ASN1F_SEQUENCE(
1460 ASN1F_PACKET("userId", S4UUserID(), S4UUserID, explicit_tag=0xA0),
1461 ASN1F_PACKET("checksum", Checksum(), Checksum, explicit_tag=0xA1),
1462 )
1463
1464
1465_PADATA_CLASSES[130] = PA_S4U_X509_USER
1466
1467
1468# Back to RFC4120
1469
1470# sect 5.10
1471KRB_MSG_TYPES = {
1472 1: "Ticket",
1473 2: "Authenticator",
1474 3: "EncTicketPart",
1475 10: "AS-REQ",
1476 11: "AS-REP",
1477 12: "TGS-REQ",
1478 13: "TGS-REP",
1479 14: "AP-REQ",
1480 15: "AP-REP",
1481 16: "KRB-TGT-REQ", # U2U
1482 17: "KRB-TGT-REP", # U2U
1483 20: "KRB-SAFE",
1484 21: "KRB-PRIV",
1485 22: "KRB-CRED",
1486 25: "EncASRepPart",
1487 26: "EncTGSRepPart",
1488 27: "EncAPRepPart",
1489 28: "EncKrbPrivPart",
1490 29: "EnvKrbCredPart",
1491 30: "KRB-ERROR",
1492}
1493
1494# sect 5.3
1495
1496
1497class KRB_Ticket(ASN1_Packet):
1498 ASN1_codec = ASN1_Codecs.BER
1499 ASN1_root = ASN1F_SEQUENCE(
1500 ASN1F_SEQUENCE(
1501 ASN1F_INTEGER("tktVno", 5, explicit_tag=0xA0),
1502 Realm("realm", "", explicit_tag=0xA1),
1503 ASN1F_PACKET("sname", PrincipalName(), PrincipalName, explicit_tag=0xA2),
1504 ASN1F_PACKET("encPart", EncryptedData(), EncryptedData, explicit_tag=0xA3),
1505 ),
1506 implicit_tag=ASN1_Class_KRB.Ticket,
1507 )
1508
1509 def getSPN(self):
1510 return "%s@%s" % (
1511 self.sname.toString(),
1512 self.realm.val.decode(),
1513 )
1514
1515
1516class TransitedEncoding(ASN1_Packet):
1517 ASN1_codec = ASN1_Codecs.BER
1518 ASN1_root = ASN1F_SEQUENCE(
1519 Int32("trType", 0, explicit_tag=0xA0),
1520 ASN1F_STRING("contents", "", explicit_tag=0xA1),
1521 )
1522
1523
1524_TICKET_FLAGS = [
1525 "reserved",
1526 "forwardable",
1527 "forwarded",
1528 "proxiable",
1529 "proxy",
1530 "may-postdate",
1531 "postdated",
1532 "invalid",
1533 "renewable",
1534 "initial",
1535 "pre-authent",
1536 "hw-authent",
1537 "transited-since-policy-checked",
1538 "ok-as-delegate",
1539 "unused",
1540 "canonicalize", # RFC6806
1541 "anonymous", # RFC6112 + RFC8129
1542]
1543
1544
1545class EncTicketPart(ASN1_Packet):
1546 ASN1_codec = ASN1_Codecs.BER
1547 ASN1_root = ASN1F_SEQUENCE(
1548 ASN1F_SEQUENCE(
1549 KerberosFlags(
1550 "flags",
1551 "",
1552 _TICKET_FLAGS,
1553 explicit_tag=0xA0,
1554 ),
1555 ASN1F_PACKET("key", EncryptionKey(), EncryptionKey, explicit_tag=0xA1),
1556 Realm("crealm", "", explicit_tag=0xA2),
1557 ASN1F_PACKET("cname", PrincipalName(), PrincipalName, explicit_tag=0xA3),
1558 ASN1F_PACKET(
1559 "transited", TransitedEncoding(), TransitedEncoding, explicit_tag=0xA4
1560 ),
1561 KerberosTime("authtime", GeneralizedTime(), explicit_tag=0xA5),
1562 ASN1F_optional(
1563 KerberosTime("starttime", GeneralizedTime(), explicit_tag=0xA6)
1564 ),
1565 KerberosTime("endtime", GeneralizedTime(), explicit_tag=0xA7),
1566 ASN1F_optional(
1567 KerberosTime("renewTill", GeneralizedTime(), explicit_tag=0xA8),
1568 ),
1569 ASN1F_optional(
1570 HostAddresses("addresses", explicit_tag=0xA9),
1571 ),
1572 ASN1F_optional(
1573 ASN1F_PACKET(
1574 "authorizationData", None, AuthorizationData, explicit_tag=0xAA
1575 ),
1576 ),
1577 ),
1578 implicit_tag=ASN1_Class_KRB.EncTicketPart,
1579 )
1580
1581
1582# sect 5.4.1
1583
1584
1585class KRB_KDC_REQ_BODY(ASN1_Packet):
1586 ASN1_codec = ASN1_Codecs.BER
1587 ASN1_root = ASN1F_SEQUENCE(
1588 KerberosFlags(
1589 "kdcOptions",
1590 "",
1591 [
1592 "reserved",
1593 "forwardable",
1594 "forwarded",
1595 "proxiable",
1596 "proxy",
1597 "allow-postdate",
1598 "postdated",
1599 "unused7",
1600 "renewable",
1601 "unused9",
1602 "unused10",
1603 "opt-hardware-auth",
1604 "unused12",
1605 "unused13",
1606 "cname-in-addl-tkt", # [MS-SFU] sect 2.2.3
1607 "canonicalize", # RFC6806
1608 "request-anonymous", # RFC6112 + RFC8129
1609 ]
1610 + ["unused%d" % i for i in range(17, 26)]
1611 + [
1612 "disable-transited-check",
1613 "renewable-ok",
1614 "enc-tkt-in-skey",
1615 "unused29",
1616 "renew",
1617 "validate",
1618 ],
1619 explicit_tag=0xA0,
1620 ),
1621 ASN1F_optional(ASN1F_PACKET("cname", None, PrincipalName, explicit_tag=0xA1)),
1622 Realm("realm", "", explicit_tag=0xA2),
1623 ASN1F_optional(
1624 ASN1F_PACKET("sname", None, PrincipalName, explicit_tag=0xA3),
1625 ),
1626 ASN1F_optional(KerberosTime("from_", None, explicit_tag=0xA4)),
1627 KerberosTime("till", GeneralizedTime(), explicit_tag=0xA5),
1628 ASN1F_optional(KerberosTime("rtime", GeneralizedTime(), explicit_tag=0xA6)),
1629 UInt32("nonce", 0, explicit_tag=0xA7),
1630 ASN1F_SEQUENCE_OF("etype", [], Int32, explicit_tag=0xA8),
1631 ASN1F_optional(
1632 HostAddresses("addresses", explicit_tag=0xA9),
1633 ),
1634 ASN1F_optional(
1635 ASN1F_PACKET(
1636 "encAuthorizationData", None, EncryptedData, explicit_tag=0xAA
1637 ),
1638 ),
1639 ASN1F_optional(
1640 ASN1F_SEQUENCE_OF("additionalTickets", [], KRB_Ticket, explicit_tag=0xAB)
1641 ),
1642 )
1643
1644
1645KRB_KDC_REQ = ASN1F_SEQUENCE(
1646 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA1),
1647 ASN1F_enum_INTEGER("msgType", 10, KRB_MSG_TYPES, explicit_tag=0xA2),
1648 ASN1F_optional(ASN1F_SEQUENCE_OF("padata", [], PADATA, explicit_tag=0xA3)),
1649 ASN1F_PACKET("reqBody", KRB_KDC_REQ_BODY(), KRB_KDC_REQ_BODY, explicit_tag=0xA4),
1650)
1651
1652
1653class KrbFastReq(ASN1_Packet):
1654 # RFC6113 sect 5.4.2
1655 ASN1_codec = ASN1_Codecs.BER
1656 ASN1_root = ASN1F_SEQUENCE(
1657 KerberosFlags(
1658 "fastOptions",
1659 "",
1660 [
1661 "RESERVED",
1662 "hide-client-names",
1663 ]
1664 + ["res%d" % i for i in range(2, 16)]
1665 + ["kdc-follow-referrals"],
1666 explicit_tag=0xA0,
1667 ),
1668 ASN1F_SEQUENCE_OF("padata", [PADATA()], PADATA, explicit_tag=0xA1),
1669 ASN1F_PACKET("reqBody", None, KRB_KDC_REQ_BODY, explicit_tag=0xA2),
1670 )
1671
1672
1673class KRB_AS_REQ(ASN1_Packet):
1674 ASN1_codec = ASN1_Codecs.BER
1675 ASN1_root = ASN1F_SEQUENCE(
1676 KRB_KDC_REQ,
1677 implicit_tag=ASN1_Class_KRB.AS_REQ,
1678 )
1679
1680
1681class KRB_TGS_REQ(ASN1_Packet):
1682 ASN1_codec = ASN1_Codecs.BER
1683 ASN1_root = ASN1F_SEQUENCE(
1684 KRB_KDC_REQ,
1685 implicit_tag=ASN1_Class_KRB.TGS_REQ,
1686 )
1687 msgType = ASN1_INTEGER(12)
1688
1689
1690# sect 5.4.2
1691
1692KRB_KDC_REP = ASN1F_SEQUENCE(
1693 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA0),
1694 ASN1F_enum_INTEGER("msgType", 11, KRB_MSG_TYPES, explicit_tag=0xA1),
1695 ASN1F_optional(
1696 ASN1F_SEQUENCE_OF("padata", [], PADATA, explicit_tag=0xA2),
1697 ),
1698 Realm("crealm", "", explicit_tag=0xA3),
1699 ASN1F_PACKET("cname", None, PrincipalName, explicit_tag=0xA4),
1700 ASN1F_PACKET("ticket", None, KRB_Ticket, explicit_tag=0xA5),
1701 ASN1F_PACKET("encPart", None, EncryptedData, explicit_tag=0xA6),
1702)
1703
1704
1705class KRB_AS_REP(ASN1_Packet):
1706 ASN1_codec = ASN1_Codecs.BER
1707 ASN1_root = ASN1F_SEQUENCE(
1708 KRB_KDC_REP,
1709 implicit_tag=ASN1_Class_KRB.AS_REP,
1710 )
1711
1712
1713class KRB_TGS_REP(ASN1_Packet):
1714 ASN1_codec = ASN1_Codecs.BER
1715 ASN1_root = ASN1F_SEQUENCE(
1716 KRB_KDC_REP,
1717 implicit_tag=ASN1_Class_KRB.TGS_REP,
1718 )
1719
1720 def getUPN(self):
1721 return "%s@%s" % (
1722 self.cname.toString(),
1723 self.crealm.val.decode(),
1724 )
1725
1726
1727class LastReqItem(ASN1_Packet):
1728 ASN1_codec = ASN1_Codecs.BER
1729 ASN1_root = ASN1F_SEQUENCE(
1730 Int32("lrType", 0, explicit_tag=0xA0),
1731 KerberosTime("lrValue", GeneralizedTime(), explicit_tag=0xA1),
1732 )
1733
1734
1735EncKDCRepPart = ASN1F_SEQUENCE(
1736 ASN1F_PACKET("key", None, EncryptionKey, explicit_tag=0xA0),
1737 ASN1F_SEQUENCE_OF("lastReq", [], LastReqItem, explicit_tag=0xA1),
1738 UInt32("nonce", 0, explicit_tag=0xA2),
1739 ASN1F_optional(
1740 KerberosTime("keyExpiration", GeneralizedTime(), explicit_tag=0xA3),
1741 ),
1742 KerberosFlags(
1743 "flags",
1744 "",
1745 _TICKET_FLAGS,
1746 explicit_tag=0xA4,
1747 ),
1748 KerberosTime("authtime", GeneralizedTime(), explicit_tag=0xA5),
1749 ASN1F_optional(
1750 KerberosTime("starttime", GeneralizedTime(), explicit_tag=0xA6),
1751 ),
1752 KerberosTime("endtime", GeneralizedTime(), explicit_tag=0xA7),
1753 ASN1F_optional(
1754 KerberosTime("renewTill", GeneralizedTime(), explicit_tag=0xA8),
1755 ),
1756 Realm("srealm", "", explicit_tag=0xA9),
1757 ASN1F_PACKET("sname", PrincipalName(), PrincipalName, explicit_tag=0xAA),
1758 ASN1F_optional(
1759 HostAddresses("caddr", explicit_tag=0xAB),
1760 ),
1761 # RFC6806 sect 11
1762 ASN1F_optional(
1763 ASN1F_SEQUENCE_OF("encryptedPaData", [], PADATA, explicit_tag=0xAC),
1764 ),
1765)
1766
1767
1768class EncASRepPart(ASN1_Packet):
1769 ASN1_codec = ASN1_Codecs.BER
1770 ASN1_root = ASN1F_SEQUENCE(
1771 EncKDCRepPart,
1772 implicit_tag=ASN1_Class_KRB.EncASRepPart,
1773 )
1774
1775
1776class EncTGSRepPart(ASN1_Packet):
1777 ASN1_codec = ASN1_Codecs.BER
1778 ASN1_root = ASN1F_SEQUENCE(
1779 EncKDCRepPart,
1780 implicit_tag=ASN1_Class_KRB.EncTGSRepPart,
1781 )
1782
1783
1784# sect 5.5.1
1785
1786
1787class KRB_AP_REQ(ASN1_Packet):
1788 ASN1_codec = ASN1_Codecs.BER
1789 ASN1_root = ASN1F_SEQUENCE(
1790 ASN1F_SEQUENCE(
1791 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA0),
1792 ASN1F_enum_INTEGER("msgType", 14, KRB_MSG_TYPES, explicit_tag=0xA1),
1793 KerberosFlags(
1794 "apOptions",
1795 "",
1796 [
1797 "reserved",
1798 "use-session-key",
1799 "mutual-required",
1800 ],
1801 explicit_tag=0xA2,
1802 ),
1803 ASN1F_PACKET("ticket", None, KRB_Ticket, explicit_tag=0xA3),
1804 ASN1F_PACKET("authenticator", None, EncryptedData, explicit_tag=0xA4),
1805 ),
1806 implicit_tag=ASN1_Class_KRB.AP_REQ,
1807 )
1808
1809
1810_PADATA_CLASSES[1] = KRB_AP_REQ
1811
1812
1813class KRB_Authenticator(ASN1_Packet):
1814 ASN1_codec = ASN1_Codecs.BER
1815 ASN1_root = ASN1F_SEQUENCE(
1816 ASN1F_SEQUENCE(
1817 ASN1F_INTEGER("authenticatorPvno", 5, explicit_tag=0xA0),
1818 Realm("crealm", "", explicit_tag=0xA1),
1819 ASN1F_PACKET("cname", None, PrincipalName, explicit_tag=0xA2),
1820 ASN1F_optional(
1821 ASN1F_PACKET("cksum", None, Checksum, explicit_tag=0xA3),
1822 ),
1823 Microseconds("cusec", 0, explicit_tag=0xA4),
1824 KerberosTime("ctime", GeneralizedTime(), explicit_tag=0xA5),
1825 ASN1F_optional(
1826 ASN1F_PACKET("subkey", None, EncryptionKey, explicit_tag=0xA6),
1827 ),
1828 ASN1F_optional(
1829 UInt32("seqNumber", 0, explicit_tag=0xA7),
1830 ),
1831 ASN1F_optional(
1832 ASN1F_PACKET(
1833 "encAuthorizationData", None, AuthorizationData, explicit_tag=0xA8
1834 ),
1835 ),
1836 ),
1837 implicit_tag=ASN1_Class_KRB.Authenticator,
1838 )
1839
1840
1841# sect 5.5.2
1842
1843
1844class KRB_AP_REP(ASN1_Packet):
1845 ASN1_codec = ASN1_Codecs.BER
1846 ASN1_root = ASN1F_SEQUENCE(
1847 ASN1F_SEQUENCE(
1848 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA0),
1849 ASN1F_enum_INTEGER("msgType", 15, KRB_MSG_TYPES, explicit_tag=0xA1),
1850 ASN1F_PACKET("encPart", None, EncryptedData, explicit_tag=0xA2),
1851 ),
1852 implicit_tag=ASN1_Class_KRB.AP_REP,
1853 )
1854
1855
1856class EncAPRepPart(ASN1_Packet):
1857 ASN1_codec = ASN1_Codecs.BER
1858 ASN1_root = ASN1F_SEQUENCE(
1859 ASN1F_SEQUENCE(
1860 KerberosTime("ctime", GeneralizedTime(), explicit_tag=0xA0),
1861 Microseconds("cusec", 0, explicit_tag=0xA1),
1862 ASN1F_optional(
1863 ASN1F_PACKET("subkey", None, EncryptionKey, explicit_tag=0xA2),
1864 ),
1865 ASN1F_optional(
1866 UInt32("seqNumber", 0, explicit_tag=0xA3),
1867 ),
1868 ),
1869 implicit_tag=ASN1_Class_KRB.EncAPRepPart,
1870 )
1871
1872
1873# sect 5.7
1874
1875
1876class KRB_PRIV(ASN1_Packet):
1877 ASN1_codec = ASN1_Codecs.BER
1878 ASN1_root = ASN1F_SEQUENCE(
1879 ASN1F_SEQUENCE(
1880 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA0),
1881 ASN1F_enum_INTEGER("msgType", 21, KRB_MSG_TYPES, explicit_tag=0xA1),
1882 ASN1F_PACKET("encPart", None, EncryptedData, explicit_tag=0xA3),
1883 ),
1884 implicit_tag=ASN1_Class_KRB.PRIV,
1885 )
1886
1887
1888class EncKrbPrivPart(ASN1_Packet):
1889 ASN1_codec = ASN1_Codecs.BER
1890 ASN1_root = ASN1F_SEQUENCE(
1891 ASN1F_SEQUENCE(
1892 ASN1F_STRING("userData", ASN1_STRING(""), explicit_tag=0xA0),
1893 ASN1F_optional(
1894 KerberosTime("timestamp", None, explicit_tag=0xA1),
1895 ),
1896 ASN1F_optional(
1897 Microseconds("usec", None, explicit_tag=0xA2),
1898 ),
1899 ASN1F_optional(
1900 UInt32("seqNumber", None, explicit_tag=0xA3),
1901 ),
1902 ASN1F_PACKET("sAddress", None, HostAddress, explicit_tag=0xA4),
1903 ASN1F_optional(
1904 ASN1F_PACKET("cAddress", None, HostAddress, explicit_tag=0xA5),
1905 ),
1906 ),
1907 implicit_tag=ASN1_Class_KRB.EncKrbPrivPart,
1908 )
1909
1910
1911# sect 5.8
1912
1913
1914class KRB_CRED(ASN1_Packet):
1915 ASN1_codec = ASN1_Codecs.BER
1916 ASN1_root = ASN1F_SEQUENCE(
1917 ASN1F_SEQUENCE(
1918 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA0),
1919 ASN1F_enum_INTEGER("msgType", 22, KRB_MSG_TYPES, explicit_tag=0xA1),
1920 ASN1F_SEQUENCE_OF("tickets", [KRB_Ticket()], KRB_Ticket, explicit_tag=0xA2),
1921 ASN1F_PACKET("encPart", None, EncryptedData, explicit_tag=0xA3),
1922 ),
1923 implicit_tag=ASN1_Class_KRB.CRED,
1924 )
1925
1926
1927class KrbCredInfo(ASN1_Packet):
1928 ASN1_codec = ASN1_Codecs.BER
1929 ASN1_root = ASN1F_SEQUENCE(
1930 ASN1F_PACKET("key", EncryptionKey(), EncryptionKey, explicit_tag=0xA0),
1931 ASN1F_optional(
1932 Realm("prealm", None, explicit_tag=0xA1),
1933 ),
1934 ASN1F_optional(
1935 ASN1F_PACKET("pname", None, PrincipalName, explicit_tag=0xA2),
1936 ),
1937 ASN1F_optional(
1938 KerberosFlags(
1939 "flags",
1940 None,
1941 _TICKET_FLAGS,
1942 explicit_tag=0xA3,
1943 ),
1944 ),
1945 ASN1F_optional(
1946 KerberosTime("authtime", None, explicit_tag=0xA4),
1947 ),
1948 ASN1F_optional(KerberosTime("starttime", None, explicit_tag=0xA5)),
1949 ASN1F_optional(
1950 KerberosTime("endtime", None, explicit_tag=0xA6),
1951 ),
1952 ASN1F_optional(
1953 KerberosTime("renewTill", None, explicit_tag=0xA7),
1954 ),
1955 ASN1F_optional(
1956 Realm("srealm", None, explicit_tag=0xA8),
1957 ),
1958 ASN1F_optional(
1959 ASN1F_PACKET("sname", None, PrincipalName, explicit_tag=0xA9),
1960 ),
1961 ASN1F_optional(
1962 HostAddresses("caddr", explicit_tag=0xAA),
1963 ),
1964 )
1965
1966
1967class EncKrbCredPart(ASN1_Packet):
1968 ASN1_codec = ASN1_Codecs.BER
1969 ASN1_root = ASN1F_SEQUENCE(
1970 ASN1F_SEQUENCE(
1971 ASN1F_SEQUENCE_OF(
1972 "ticketInfo",
1973 [KrbCredInfo()],
1974 KrbCredInfo,
1975 explicit_tag=0xA0,
1976 ),
1977 ASN1F_optional(
1978 UInt32("nonce", None, explicit_tag=0xA1),
1979 ),
1980 ASN1F_optional(
1981 KerberosTime("timestamp", None, explicit_tag=0xA2),
1982 ),
1983 ASN1F_optional(
1984 Microseconds("usec", None, explicit_tag=0xA3),
1985 ),
1986 ASN1F_optional(
1987 ASN1F_PACKET("sAddress", None, HostAddress, explicit_tag=0xA4),
1988 ),
1989 ASN1F_optional(
1990 ASN1F_PACKET("cAddress", None, HostAddress, explicit_tag=0xA5),
1991 ),
1992 ),
1993 implicit_tag=ASN1_Class_KRB.EncKrbCredPart,
1994 )
1995
1996
1997# sect 5.9.1
1998
1999
2000class MethodData(ASN1_Packet):
2001 ASN1_codec = ASN1_Codecs.BER
2002 ASN1_root = ASN1F_SEQUENCE_OF("seq", [PADATA()], PADATA)
2003
2004
2005class _KRBERROR_data_Field(ASN1F_STRING_PacketField):
2006 def m2i(self, pkt, s):
2007 val = super(_KRBERROR_data_Field, self).m2i(pkt, s)
2008 if not val[0].val:
2009 return val
2010 if pkt.errorCode.val in [14, 24, 25, 36]:
2011 # 14: KDC_ERR_ETYPE_NOSUPP
2012 # 24: KDC_ERR_PREAUTH_FAILED
2013 # 25: KDC_ERR_PREAUTH_REQUIRED
2014 # 36: KRB_AP_ERR_BADMATCH
2015 return MethodData(val[0].val, _underlayer=pkt), val[1]
2016 elif pkt.errorCode.val in [6, 7, 12, 13, 18, 29, 41, 60]:
2017 # 6: KDC_ERR_C_PRINCIPAL_UNKNOWN
2018 # 7: KDC_ERR_S_PRINCIPAL_UNKNOWN
2019 # 12: KDC_ERR_POLICY
2020 # 13: KDC_ERR_BADOPTION
2021 # 18: KDC_ERR_CLIENT_REVOKED
2022 # 29: KDC_ERR_SVC_UNAVAILABLE
2023 # 41: KRB_AP_ERR_MODIFIED
2024 # 60: KRB_ERR_GENERIC
2025 try:
2026 return KERB_ERROR_DATA(val[0].val, _underlayer=pkt), val[1]
2027 except BER_Decoding_Error:
2028 if pkt.errorCode.val in [18, 12]:
2029 # Some types can also happen in FAST sessions
2030 # 18: KDC_ERR_CLIENT_REVOKED
2031 return MethodData(val[0].val, _underlayer=pkt), val[1]
2032 elif pkt.errorCode.val == 7:
2033 # This looks like an undocumented structure.
2034 # 7: KDC_ERR_S_PRINCIPAL_UNKNOWN
2035 return KERB_ERROR_UNK(val[0].val, _underlayer=pkt), val[1]
2036 raise
2037 elif pkt.errorCode.val == 69:
2038 # KRB_AP_ERR_USER_TO_USER_REQUIRED
2039 return KRB_TGT_REP(val[0].val, _underlayer=pkt), val[1]
2040 return val
2041
2042
2043class KRB_ERROR(ASN1_Packet):
2044 ASN1_codec = ASN1_Codecs.BER
2045 ASN1_root = ASN1F_SEQUENCE(
2046 ASN1F_SEQUENCE(
2047 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA0),
2048 ASN1F_enum_INTEGER("msgType", 30, KRB_MSG_TYPES, explicit_tag=0xA1),
2049 ASN1F_optional(
2050 KerberosTime("ctime", None, explicit_tag=0xA2),
2051 ),
2052 ASN1F_optional(
2053 Microseconds("cusec", None, explicit_tag=0xA3),
2054 ),
2055 KerberosTime("stime", GeneralizedTime(), explicit_tag=0xA4),
2056 Microseconds("susec", 0, explicit_tag=0xA5),
2057 ASN1F_enum_INTEGER(
2058 "errorCode",
2059 0,
2060 {
2061 # RFC4120 sect 7.5.9
2062 0: "KDC_ERR_NONE",
2063 1: "KDC_ERR_NAME_EXP",
2064 2: "KDC_ERR_SERVICE_EXP",
2065 3: "KDC_ERR_BAD_PVNO",
2066 4: "KDC_ERR_C_OLD_MAST_KVNO",
2067 5: "KDC_ERR_S_OLD_MAST_KVNO",
2068 6: "KDC_ERR_C_PRINCIPAL_UNKNOWN",
2069 7: "KDC_ERR_S_PRINCIPAL_UNKNOWN",
2070 8: "KDC_ERR_PRINCIPAL_NOT_UNIQUE",
2071 9: "KDC_ERR_NULL_KEY",
2072 10: "KDC_ERR_CANNOT_POSTDATE",
2073 11: "KDC_ERR_NEVER_VALID",
2074 12: "KDC_ERR_POLICY",
2075 13: "KDC_ERR_BADOPTION",
2076 14: "KDC_ERR_ETYPE_NOSUPP",
2077 15: "KDC_ERR_SUMTYPE_NOSUPP",
2078 16: "KDC_ERR_PADATA_TYPE_NOSUPP",
2079 17: "KDC_ERR_TRTYPE_NOSUPP",
2080 18: "KDC_ERR_CLIENT_REVOKED",
2081 19: "KDC_ERR_SERVICE_REVOKED",
2082 20: "KDC_ERR_TGT_REVOKED",
2083 21: "KDC_ERR_CLIENT_NOTYET",
2084 22: "KDC_ERR_SERVICE_NOTYET",
2085 23: "KDC_ERR_KEY_EXPIRED",
2086 24: "KDC_ERR_PREAUTH_FAILED",
2087 25: "KDC_ERR_PREAUTH_REQUIRED",
2088 26: "KDC_ERR_SERVER_NOMATCH",
2089 27: "KDC_ERR_MUST_USE_USER2USER",
2090 28: "KDC_ERR_PATH_NOT_ACCEPTED",
2091 29: "KDC_ERR_SVC_UNAVAILABLE",
2092 31: "KRB_AP_ERR_BAD_INTEGRITY",
2093 32: "KRB_AP_ERR_TKT_EXPIRED",
2094 33: "KRB_AP_ERR_TKT_NYV",
2095 34: "KRB_AP_ERR_REPEAT",
2096 35: "KRB_AP_ERR_NOT_US",
2097 36: "KRB_AP_ERR_BADMATCH",
2098 37: "KRB_AP_ERR_SKEW",
2099 38: "KRB_AP_ERR_BADADDR",
2100 39: "KRB_AP_ERR_BADVERSION",
2101 40: "KRB_AP_ERR_MSG_TYPE",
2102 41: "KRB_AP_ERR_MODIFIED",
2103 42: "KRB_AP_ERR_BADORDER",
2104 44: "KRB_AP_ERR_BADKEYVER",
2105 45: "KRB_AP_ERR_NOKEY",
2106 46: "KRB_AP_ERR_MUT_FAIL",
2107 47: "KRB_AP_ERR_BADDIRECTION",
2108 48: "KRB_AP_ERR_METHOD",
2109 49: "KRB_AP_ERR_BADSEQ",
2110 50: "KRB_AP_ERR_INAPP_CKSUM",
2111 51: "KRB_AP_PATH_NOT_ACCEPTED",
2112 52: "KRB_ERR_RESPONSE_TOO_BIG",
2113 60: "KRB_ERR_GENERIC",
2114 61: "KRB_ERR_FIELD_TOOLONG",
2115 62: "KDC_ERROR_CLIENT_NOT_TRUSTED",
2116 63: "KDC_ERROR_KDC_NOT_TRUSTED",
2117 64: "KDC_ERROR_INVALID_SIG",
2118 65: "KDC_ERR_KEY_TOO_WEAK",
2119 66: "KDC_ERR_CERTIFICATE_MISMATCH",
2120 67: "KRB_AP_ERR_NO_TGT",
2121 68: "KDC_ERR_WRONG_REALM",
2122 69: "KRB_AP_ERR_USER_TO_USER_REQUIRED",
2123 70: "KDC_ERR_CANT_VERIFY_CERTIFICATE",
2124 71: "KDC_ERR_INVALID_CERTIFICATE",
2125 72: "KDC_ERR_REVOKED_CERTIFICATE",
2126 73: "KDC_ERR_REVOCATION_STATUS_UNKNOWN",
2127 74: "KDC_ERR_REVOCATION_STATUS_UNAVAILABLE",
2128 75: "KDC_ERR_CLIENT_NAME_MISMATCH",
2129 76: "KDC_ERR_KDC_NAME_MISMATCH",
2130 # draft-ietf-kitten-iakerb
2131 85: "KRB_AP_ERR_IAKERB_KDC_NOT_FOUND",
2132 86: "KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE",
2133 # RFC6113
2134 90: "KDC_ERR_PREAUTH_EXPIRED",
2135 91: "KDC_ERR_MORE_PREAUTH_DATA_REQUIRED",
2136 92: "KDC_ERR_PREAUTH_BAD_AUTHENTICATION_SET",
2137 93: "KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS",
2138 # RFC8636
2139 100: "KDC_ERR_NO_ACCEPTABLE_KDF",
2140 },
2141 explicit_tag=0xA6,
2142 ),
2143 ASN1F_optional(Realm("crealm", None, explicit_tag=0xA7)),
2144 ASN1F_optional(
2145 ASN1F_PACKET("cname", None, PrincipalName, explicit_tag=0xA8),
2146 ),
2147 Realm("realm", "", explicit_tag=0xA9),
2148 ASN1F_PACKET("sname", PrincipalName(), PrincipalName, explicit_tag=0xAA),
2149 ASN1F_optional(KerberosString("eText", "", explicit_tag=0xAB)),
2150 ASN1F_optional(_KRBERROR_data_Field("eData", "", explicit_tag=0xAC)),
2151 ),
2152 implicit_tag=ASN1_Class_KRB.ERROR,
2153 )
2154
2155 def getSPN(self):
2156 return "%s@%s" % (
2157 self.sname.toString(),
2158 self.realm.val.decode(),
2159 )
2160
2161
2162# PA-FX-ERROR
2163_PADATA_CLASSES[137] = KRB_ERROR
2164
2165
2166# [MS-KILE] sect 2.2.1
2167
2168
2169class KERB_EXT_ERROR(Packet):
2170 fields_desc = [
2171 XLEIntEnumField("status", 0, STATUS_ERREF),
2172 XLEIntField("reserved", 0),
2173 XLEIntField("flags", 0x00000001),
2174 ]
2175
2176
2177# [MS-KILE] sect 2.2.2
2178
2179
2180class _Error_Field(ASN1F_STRING_PacketField):
2181 def m2i(self, pkt, s):
2182 val = super(_Error_Field, self).m2i(pkt, s)
2183 if not val[0].val:
2184 return val
2185 if pkt.dataType.val == 3: # KERB_ERR_TYPE_EXTENDED
2186 return KERB_EXT_ERROR(val[0].val, _underlayer=pkt), val[1]
2187 return val
2188
2189
2190class KERB_ERROR_DATA(ASN1_Packet):
2191 ASN1_codec = ASN1_Codecs.BER
2192 ASN1_root = ASN1F_SEQUENCE(
2193 ASN1F_enum_INTEGER(
2194 "dataType",
2195 2,
2196 {
2197 1: "KERB_AP_ERR_TYPE_NTSTATUS", # from the wdk
2198 2: "KERB_AP_ERR_TYPE_SKEW_RECOVERY",
2199 3: "KERB_ERR_TYPE_EXTENDED",
2200 },
2201 explicit_tag=0xA1,
2202 ),
2203 ASN1F_optional(_Error_Field("dataValue", None, explicit_tag=0xA2)),
2204 )
2205
2206
2207# This looks like an undocumented structure.
2208
2209
2210class KERB_ERROR_UNK(ASN1_Packet):
2211 ASN1_codec = ASN1_Codecs.BER
2212 ASN1_root = ASN1F_SEQUENCE(
2213 ASN1F_SEQUENCE(
2214 ASN1F_enum_INTEGER(
2215 "dataType",
2216 0,
2217 {
2218 -128: "KDC_ERR_MUST_USE_USER2USER",
2219 },
2220 explicit_tag=0xA0,
2221 ),
2222 ASN1F_STRING("dataValue", None, explicit_tag=0xA1),
2223 )
2224 )
2225
2226
2227# Kerberos U2U - draft-ietf-cat-user2user-03
2228
2229
2230class KRB_TGT_REQ(ASN1_Packet):
2231 ASN1_codec = ASN1_Codecs.BER
2232 ASN1_root = ASN1F_SEQUENCE(
2233 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA0),
2234 ASN1F_enum_INTEGER("msgType", 16, KRB_MSG_TYPES, explicit_tag=0xA1),
2235 ASN1F_optional(
2236 ASN1F_PACKET("sname", None, PrincipalName, explicit_tag=0xA2),
2237 ),
2238 ASN1F_optional(
2239 Realm("realm", None, explicit_tag=0xA3),
2240 ),
2241 )
2242
2243
2244class KRB_TGT_REP(ASN1_Packet):
2245 ASN1_codec = ASN1_Codecs.BER
2246 ASN1_root = ASN1F_SEQUENCE(
2247 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA0),
2248 ASN1F_enum_INTEGER("msgType", 17, KRB_MSG_TYPES, explicit_tag=0xA1),
2249 ASN1F_PACKET("ticket", None, KRB_Ticket, explicit_tag=0xA2),
2250 )
2251
2252
2253# draft-ietf-kitten-iakerb-03 sect 4
2254
2255
2256class KRB_FINISHED(ASN1_Packet):
2257 ASN1_codec = ASN1_Codecs.BER
2258 ASN1_root = ASN1F_SEQUENCE(
2259 ASN1F_PACKET("gssMic", Checksum(), Checksum, explicit_tag=0xA1),
2260 )
2261
2262
2263# RFC 6542 sect 3.1
2264
2265
2266class KRB_GSS_EXT(Packet):
2267 fields_desc = [
2268 IntEnumField(
2269 "type",
2270 0,
2271 {
2272 # https://www.iana.org/assignments/kerberos-v-gss-api/kerberos-v-gss-api.xhtml
2273 0x00000000: "GSS_EXTS_CHANNEL_BINDING", # RFC 6542 sect 3.2
2274 0x00000001: "GSS_EXTS_IAKERB_FINISHED", # not standard
2275 0x00000002: "GSS_EXTS_FINISHED", # PKU2U / IAKERB
2276 },
2277 ),
2278 FieldLenField("length", None, length_of="data", fmt="!I"),
2279 MultipleTypeField(
2280 [
2281 (
2282 PacketField("data", KRB_FINISHED(), KRB_FINISHED),
2283 lambda pkt: pkt.type == 0x00000002,
2284 ),
2285 ],
2286 XStrLenField("data", b"", length_from=lambda pkt: pkt.length),
2287 ),
2288 ]
2289
2290
2291# RFC 4121 sect 4.1.1
2292
2293
2294class KRB_AuthenticatorChecksum(Packet):
2295 fields_desc = [
2296 FieldLenField("Lgth", None, length_of="Bnd", fmt="<I"),
2297 XStrLenField("Bnd", b"\x00" * 16, length_from=lambda pkt: pkt.Lgth),
2298 FlagsField(
2299 "Flags",
2300 0,
2301 -32,
2302 {
2303 0x01: "GSS_C_DELEG_FLAG",
2304 0x02: "GSS_C_MUTUAL_FLAG",
2305 0x04: "GSS_C_REPLAY_FLAG",
2306 0x08: "GSS_C_SEQUENCE_FLAG",
2307 0x10: "GSS_C_CONF_FLAG", # confidentiality
2308 0x20: "GSS_C_INTEG_FLAG", # integrity
2309 # RFC4757
2310 0x1000: "GSS_C_DCE_STYLE",
2311 0x2000: "GSS_C_IDENTIFY_FLAG",
2312 0x4000: "GSS_C_EXTENDED_ERROR_FLAG",
2313 },
2314 ),
2315 ConditionalField(
2316 LEShortField("DlgOpt", 0),
2317 lambda pkt: pkt.Flags.GSS_C_DELEG_FLAG,
2318 ),
2319 ConditionalField(
2320 FieldLenField("Dlgth", None, length_of="Deleg"),
2321 lambda pkt: pkt.Flags.GSS_C_DELEG_FLAG,
2322 ),
2323 ConditionalField(
2324 PacketLenField(
2325 "Deleg", KRB_CRED(), KRB_CRED, length_from=lambda pkt: pkt.Dlgth
2326 ),
2327 lambda pkt: pkt.Flags.GSS_C_DELEG_FLAG,
2328 ),
2329 # Extensions: RFC 6542 sect 3.1
2330 PacketListField("Exts", KRB_GSS_EXT(), KRB_GSS_EXT),
2331 ]
2332
2333
2334# Kerberos V5 GSS-API - RFC1964 and RFC4121
2335
2336_TOK_IDS = {
2337 # RFC 1964
2338 b"\x01\x00": "KRB-AP-REQ",
2339 b"\x02\x00": "KRB-AP-REP",
2340 b"\x03\x00": "KRB-ERROR",
2341 b"\x01\x01": "GSS_GetMIC-RFC1964",
2342 b"\x02\x01": "GSS_Wrap-RFC1964",
2343 b"\x01\x02": "GSS_Delete_sec_context-RFC1964",
2344 # U2U: [draft-ietf-cat-user2user-03]
2345 b"\x04\x00": "KRB-TGT-REQ",
2346 b"\x04\x01": "KRB-TGT-REP",
2347 # RFC 4121
2348 b"\x04\x04": "GSS_GetMIC",
2349 b"\x05\x04": "GSS_Wrap",
2350 # IAKERB: [draft-ietf-kitten-iakerb-03]
2351 b"\x05\x01": "IAKERB_PROXY",
2352}
2353_SGN_ALGS = {
2354 0x00: "DES MAC MD5",
2355 0x01: "MD2.5",
2356 0x02: "DES MAC",
2357 # RFC 4757
2358 0x11: "HMAC",
2359}
2360_SEAL_ALGS = {
2361 0: "DES",
2362 0xFFFF: "none",
2363 # RFC 4757
2364 0x10: "RC4",
2365}
2366
2367
2368# RFC 1964 - sect 1.1
2369
2370# See https://www.iana.org/assignments/kerberos-v-gss-api/kerberos-v-gss-api.xhtml
2371_InitialContextTokens = {} # filled below
2372
2373
2374class KRB_InnerToken(Packet):
2375 name = "Kerberos v5 InnerToken"
2376 fields_desc = [
2377 StrFixedLenEnumField("TOK_ID", b"\x01\x00", _TOK_IDS, length=2),
2378 PacketField(
2379 "root",
2380 KRB_AP_REQ(),
2381 lambda x, _parent: _InitialContextTokens[_parent.TOK_ID](x),
2382 ),
2383 ]
2384
2385 def mysummary(self):
2386 return self.sprintf(
2387 "Kerberos %s" % _TOK_IDS.get(self.TOK_ID, repr(self.TOK_ID))
2388 )
2389
2390 def guess_payload_class(self, payload):
2391 if self.TOK_ID in [b"\x01\x01", b"\x02\x01", b"\x04\x04", b"\x05\x04"]:
2392 return conf.padding_layer
2393 return Kerberos
2394
2395 @classmethod
2396 def dispatch_hook(cls, _pkt=None, *args, **kargs):
2397 if _pkt and len(_pkt) >= 13:
2398 # Older RFC1964 variants of the token have KRB_GSSAPI_Token wrapper
2399 if _pkt[2:13] == b"\x06\t*\x86H\x86\xf7\x12\x01\x02\x02":
2400 return KRB_GSSAPI_Token
2401 return cls
2402
2403
2404# RFC 4121 - sect 4.1
2405
2406
2407class KRB_GSSAPI_Token(GSSAPI_BLOB):
2408 name = "Kerberos GSSAPI-Token"
2409 ASN1_codec = ASN1_Codecs.BER
2410 ASN1_root = ASN1F_SEQUENCE(
2411 ASN1F_OID("MechType", "1.2.840.113554.1.2.2"),
2412 ASN1F_PACKET(
2413 "innerToken",
2414 KRB_InnerToken(),
2415 KRB_InnerToken,
2416 implicit_tag=0x0,
2417 ),
2418 implicit_tag=ASN1_Class_KRB.Token,
2419 )
2420
2421
2422# RFC 1964 - sect 1.2.1
2423
2424
2425class KRB_GSS_MIC_RFC1964(Packet):
2426 name = "Kerberos v5 MIC Token (RFC1964)"
2427 fields_desc = [
2428 LEShortEnumField("SGN_ALG", 0, _SGN_ALGS),
2429 XLEIntField("Filler", 0xFFFFFFFF),
2430 XStrFixedLenField("SND_SEQ", b"", length=8),
2431 PadField( # sect 1.2.2.3
2432 XStrFixedLenField("SGN_CKSUM", b"", length=8),
2433 align=8,
2434 padwith=b"\x04",
2435 ),
2436 ]
2437
2438 def default_payload_class(self, payload):
2439 return conf.padding_layer
2440
2441
2442_InitialContextTokens[b"\x01\x01"] = KRB_GSS_MIC_RFC1964
2443
2444# RFC 1964 - sect 1.2.2
2445
2446
2447class KRB_GSS_Wrap_RFC1964(Packet):
2448 name = "Kerberos v5 GSS_Wrap (RFC1964)"
2449 fields_desc = [
2450 LEShortEnumField("SGN_ALG", 0, _SGN_ALGS),
2451 LEShortEnumField("SEAL_ALG", 0, _SEAL_ALGS),
2452 XLEShortField("Filler", 0xFFFF),
2453 XStrFixedLenField("SND_SEQ", b"", length=8),
2454 PadField( # sect 1.2.2.3
2455 XStrFixedLenField("SGN_CKSUM", b"", length=8),
2456 align=8,
2457 padwith=b"\x04",
2458 ),
2459 # sect 1.2.2.3
2460 XStrFixedLenField("CONFOUNDER", b"", length=8),
2461 ]
2462
2463 def default_payload_class(self, payload):
2464 return conf.padding_layer
2465
2466
2467_InitialContextTokens[b"\x02\x01"] = KRB_GSS_Wrap_RFC1964
2468
2469
2470# RFC 1964 - sect 1.2.2
2471
2472
2473class KRB_GSS_Delete_sec_context_RFC1964(Packet):
2474 name = "Kerberos v5 GSS_Delete_sec_context (RFC1964)"
2475 fields_desc = KRB_GSS_MIC_RFC1964.fields_desc
2476
2477
2478_InitialContextTokens[b"\x01\x02"] = KRB_GSS_Delete_sec_context_RFC1964
2479
2480
2481# RFC 4121 - sect 4.2.2
2482_KRB5_GSS_Flags = [
2483 "SentByAcceptor",
2484 "Sealed",
2485 "AcceptorSubkey",
2486]
2487
2488
2489# RFC 4121 - sect 4.2.6.1
2490
2491
2492class KRB_GSS_MIC(Packet):
2493 name = "Kerberos v5 MIC Token"
2494 fields_desc = [
2495 FlagsField("Flags", 0, 8, _KRB5_GSS_Flags),
2496 XStrFixedLenField("Filler", b"\xff\xff\xff\xff\xff", length=5),
2497 LongField("SND_SEQ", 0), # Big endian
2498 XStrField("SGN_CKSUM", b"\x00" * 12),
2499 ]
2500
2501 def default_payload_class(self, payload):
2502 return conf.padding_layer
2503
2504
2505_InitialContextTokens[b"\x04\x04"] = KRB_GSS_MIC
2506
2507
2508# RFC 4121 - sect 4.2.6.2
2509
2510
2511class KRB_GSS_Wrap(Packet):
2512 name = "Kerberos v5 Wrap Token"
2513 fields_desc = [
2514 FlagsField("Flags", 0, 8, _KRB5_GSS_Flags),
2515 XByteField("Filler", 0xFF),
2516 ShortField("EC", 0), # Big endian
2517 ShortField("RRC", 0), # Big endian
2518 LongField("SND_SEQ", 0), # Big endian
2519 MultipleTypeField(
2520 [
2521 (
2522 XStrField("Data", b""),
2523 lambda pkt: pkt.Flags.Sealed,
2524 )
2525 ],
2526 XStrLenField("Data", b"", length_from=lambda pkt: pkt.EC),
2527 ),
2528 ]
2529
2530 def default_payload_class(self, payload):
2531 return conf.padding_layer
2532
2533
2534_InitialContextTokens[b"\x05\x04"] = KRB_GSS_Wrap
2535
2536
2537# Kerberos IAKERB - draft-ietf-kitten-iakerb-03
2538
2539
2540class IAKERB_HEADER(ASN1_Packet):
2541 ASN1_codec = ASN1_Codecs.BER
2542 ASN1_root = ASN1F_SEQUENCE(
2543 Realm("targetRealm", "", explicit_tag=0xA1),
2544 ASN1F_optional(
2545 ASN1F_STRING("cookie", None, explicit_tag=0xA2),
2546 ),
2547 )
2548
2549
2550_InitialContextTokens[b"\x05\x01"] = IAKERB_HEADER
2551
2552
2553# Register for GSSAPI
2554
2555# Kerberos 5
2556_GSSAPI_OIDS["1.2.840.113554.1.2.2"] = KRB_InnerToken
2557_GSSAPI_SIGNATURE_OIDS["1.2.840.113554.1.2.2"] = KRB_InnerToken
2558# Kerberos 5 - U2U
2559_GSSAPI_OIDS["1.2.840.113554.1.2.2.3"] = KRB_InnerToken
2560# Kerberos 5 - IAKERB
2561_GSSAPI_OIDS["1.3.6.1.5.2.5"] = KRB_InnerToken
2562
2563
2564# Entry class
2565
2566# RFC4120 sect 5.10
2567
2568
2569class Kerberos(ASN1_Packet):
2570 ASN1_codec = ASN1_Codecs.BER
2571 ASN1_root = ASN1F_CHOICE(
2572 "root",
2573 None,
2574 # RFC4120
2575 KRB_GSSAPI_Token, # [APPLICATION 0]
2576 KRB_Ticket, # [APPLICATION 1]
2577 KRB_Authenticator, # [APPLICATION 2]
2578 KRB_AS_REQ, # [APPLICATION 10]
2579 KRB_AS_REP, # [APPLICATION 11]
2580 KRB_TGS_REQ, # [APPLICATION 12]
2581 KRB_TGS_REP, # [APPLICATION 13]
2582 KRB_AP_REQ, # [APPLICATION 14]
2583 KRB_AP_REP, # [APPLICATION 15]
2584 # RFC4120
2585 KRB_ERROR, # [APPLICATION 30]
2586 )
2587
2588 def mysummary(self):
2589 return self.root.summary()
2590
2591
2592bind_bottom_up(UDP, Kerberos, sport=88)
2593bind_bottom_up(UDP, Kerberos, dport=88)
2594bind_layers(UDP, Kerberos, sport=88, dport=88)
2595
2596_InitialContextTokens[b"\x01\x00"] = KRB_AP_REQ
2597_InitialContextTokens[b"\x02\x00"] = KRB_AP_REP
2598_InitialContextTokens[b"\x03\x00"] = KRB_ERROR
2599_InitialContextTokens[b"\x04\x00"] = KRB_TGT_REQ
2600_InitialContextTokens[b"\x04\x01"] = KRB_TGT_REP
2601
2602
2603# RFC4120 sect 7.2.2
2604
2605
2606class KerberosTCPHeader(Packet):
2607 # According to RFC 5021, first bit to 1 has a special meaning and
2608 # negotiates Kerberos TCP extensions... But apart from rfc6251 no one used that
2609 # https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-4
2610 fields_desc = [LenField("len", None, fmt="!I")]
2611
2612 @classmethod
2613 def tcp_reassemble(cls, data, *args, **kwargs):
2614 if len(data) < 4:
2615 return None
2616 length = struct.unpack("!I", data[:4])[0]
2617 if len(data) == length + 4:
2618 return cls(data)
2619
2620
2621bind_layers(KerberosTCPHeader, Kerberos)
2622
2623bind_bottom_up(TCP, KerberosTCPHeader, sport=88)
2624bind_layers(TCP, KerberosTCPHeader, dport=88)
2625
2626
2627# RFC3244 sect 2
2628
2629
2630class KPASSWD_REQ(Packet):
2631 fields_desc = [
2632 ShortField("len", None),
2633 ShortField("pvno", 0xFF80),
2634 ShortField("apreqlen", None),
2635 PacketLenField(
2636 "apreq", KRB_AP_REQ(), KRB_AP_REQ, length_from=lambda pkt: pkt.apreqlen
2637 ),
2638 ConditionalField(
2639 PacketLenField(
2640 "krbpriv",
2641 KRB_PRIV(),
2642 KRB_PRIV,
2643 length_from=lambda pkt: pkt.len - 6 - pkt.apreqlen,
2644 ),
2645 lambda pkt: pkt.apreqlen != 0,
2646 ),
2647 ConditionalField(
2648 PacketLenField(
2649 "error", KRB_ERROR(), KRB_ERROR, length_from=lambda pkt: pkt.len - 6
2650 ),
2651 lambda pkt: pkt.apreqlen == 0,
2652 ),
2653 ]
2654
2655 def post_build(self, p, pay):
2656 if self.len is None:
2657 p = struct.pack("!H", len(p)) + p[2:]
2658 if self.apreqlen is None and self.krbpriv is not None:
2659 p = p[:4] + struct.pack("!H", len(self.apreq)) + p[6:]
2660 return p + pay
2661
2662
2663class ChangePasswdData(ASN1_Packet):
2664 ASN1_codec = ASN1_Codecs.BER
2665 ASN1_root = ASN1F_SEQUENCE(
2666 ASN1F_STRING("newpasswd", ASN1_STRING(""), explicit_tag=0xA0),
2667 ASN1F_optional(
2668 ASN1F_PACKET("targname", None, PrincipalName, explicit_tag=0xA1)
2669 ),
2670 ASN1F_optional(Realm("targrealm", None, explicit_tag=0xA2)),
2671 )
2672
2673
2674class KPASSWD_REP(Packet):
2675 fields_desc = [
2676 ShortField("len", None),
2677 ShortField("pvno", 0x0001),
2678 ShortField("apreplen", None),
2679 PacketLenField(
2680 "aprep", KRB_AP_REP(), KRB_AP_REP, length_from=lambda pkt: pkt.apreplen
2681 ),
2682 ConditionalField(
2683 PacketLenField(
2684 "krbpriv",
2685 KRB_PRIV(),
2686 KRB_PRIV,
2687 length_from=lambda pkt: pkt.len - 6 - pkt.apreplen,
2688 ),
2689 lambda pkt: pkt.apreplen != 0,
2690 ),
2691 ConditionalField(
2692 PacketLenField(
2693 "error", KRB_ERROR(), KRB_ERROR, length_from=lambda pkt: pkt.len - 6
2694 ),
2695 lambda pkt: pkt.apreplen == 0,
2696 ),
2697 ]
2698
2699 def post_build(self, p, pay):
2700 if self.len is None:
2701 p = struct.pack("!H", len(p)) + p[2:]
2702 if self.apreplen is None and self.krbpriv is not None:
2703 p = p[:4] + struct.pack("!H", len(self.aprep)) + p[6:]
2704 return p + pay
2705
2706 def answers(self, other):
2707 return isinstance(other, KPASSWD_REQ)
2708
2709
2710KPASSWD_RESULTS = {
2711 0: "KRB5_KPASSWD_SUCCESS",
2712 1: "KRB5_KPASSWD_MALFORMED",
2713 2: "KRB5_KPASSWD_HARDERROR",
2714 3: "KRB5_KPASSWD_AUTHERROR",
2715 4: "KRB5_KPASSWD_SOFTERROR",
2716 5: "KRB5_KPASSWD_ACCESSDENIED",
2717 6: "KRB5_KPASSWD_BAD_VERSION",
2718 7: "KRB5_KPASSWD_INITIAL_FLAG_NEEDED",
2719}
2720
2721
2722class KPasswdRepData(Packet):
2723 fields_desc = [
2724 ShortEnumField("resultCode", 0, KPASSWD_RESULTS),
2725 StrField("resultString", ""),
2726 ]
2727
2728
2729class Kpasswd(Packet):
2730 @classmethod
2731 def dispatch_hook(cls, _pkt=None, *args, **kargs):
2732 if _pkt and len(_pkt) >= 4:
2733 if _pkt[2:4] == b"\xff\x80":
2734 return KPASSWD_REQ
2735 elif _pkt[2:4] == b"\x00\x01":
2736 asn1_tag = BER_id_dec(_pkt[6:8])[0] & 0x1F
2737 if asn1_tag == 14:
2738 return KPASSWD_REQ
2739 elif asn1_tag == 15:
2740 return KPASSWD_REP
2741 return KPASSWD_REQ
2742
2743
2744bind_bottom_up(UDP, Kpasswd, sport=464)
2745bind_bottom_up(UDP, Kpasswd, dport=464)
2746bind_top_down(UDP, KPASSWD_REQ, sport=464, dport=464)
2747bind_top_down(UDP, KPASSWD_REP, sport=464, dport=464)
2748
2749
2750class KpasswdTCPHeader(Packet):
2751 fields_desc = [LenField("len", None, fmt="!I")]
2752
2753 @classmethod
2754 def tcp_reassemble(cls, data, *args, **kwargs):
2755 if len(data) < 4:
2756 return None
2757 length = struct.unpack("!I", data[:4])[0]
2758 if len(data) == length + 4:
2759 return cls(data)
2760
2761
2762bind_layers(KpasswdTCPHeader, Kpasswd)
2763
2764bind_bottom_up(TCP, KpasswdTCPHeader, sport=464)
2765bind_layers(TCP, KpasswdTCPHeader, dport=464)
2766
2767# [MS-KKDCP]
2768
2769
2770class _KerbMessage_Field(ASN1F_STRING_PacketField):
2771 def m2i(self, pkt, s):
2772 val = super(_KerbMessage_Field, self).m2i(pkt, s)
2773 if not val[0].val:
2774 return val
2775 return KerberosTCPHeader(val[0].val, _underlayer=pkt), val[1]
2776
2777
2778class KDC_PROXY_MESSAGE(ASN1_Packet):
2779 ASN1_codec = ASN1_Codecs.BER
2780 ASN1_root = ASN1F_SEQUENCE(
2781 _KerbMessage_Field("kerbMessage", "", explicit_tag=0xA0),
2782 ASN1F_optional(Realm("targetDomain", None, explicit_tag=0xA1)),
2783 ASN1F_optional(
2784 ASN1F_FLAGS(
2785 "dclocatorHint",
2786 None,
2787 FlagsField("", 0, -32, _NV_VERSION).names,
2788 explicit_tag=0xA2,
2789 )
2790 ),
2791 )
2792
2793
2794class KdcProxySocket(SuperSocket):
2795 """
2796 This is a wrapper of a HTTP_Client that does KKDCP proxying,
2797 disguised as a SuperSocket to be compatible with the rest of the KerberosClient.
2798 """
2799
2800 def __init__(
2801 self,
2802 url,
2803 targetDomain,
2804 dclocatorHint=None,
2805 no_check_certificate=False,
2806 **kwargs,
2807 ):
2808 self.url = url
2809 self.targetDomain = targetDomain
2810 self.dclocatorHint = dclocatorHint
2811 self.no_check_certificate = no_check_certificate
2812 self.queue = deque()
2813 super(KdcProxySocket, self).__init__(**kwargs)
2814
2815 def recv(self, x=None):
2816 return self.queue.popleft()
2817
2818 def send(self, x, **kwargs):
2819 from scapy.layers.http import HTTP_Client
2820
2821 cli = HTTP_Client(no_check_certificate=self.no_check_certificate)
2822 try:
2823 # sr it via the web client
2824 resp = cli.request(
2825 self.url,
2826 Method="POST",
2827 data=bytes(
2828 # Wrap request in KDC_PROXY_MESSAGE
2829 KDC_PROXY_MESSAGE(
2830 kerbMessage=bytes(x),
2831 targetDomain=ASN1_GENERAL_STRING(self.targetDomain.encode()),
2832 # dclocatorHint is optional
2833 dclocatorHint=self.dclocatorHint,
2834 )
2835 ),
2836 http_headers={
2837 "Cache-Control": "no-cache",
2838 "Pragma": "no-cache",
2839 "User-Agent": "kerberos/1.0",
2840 },
2841 )
2842 if resp and conf.raw_layer in resp:
2843 # Parse the payload
2844 resp = KDC_PROXY_MESSAGE(resp.load).kerbMessage
2845 # We have an answer, queue it.
2846 self.queue.append(resp)
2847 else:
2848 raise EOFError
2849 finally:
2850 cli.close()
2851
2852 @staticmethod
2853 def select(sockets, remain=None):
2854 return [x for x in sockets if isinstance(x, KdcProxySocket) and x.queue]
2855
2856
2857# Util functions
2858
2859
2860class KerberosClient(Automaton):
2861 """
2862 Implementation of a Kerberos client.
2863
2864 Prefer to use the ``krb_as_req`` and ``krb_tgs_req`` functions which
2865 wrap this client.
2866
2867 Common parameters:
2868
2869 :param mode: the mode to use for the client (default: AS_REQ).
2870 :param ip: the IP of the DC (default: discovered by dclocator)
2871 :param upn: the UPN of the client.
2872 :param password: the password of the client.
2873 :param key: the Key of the client (instead of the password)
2874 :param realm: the realm of the domain. (default: from the UPN)
2875 :param host: the name of the host doing the request
2876 :param port: the Kerberos port (default 88)
2877 :param timeout: timeout of each request (default 5)
2878
2879 Advanced common parameters:
2880
2881 :param kdc_proxy: specify a KDC proxy url
2882 :param kdc_proxy_no_check_certificate: do not check the KDC proxy certificate
2883 :param fast: use FAST armoring
2884 :param armor_ticket: an external ticket to use for armoring
2885 :param armor_ticket_upn: the UPN of the client of the armoring ticket
2886 :param armor_ticket_skey: the session Key object of the armoring ticket
2887 :param etypes: specify the list of encryption types to support
2888
2889 AS-REQ only:
2890
2891 :param x509: a X509 certificate to use for PKINIT AS_REQ or S4U2Proxy
2892 :param x509key: the private key of the X509 certificate (in an AS_REQ)
2893 :param p12: (optional) use a pfx/p12 instead of x509 and x509key. In this case,
2894 'password' is the password of the p12.
2895
2896 TGS-REQ only:
2897
2898 :param spn: the SPN to request in a TGS-REQ
2899 :param ticket: the existing ticket to use in a TGS-REQ
2900 :param renew: sets the Renew flag in a TGS-REQ
2901 :param additional_tickets: in U2U or S4U2Proxy, the additional tickets
2902 :param u2u: sets the U2U flag
2903 :param for_user: the UPN of another user in TGS-REQ, to do a S4U2Self
2904 :param s4u2proxy: sets the S4U2Proxy flag
2905 :param dmsa: sets the 'unconditional delegation' mode for DMSA TGT retrieval
2906 """
2907
2908 RES_AS_MODE = namedtuple("AS_Result", ["asrep", "sessionkey", "kdcrep"])
2909 RES_TGS_MODE = namedtuple("TGS_Result", ["tgsrep", "sessionkey", "kdcrep"])
2910
2911 class MODE(IntEnum):
2912 AS_REQ = 0
2913 TGS_REQ = 1
2914 GET_SALT = 2
2915
2916 def __init__(
2917 self,
2918 mode=MODE.AS_REQ,
2919 ip=None,
2920 upn=None,
2921 password=None,
2922 key=None,
2923 realm=None,
2924 x509=None,
2925 x509key=None,
2926 p12=None,
2927 spn=None,
2928 ticket=None,
2929 host=None,
2930 renew=False,
2931 additional_tickets=[],
2932 u2u=False,
2933 for_user=None,
2934 s4u2proxy=False,
2935 dmsa=False,
2936 kdc_proxy=None,
2937 kdc_proxy_no_check_certificate=False,
2938 fast=False,
2939 armor_ticket=None,
2940 armor_ticket_upn=None,
2941 armor_ticket_skey=None,
2942 key_list_req=[],
2943 etypes=None,
2944 port=88,
2945 timeout=5,
2946 **kwargs,
2947 ):
2948 import scapy.libs.rfc3961 # Trigger error if any # noqa: F401
2949 from scapy.layers.ldap import dclocator
2950
2951 if not upn:
2952 raise ValueError("Invalid upn")
2953 if not spn:
2954 raise ValueError("Invalid spn")
2955 if realm is None:
2956 if mode in [self.MODE.AS_REQ, self.MODE.GET_SALT]:
2957 _, realm = _parse_upn(upn)
2958 elif mode == self.MODE.TGS_REQ:
2959 _, realm = _parse_spn(spn)
2960 if not realm and ticket:
2961 # if no realm is specified, but there's a ticket, take the realm
2962 # of the ticket.
2963 realm = ticket.realm.val.decode()
2964 else:
2965 raise ValueError("Invalid realm")
2966
2967 # PKINIT checks
2968 if p12 is not None:
2969 from cryptography.hazmat.primitives.serialization import pkcs12
2970
2971 # password should be None or bytes
2972 if isinstance(password, str):
2973 password = password.encode()
2974
2975 # Read p12/pfx
2976 with open(p12, "rb") as fd:
2977 x509key, x509, _ = pkcs12.load_key_and_certificates(
2978 fd.read(),
2979 password=password,
2980 )
2981 x509 = Cert(cryptography_obj=x509)
2982 x509key = PrivKey(cryptography_obj=x509key)
2983 elif x509 and x509key:
2984 x509 = Cert(x509)
2985 x509key = PrivKey(x509key)
2986
2987 if mode in [self.MODE.AS_REQ, self.MODE.GET_SALT]:
2988 if not host:
2989 raise ValueError("Invalid host")
2990 if (x509 is None) ^ (x509key is None):
2991 raise ValueError("Must provide both 'x509' and 'x509key' !")
2992 elif mode == self.MODE.TGS_REQ:
2993 if not ticket:
2994 raise ValueError("Invalid ticket")
2995
2996 if not ip and not kdc_proxy:
2997 # No KDC IP provided. Find it by querying the DNS
2998 ip = dclocator(
2999 realm,
3000 timeout=timeout,
3001 # Use connect mode instead of ldap for compatibility
3002 # with MIT kerberos servers
3003 mode="connect",
3004 port=port,
3005 debug=kwargs.get("debug", 0),
3006 ).ip
3007
3008 # Armoring checks
3009 if fast:
3010 if mode == self.MODE.AS_REQ:
3011 # Requires an external ticket
3012 if not armor_ticket or not armor_ticket_upn or not armor_ticket_skey:
3013 raise ValueError(
3014 "Implicit armoring is not possible on AS-REQ: "
3015 "please provide the 3 required armor arguments"
3016 )
3017 elif mode == self.MODE.TGS_REQ:
3018 if armor_ticket and (not armor_ticket_upn or not armor_ticket_skey):
3019 raise ValueError(
3020 "Cannot specify armor_ticket without armor_ticket_{upn,skey}"
3021 )
3022
3023 if mode == self.MODE.GET_SALT:
3024 if etypes is not None:
3025 raise ValueError("Cannot specify etypes in GET_SALT mode !")
3026
3027 from scapy.libs.rfc3961 import EncryptionType
3028
3029 etypes = [
3030 EncryptionType.AES256_CTS_HMAC_SHA1_96,
3031 EncryptionType.AES128_CTS_HMAC_SHA1_96,
3032 ]
3033 elif etypes is None:
3034 from scapy.libs.rfc3961 import EncryptionType
3035
3036 etypes = [
3037 EncryptionType.AES256_CTS_HMAC_SHA1_96,
3038 EncryptionType.AES128_CTS_HMAC_SHA1_96,
3039 EncryptionType.RC4_HMAC,
3040 EncryptionType.RC4_HMAC_EXP,
3041 EncryptionType.DES_CBC_MD5,
3042 ]
3043 self.etypes = etypes
3044
3045 self.mode = mode
3046
3047 self.result = None # Result
3048
3049 self._timeout = timeout
3050 self._ip = ip
3051 self._port = port
3052 self.kdc_proxy = kdc_proxy
3053 self.kdc_proxy_no_check_certificate = kdc_proxy_no_check_certificate
3054
3055 if self.mode in [self.MODE.AS_REQ, self.MODE.GET_SALT]:
3056 self.host = host.upper()
3057 self.password = password and bytes_encode(password)
3058 self.spn = spn
3059 self.upn = upn
3060 self.realm = realm.upper()
3061 self.x509 = x509
3062 self.x509key = x509key
3063 self.ticket = ticket
3064 self.fast = fast
3065 self.armor_ticket = armor_ticket
3066 self.armor_ticket_upn = armor_ticket_upn
3067 self.armor_ticket_skey = armor_ticket_skey
3068 self.key_list_req = key_list_req
3069 self.renew = renew
3070 self.additional_tickets = additional_tickets # U2U + S4U2Proxy
3071 self.u2u = u2u # U2U
3072 self.for_user = for_user # FOR-USER
3073 self.s4u2proxy = s4u2proxy # S4U2Proxy
3074 self.dmsa = dmsa # DMSA
3075 self.key = key
3076 self.subkey = None # In the AP-REQ authenticator
3077 self.replykey = None # Key used for reply
3078 # See RFC4120 - sect 7.2.2
3079 # This marks whether we should follow-up after an EOF
3080 self.should_followup = False
3081 # This marks that we sent a FAST-req and are awaiting for an answer
3082 self.fast_req_sent = False
3083 # Session parameters
3084 self.pre_auth = False
3085 self.fast_rep = None
3086 self.fast_error = None
3087 self.fast_skey = None # The random subkey used for fast
3088 self.fast_armorkey = None # The armor key
3089 self.fxcookie = None
3090
3091 sock = self._connect()
3092 super(KerberosClient, self).__init__(
3093 sock=sock,
3094 **kwargs,
3095 )
3096
3097 def _connect(self):
3098 """
3099 Internal function to bind a socket to the DC.
3100 This also takes care of an eventual KDC proxy.
3101 """
3102 if self.kdc_proxy:
3103 # If we are using a KDC Proxy, wrap the socket with the KdcProxySocket,
3104 # that takes our messages and transport them over HTTP.
3105 sock = KdcProxySocket(
3106 url=self.kdc_proxy,
3107 targetDomain=self.realm,
3108 no_check_certificate=self.kdc_proxy_no_check_certificate,
3109 )
3110 else:
3111 sock = socket.socket()
3112 sock.settimeout(self._timeout)
3113 sock.connect((self._ip, self._port))
3114 sock = StreamSocket(sock, KerberosTCPHeader)
3115 return sock
3116
3117 def send(self, pkt):
3118 """
3119 Sends a wrapped Kerberos packet
3120 """
3121 super(KerberosClient, self).send(KerberosTCPHeader() / pkt)
3122
3123 def _base_kdc_req(self, now_time):
3124 """
3125 Return the KRB_KDC_REQ_BODY used in both AS-REQ and TGS-REQ
3126 """
3127 kdcreq = KRB_KDC_REQ_BODY(
3128 etype=[ASN1_INTEGER(x) for x in self.etypes],
3129 additionalTickets=None,
3130 # Windows default
3131 kdcOptions="forwardable+renewable+canonicalize+renewable-ok",
3132 cname=None,
3133 realm=ASN1_GENERAL_STRING(self.realm),
3134 till=ASN1_GENERALIZED_TIME(now_time + timedelta(hours=10)),
3135 rtime=ASN1_GENERALIZED_TIME(now_time + timedelta(hours=10)),
3136 nonce=ASN1_INTEGER(RandNum(0, 0x7FFFFFFF)._fix()),
3137 )
3138 if self.renew:
3139 kdcreq.kdcOptions.set(30, 1) # set 'renew' (bit 30)
3140 return kdcreq
3141
3142 def calc_fast_armorkey(self):
3143 """
3144 Calculate and return the FAST armorkey
3145 """
3146 # Generate a random key of the same type than ticket_skey
3147 from scapy.libs.rfc3961 import Key, KRB_FX_CF2
3148
3149 if self.mode == self.MODE.AS_REQ:
3150 # AS-REQ mode
3151 self.fast_skey = Key.new_random_key(self.armor_ticket_skey.etype)
3152
3153 self.fast_armorkey = KRB_FX_CF2(
3154 self.fast_skey,
3155 self.armor_ticket_skey,
3156 b"subkeyarmor",
3157 b"ticketarmor",
3158 )
3159 elif self.mode == self.MODE.TGS_REQ:
3160 # TGS-REQ: 2 cases
3161
3162 self.subkey = Key.new_random_key(self.key.etype)
3163
3164 if not self.armor_ticket:
3165 # Case 1: Implicit armoring
3166 self.fast_armorkey = KRB_FX_CF2(
3167 self.subkey,
3168 self.key,
3169 b"subkeyarmor",
3170 b"ticketarmor",
3171 )
3172 else:
3173 # Case 2: Explicit armoring, in "Compounded Identity mode".
3174 # This is a Microsoft extension: see [MS-KILE] sect 3.3.5.7.4
3175
3176 self.fast_skey = Key.new_random_key(self.armor_ticket_skey.etype)
3177
3178 explicit_armor_key = KRB_FX_CF2(
3179 self.fast_skey,
3180 self.armor_ticket_skey,
3181 b"subkeyarmor",
3182 b"ticketarmor",
3183 )
3184
3185 self.fast_armorkey = KRB_FX_CF2(
3186 explicit_armor_key,
3187 self.subkey,
3188 b"explicitarmor",
3189 b"tgsarmor",
3190 )
3191
3192 def _fast_wrap(self, kdc_req, padata, now_time, pa_tgsreq_ap=None):
3193 """
3194 :param kdc_req: the KDC_REQ_BODY to wrap
3195 :param padata: the list of PADATA to wrap
3196 :param now_time: the current timestamp used by the client
3197 """
3198
3199 # Create the PA Fast request wrapper
3200 pafastreq = PA_FX_FAST_REQUEST(
3201 armoredData=KrbFastArmoredReq(
3202 reqChecksum=Checksum(),
3203 encFastReq=EncryptedData(),
3204 )
3205 )
3206
3207 if self.armor_ticket is not None:
3208 # EXPLICIT mode only (AS-REQ or TGS-REQ)
3209
3210 pafastreq.armoredData.armor = KrbFastArmor(
3211 armorType=1, # FX_FAST_ARMOR_AP_REQUEST
3212 armorValue=KRB_AP_REQ(
3213 ticket=self.armor_ticket,
3214 authenticator=EncryptedData(),
3215 ),
3216 )
3217
3218 # Populate the authenticator. Note the client is the wrapper
3219 _, crealm = _parse_upn(self.armor_ticket_upn)
3220 authenticator = KRB_Authenticator(
3221 crealm=ASN1_GENERAL_STRING(crealm),
3222 cname=PrincipalName.fromUPN(self.armor_ticket_upn),
3223 cksum=None,
3224 ctime=ASN1_GENERALIZED_TIME(now_time),
3225 cusec=ASN1_INTEGER(0),
3226 subkey=EncryptionKey.fromKey(self.fast_skey),
3227 seqNumber=ASN1_INTEGER(0),
3228 encAuthorizationData=None,
3229 )
3230 pafastreq.armoredData.armor.armorValue.authenticator.encrypt(
3231 self.armor_ticket_skey,
3232 authenticator,
3233 )
3234
3235 # Sign the fast request wrapper
3236 if self.mode == self.MODE.TGS_REQ:
3237 # "for a TGS-REQ, it is performed over the type AP-
3238 # REQ in the PA-TGS-REQ padata of the TGS request"
3239 pafastreq.armoredData.reqChecksum.make(
3240 self.fast_armorkey,
3241 bytes(pa_tgsreq_ap),
3242 )
3243 else:
3244 # "For an AS-REQ, it is performed over the type KDC-REQ-
3245 # BODY for the req-body field of the KDC-REQ structure of the
3246 # containing message"
3247 pafastreq.armoredData.reqChecksum.make(
3248 self.fast_armorkey,
3249 bytes(kdc_req),
3250 )
3251
3252 # Build and encrypt the Fast request
3253 fastreq = KrbFastReq(
3254 padata=padata,
3255 reqBody=kdc_req,
3256 )
3257 pafastreq.armoredData.encFastReq.encrypt(
3258 self.fast_armorkey,
3259 fastreq,
3260 )
3261
3262 # Return the PADATA
3263 return PADATA(
3264 padataType=ASN1_INTEGER(136), # PA-FX-FAST
3265 padataValue=pafastreq,
3266 )
3267
3268 def as_req(self):
3269 now_time = datetime.now(timezone.utc).replace(microsecond=0)
3270
3271 # 1. Build and populate KDC-REQ
3272 kdc_req = self._base_kdc_req(now_time=now_time)
3273 kdc_req.addresses = [
3274 HostAddress(
3275 addrType=ASN1_INTEGER(20), # Netbios
3276 address=ASN1_STRING(self.host.ljust(16, " ")),
3277 )
3278 ]
3279 kdc_req.cname = PrincipalName.fromUPN(self.upn)
3280 kdc_req.sname = PrincipalName.fromSPN(self.spn)
3281
3282 # 2. Build the list of PADATA
3283 padata = [
3284 PADATA(
3285 padataType=ASN1_INTEGER(128), # PA-PAC-REQUEST
3286 padataValue=PA_PAC_REQUEST(includePac=ASN1_BOOLEAN(-1)),
3287 )
3288 ]
3289
3290 # Cookie support
3291 if self.fxcookie:
3292 padata.insert(
3293 0,
3294 PADATA(
3295 padataType=133, # PA-FX-COOKIE
3296 padataValue=self.fxcookie,
3297 ),
3298 )
3299
3300 # FAST
3301 if self.fast:
3302 # Calculate the armor key
3303 self.calc_fast_armorkey()
3304
3305 # [MS-KILE] sect 3.2.5.5
3306 # "When sending the AS-REQ, add a PA-PAC-OPTIONS [167]"
3307 padata.append(
3308 PADATA(
3309 padataType=ASN1_INTEGER(167), # PA-PAC-OPTIONS
3310 padataValue=PA_PAC_OPTIONS(
3311 options="Claims",
3312 ),
3313 )
3314 )
3315
3316 # Pre-auth is requested
3317 if self.pre_auth:
3318 if self.x509:
3319 # Special PKINIT (RFC4556) factor
3320 pafactor = PADATA(
3321 padataType=16, padataValue=PA_PK_AS_REQ() # PA-PK-AS-REQ
3322 )
3323 raise NotImplementedError("PKINIT isn't implemented yet !")
3324 else:
3325 # Key-based factor
3326
3327 if self.fast:
3328 # Special FAST factor
3329 # RFC6113 sect 5.4.6
3330 from scapy.libs.rfc3961 import KRB_FX_CF2
3331
3332 # Calculate the 'challenge key'
3333 ts_key = KRB_FX_CF2(
3334 self.fast_armorkey,
3335 self.key,
3336 b"clientchallengearmor",
3337 b"challengelongterm",
3338 )
3339 pafactor = PADATA(
3340 padataType=138, # PA-ENCRYPTED-CHALLENGE
3341 padataValue=EncryptedData(),
3342 )
3343 else:
3344 # Usual 'timestamp' factor
3345 ts_key = self.key
3346 pafactor = PADATA(
3347 padataType=2, # PA-ENC-TIMESTAMP
3348 padataValue=EncryptedData(),
3349 )
3350 pafactor.padataValue.encrypt(
3351 ts_key,
3352 PA_ENC_TS_ENC(patimestamp=ASN1_GENERALIZED_TIME(now_time)),
3353 )
3354
3355 # Insert Pre-Authentication data
3356 padata.insert(
3357 0,
3358 pafactor,
3359 )
3360
3361 # FAST support
3362 if self.fast:
3363 # We are using RFC6113's FAST armoring. The PADATA's are therefore
3364 # hidden inside the encrypted section.
3365 padata = [
3366 self._fast_wrap(
3367 kdc_req=kdc_req,
3368 padata=padata,
3369 now_time=now_time,
3370 )
3371 ]
3372
3373 # 3. Build the request
3374 asreq = Kerberos(
3375 root=KRB_AS_REQ(
3376 padata=padata,
3377 reqBody=kdc_req,
3378 )
3379 )
3380
3381 # Note the reply key
3382 self.replykey = self.key
3383
3384 return asreq
3385
3386 def tgs_req(self):
3387 now_time = datetime.now(timezone.utc).replace(microsecond=0)
3388
3389 # Compute armor key for FAST
3390 if self.fast:
3391 self.calc_fast_armorkey()
3392
3393 # 1. Build and populate KDC-REQ
3394 kdc_req = self._base_kdc_req(now_time=now_time)
3395 kdc_req.sname = PrincipalName.fromSPN(self.spn)
3396
3397 # Additional tickets
3398 if self.additional_tickets:
3399 kdc_req.additionalTickets = self.additional_tickets
3400
3401 # U2U
3402 if self.u2u:
3403 kdc_req.kdcOptions.set(28, 1) # set 'enc-tkt-in-skey' (bit 28)
3404
3405 # 2. Build the list of PADATA
3406 padata = []
3407
3408 # [MS-SFU] FOR-USER extension
3409 if self.for_user is not None:
3410 from scapy.libs.rfc3961 import ChecksumType, EncryptionType
3411
3412 # [MS-SFU] note 4:
3413 # "Windows Vista, Windows Server 2008, Windows 7, and Windows Server
3414 # 2008 R2 send the PA-S4U-X509-USER padata type alone if the user's
3415 # certificate is available.
3416 # If the user's certificate is not available, it sends both the
3417 # PA-S4U-X509-USER padata type and the PA-FOR-USER padata type.
3418 # When the PA-S4U-X509-USER padata type is used without the user's
3419 # certificate, the certificate field is not present."
3420
3421 # 1. Add PA_S4U_X509_USER
3422 pasfux509 = PA_S4U_X509_USER(
3423 userId=S4UUserID(
3424 nonce=kdc_req.nonce,
3425 # [MS-SFU] note 5:
3426 # "Windows S4U clients always set this option."
3427 options="USE_REPLY_KEY_USAGE",
3428 cname=PrincipalName.fromUPN(self.for_user),
3429 crealm=ASN1_GENERAL_STRING(_parse_upn(self.for_user)[1]),
3430 subjectCertificate=None, # TODO
3431 ),
3432 checksum=Checksum(),
3433 )
3434
3435 if self.dmsa:
3436 # DMSA = set UNCONDITIONAL_DELEGATION to 1
3437 pasfux509.userId.options.set(4, 1)
3438
3439 if self.key.etype in [EncryptionType.RC4_HMAC, EncryptionType.RC4_HMAC_EXP]:
3440 # "if the key's encryption type is RC4_HMAC_NT (23) the checksum type
3441 # is rsa-md4 (2) as defined in section 6.2.6 of [RFC3961]."
3442 pasfux509.checksum.make(
3443 self.key,
3444 bytes(pasfux509.userId),
3445 cksumtype=ChecksumType.RSA_MD4,
3446 )
3447 else:
3448 pasfux509.checksum.make(
3449 self.key,
3450 bytes(pasfux509.userId),
3451 )
3452 padata.append(
3453 PADATA(
3454 padataType=ASN1_INTEGER(130), # PA-FOR-X509-USER
3455 padataValue=pasfux509,
3456 )
3457 )
3458
3459 # 2. Add PA_FOR_USER
3460 if True: # XXX user's certificate is not available.
3461 paforuser = PA_FOR_USER(
3462 userName=PrincipalName.fromUPN(self.for_user),
3463 userRealm=ASN1_GENERAL_STRING(_parse_upn(self.for_user)[1]),
3464 cksum=Checksum(),
3465 )
3466 S4UByteArray = struct.pack( # [MS-SFU] sect 2.2.1
3467 "<I", paforuser.userName.nameType.val
3468 ) + (
3469 (
3470 "".join(x.val for x in paforuser.userName.nameString)
3471 + paforuser.userRealm.val
3472 + paforuser.authPackage.val
3473 ).encode()
3474 )
3475 paforuser.cksum.make(
3476 self.key,
3477 S4UByteArray,
3478 cksumtype=ChecksumType.HMAC_MD5,
3479 )
3480 padata.append(
3481 PADATA(
3482 padataType=ASN1_INTEGER(129), # PA-FOR-USER
3483 padataValue=paforuser,
3484 )
3485 )
3486
3487 # [MS-SFU] S4U2proxy - sect 3.1.5.2.1
3488 if self.s4u2proxy:
3489 # "PA-PAC-OPTIONS with resource-based constrained-delegation bit set"
3490 padata.append(
3491 PADATA(
3492 padataType=ASN1_INTEGER(167), # PA-PAC-OPTIONS
3493 padataValue=PA_PAC_OPTIONS(
3494 options="Resource-based-constrained-delegation",
3495 ),
3496 )
3497 )
3498 # "kdc-options field: MUST include the new cname-in-addl-tkt options flag"
3499 kdc_req.kdcOptions.set(14, 1)
3500
3501 # [MS-KILE] 2.2.11 KERB-KEY-LIST-REQ
3502 if self.key_list_req:
3503 padata.append(
3504 PADATA(
3505 padataType=ASN1_INTEGER(161), # KERB-KEY-LIST-REQ
3506 padataValue=KERB_KEY_LIST_REQ(
3507 keytypes=[ASN1_INTEGER(x) for x in self.key_list_req]
3508 ),
3509 )
3510 )
3511
3512 # 3. Build the AP-req inside a PA
3513 apreq = KRB_AP_REQ(ticket=self.ticket, authenticator=EncryptedData())
3514 pa_tgs_req = PADATA(
3515 padataType=ASN1_INTEGER(1), # PA-TGS-REQ
3516 padataValue=apreq,
3517 )
3518
3519 # 4. Populate it's authenticator
3520 _, crealm = _parse_upn(self.upn)
3521 authenticator = KRB_Authenticator(
3522 crealm=ASN1_GENERAL_STRING(crealm),
3523 cname=PrincipalName.fromUPN(self.upn),
3524 cksum=None,
3525 ctime=ASN1_GENERALIZED_TIME(now_time),
3526 cusec=ASN1_INTEGER(0),
3527 subkey=EncryptionKey.fromKey(self.subkey) if self.subkey else None,
3528 seqNumber=None,
3529 encAuthorizationData=None,
3530 )
3531
3532 # Compute checksum
3533 if self.key.cksumtype:
3534 authenticator.cksum = Checksum()
3535 authenticator.cksum.make(
3536 self.key,
3537 bytes(kdc_req),
3538 )
3539
3540 # Encrypt authenticator
3541 apreq.authenticator.encrypt(self.key, authenticator)
3542
3543 # 5. Process FAST if required
3544 if self.fast:
3545 padata = [
3546 self._fast_wrap(
3547 kdc_req=kdc_req,
3548 padata=padata,
3549 now_time=now_time,
3550 pa_tgsreq_ap=apreq,
3551 )
3552 ]
3553
3554 # 6. Add the final PADATA
3555 padata.append(pa_tgs_req)
3556
3557 # 7. Build the request
3558 tgsreq = Kerberos(
3559 root=KRB_TGS_REQ(
3560 padata=padata,
3561 reqBody=kdc_req,
3562 )
3563 )
3564
3565 # Note the reply key
3566 if self.subkey:
3567 self.replykey = self.subkey
3568 else:
3569 self.replykey = self.key
3570
3571 return tgsreq
3572
3573 @ATMT.state(initial=1)
3574 def BEGIN(self):
3575 pass
3576
3577 @ATMT.condition(BEGIN)
3578 def should_send_as_req(self):
3579 if self.mode in [self.MODE.AS_REQ, self.MODE.GET_SALT]:
3580 raise self.SENT_AS_REQ()
3581
3582 @ATMT.condition(BEGIN)
3583 def should_send_tgs_req(self):
3584 if self.mode == self.MODE.TGS_REQ:
3585 raise self.SENT_TGS_REQ()
3586
3587 @ATMT.action(should_send_as_req)
3588 def send_as_req(self):
3589 self.send(self.as_req())
3590
3591 @ATMT.action(should_send_tgs_req)
3592 def send_tgs_req(self):
3593 self.send(self.tgs_req())
3594
3595 @ATMT.state()
3596 def SENT_AS_REQ(self):
3597 pass
3598
3599 @ATMT.state()
3600 def SENT_TGS_REQ(self):
3601 pass
3602
3603 def _process_padatas_and_key(self, padatas):
3604 from scapy.libs.rfc3961 import EncryptionType, Key, KRB_FX_CF2
3605
3606 etype = None
3607 salt = b""
3608 # 1. Process pa-data
3609 if padatas is not None:
3610 for padata in padatas:
3611 if padata.padataType == 0x13 and etype is None: # PA-ETYPE-INFO2
3612 elt = padata.padataValue.seq[0]
3613 if elt.etype.val in self.etypes:
3614 etype = elt.etype.val
3615 if etype != EncryptionType.RC4_HMAC:
3616 salt = elt.salt.val
3617 elif padata.padataType == 133: # PA-FX-COOKIE
3618 self.fxcookie = padata.padataValue
3619 elif padata.padataType == 136: # PA-FX-FAST
3620 if isinstance(padata.padataValue, PA_FX_FAST_REPLY):
3621 self.fast_rep = (
3622 padata.padataValue.armoredData.encFastRep.decrypt(
3623 self.fast_armorkey,
3624 )
3625 )
3626 elif padata.padataType == 137: # PA-FX-ERROR
3627 self.fast_error = padata.padataValue
3628 elif padata.padataType == 130: # PA-FOR-X509-USER
3629 # Verify S4U checksum
3630 key_usage_number = None
3631 pasfux509 = padata.padataValue
3632 # [MS-SFU] sect 2.2.2
3633 # "In a reply, indicates that it was signed with key usage 27"
3634 if pasfux509.userId.options.val[2] == "1": # USE_REPLY_KEY_USAGE
3635 key_usage_number = 27
3636 pasfux509.checksum.verify(
3637 self.key,
3638 bytes(pasfux509.userId),
3639 key_usage_number=key_usage_number,
3640 )
3641
3642 # 2. Update the current key if necessary
3643
3644 # Compute key if not already provided
3645 if self.key is None and etype is not None:
3646 self.key = Key.string_to_key(
3647 etype,
3648 self.password,
3649 salt,
3650 )
3651
3652 # Update the key with the fast reply, if necessary
3653 if self.fast_rep and self.fast_rep.strengthenKey:
3654 # "The strengthen-key field MAY be set in an AS reply"
3655 self.replykey = KRB_FX_CF2(
3656 self.fast_rep.strengthenKey.toKey(),
3657 self.replykey,
3658 b"strengthenkey",
3659 b"replykey",
3660 )
3661
3662 @ATMT.receive_condition(SENT_AS_REQ, prio=0)
3663 def receive_salt_mode(self, pkt):
3664 # This is only for "Salt-Mode", a mode where we get the salt then
3665 # exit.
3666 if self.mode == self.MODE.GET_SALT:
3667 if Kerberos not in pkt:
3668 raise self.FINAL()
3669 if not isinstance(pkt.root, KRB_ERROR):
3670 log_runtime.error("Pre-auth is likely disabled !")
3671 raise self.FINAL()
3672 if pkt.root.errorCode == 25: # KDC_ERR_PREAUTH_REQUIRED
3673 for padata in pkt.root.eData.seq:
3674 if padata.padataType == 0x13: # PA-ETYPE-INFO2
3675 elt = padata.padataValue.seq[0]
3676 if elt.etype.val in self.etypes:
3677 self.result = elt.salt.val
3678 raise self.FINAL()
3679 else:
3680 log_runtime.error("Failed to retrieve the salt !")
3681 raise self.FINAL()
3682
3683 @ATMT.receive_condition(SENT_AS_REQ, prio=1)
3684 def receive_krb_error_as_req(self, pkt):
3685 # We check for Kerberos errors.
3686 # There is a special case for PREAUTH_REQUIRED error, which means that preauth
3687 # is required and we need to do a second exchange.
3688 if Kerberos in pkt and isinstance(pkt.root, KRB_ERROR):
3689 # Process PAs if available
3690 if pkt.root.eData and isinstance(pkt.root.eData, MethodData):
3691 self._process_padatas_and_key(pkt.root.eData.seq)
3692
3693 # Special case for FAST errors
3694 if self.fast_rep:
3695 # This is actually a fast response error !
3696 frep, self.fast_rep = self.fast_rep, None
3697 # Re-process PAs
3698 self._process_padatas_and_key(frep.padata)
3699 # Extract real Kerberos error from FAST message
3700 ferr = Kerberos(root=self.fast_error)
3701 self.fast_error = None
3702 # Recurse
3703 self.receive_krb_error_as_req(ferr)
3704 return
3705
3706 if pkt.root.errorCode == 25: # KDC_ERR_PREAUTH_REQUIRED
3707 if not self.key:
3708 log_runtime.error(
3709 "Got 'KDC_ERR_PREAUTH_REQUIRED', "
3710 "but no possible key could be computed."
3711 )
3712 raise self.FINAL()
3713 self.should_followup = True
3714 self.pre_auth = True
3715 raise self.BEGIN()
3716 else:
3717 log_runtime.error("Received KRB_ERROR")
3718 pkt.show()
3719 raise self.FINAL()
3720
3721 @ATMT.receive_condition(SENT_AS_REQ, prio=2)
3722 def receive_as_rep(self, pkt):
3723 if Kerberos in pkt and isinstance(pkt.root, KRB_AS_REP):
3724 raise self.FINAL().action_parameters(pkt)
3725
3726 @ATMT.eof(SENT_AS_REQ)
3727 def retry_after_eof_in_apreq(self):
3728 if self.should_followup:
3729 # Reconnect and Restart
3730 self.should_followup = False
3731 self.update_sock(self._connect())
3732 raise self.BEGIN()
3733 else:
3734 log_runtime.error("Socket was closed in an unexpected state")
3735 raise self.FINAL()
3736
3737 @ATMT.action(receive_as_rep)
3738 def decrypt_as_rep(self, pkt):
3739 self._process_padatas_and_key(pkt.root.padata)
3740 if not self.pre_auth:
3741 log_runtime.warning("Pre-authentication was disabled for this account !")
3742
3743 # Process FAST response
3744 if self.fast_rep:
3745 # Verify the ticket-checksum
3746 self.fast_rep.finished.ticketChecksum.verify(
3747 self.fast_armorkey,
3748 bytes(pkt.root.ticket),
3749 )
3750 self.fast_rep = None
3751 elif self.fast:
3752 raise ValueError("Answer was not FAST ! Is it supported?")
3753
3754 # Decrypt AS-REP response
3755 enc = pkt.root.encPart
3756 res = enc.decrypt(self.replykey)
3757 self.result = self.RES_AS_MODE(pkt.root, res.key.toKey(), res)
3758
3759 @ATMT.receive_condition(SENT_TGS_REQ)
3760 def receive_krb_error_tgs_req(self, pkt):
3761 if Kerberos in pkt and isinstance(pkt.root, KRB_ERROR):
3762 # Process PAs if available
3763 if pkt.root.eData and isinstance(pkt.root.eData, MethodData):
3764 self._process_padatas_and_key(pkt.root.eData.seq)
3765
3766 if self.fast_rep:
3767 # This is actually a fast response error !
3768 frep, self.fast_rep = self.fast_rep, None
3769 # Re-process PAs
3770 self._process_padatas_and_key(frep.padata)
3771 # Extract real Kerberos error from FAST message
3772 ferr = Kerberos(root=self.fast_error)
3773 self.fast_error = None
3774 # Recurse
3775 self.receive_krb_error_tgs_req(ferr)
3776 return
3777
3778 if (
3779 pkt.root.errorCode == 0x07
3780 and isinstance(pkt.root.eData, KERB_ERROR_UNK)
3781 and pkt.root.eData.dataType == -128
3782 ):
3783 log_runtime.warning(
3784 "KDC requires U2U for SPN '%s' !" % pkt.root.getSPN()
3785 )
3786 else:
3787 log_runtime.warning("Received KRB_ERROR")
3788 pkt.show()
3789 raise self.FINAL()
3790
3791 @ATMT.receive_condition(SENT_TGS_REQ)
3792 def receive_tgs_rep(self, pkt):
3793 if Kerberos in pkt and isinstance(pkt.root, KRB_TGS_REP):
3794 if (
3795 not self.renew
3796 and not self.dmsa
3797 and pkt.root.ticket.sname.nameString[0].val == b"krbtgt"
3798 ):
3799 log_runtime.warning("Received a cross-realm referral ticket !")
3800 raise self.FINAL().action_parameters(pkt)
3801
3802 @ATMT.action(receive_tgs_rep)
3803 def decrypt_tgs_rep(self, pkt):
3804 self._process_padatas_and_key(pkt.root.padata)
3805
3806 # Process FAST response
3807 if self.fast_rep:
3808 # Verify the ticket-checksum
3809 self.fast_rep.finished.ticketChecksum.verify(
3810 self.fast_armorkey,
3811 bytes(pkt.root.ticket),
3812 )
3813 self.fast_rep = None
3814 elif self.fast:
3815 raise ValueError("Answer was not FAST ! Is it supported?")
3816
3817 # Decrypt TGS-REP response
3818 enc = pkt.root.encPart
3819 if self.subkey:
3820 # "In a TGS-REP message, the key
3821 # usage value is 8 if the TGS session key is used, or 9 if a TGS
3822 # authenticator subkey is used."
3823 res = enc.decrypt(self.replykey, key_usage_number=9, cls=EncTGSRepPart)
3824 else:
3825 res = enc.decrypt(self.replykey)
3826
3827 # Store result
3828 self.result = self.RES_TGS_MODE(pkt.root, res.key.toKey(), res)
3829
3830 @ATMT.state(final=1)
3831 def FINAL(self):
3832 pass
3833
3834
3835def _parse_upn(upn):
3836 """
3837 Extract the username and realm from full UPN
3838 """
3839 m = re.match(r"^([^@\\/]+)(@|\\)([^@\\/]+)$", upn)
3840 if not m:
3841 err = "Invalid UPN: '%s'" % upn
3842 if "/" in upn:
3843 err += ". Did you mean '%s' ?" % upn.replace("/", "\\")
3844 elif "@" not in upn and "\\" not in upn:
3845 err += ". Provide domain as so: '%s@domain.local'" % upn
3846 raise ValueError(err)
3847 if m.group(2) == "@":
3848 user = m.group(1)
3849 domain = m.group(3)
3850 else:
3851 user = m.group(3)
3852 domain = m.group(1)
3853 return user, domain
3854
3855
3856def _parse_spn(spn):
3857 """
3858 Extract ServiceName and realm from full SPN
3859 """
3860 # See [MS-ADTS] sect 2.2.21 for SPN format. We discard the servicename.
3861 m = re.match(r"^((?:[^@\\/]+)/(?:[^@\\/]+))(?:/[^@\\/]+)?(?:@([^@\\/]+))?$", spn)
3862 if not m:
3863 try:
3864 # If SPN is a UPN, we are doing U2U :D
3865 return _parse_upn(spn)
3866 except ValueError:
3867 raise ValueError("Invalid SPN: '%s'" % spn)
3868 return m.group(1), m.group(2)
3869
3870
3871def _spn_are_equal(spn1, spn2):
3872 """
3873 Check that two SPNs are equal.
3874 """
3875 spn1, _ = _parse_spn(spn1)
3876 spn2, _ = _parse_spn(spn2)
3877 return spn1.lower() == spn2.lower()
3878
3879
3880def krb_as_req(
3881 upn,
3882 spn=None,
3883 ip=None,
3884 key=None,
3885 password=None,
3886 realm=None,
3887 host="WIN10",
3888 p12=None,
3889 x509=None,
3890 x509key=None,
3891 **kwargs,
3892):
3893 r"""
3894 Kerberos AS-Req
3895
3896 :param upn: the user principal name formatted as "DOMAIN\user", "DOMAIN/user"
3897 or "user@DOMAIN"
3898 :param spn: (optional) the full service principal name.
3899 Defaults to "krbtgt/<realm>"
3900 :param ip: the KDC ip. (optional. If not provided, Scapy will query the DNS for
3901 _kerberos._tcp.dc._msdcs.domain.local).
3902 :param key: (optional) pass the Key object.
3903 :param password: (optional) otherwise, pass the user's password
3904 :param x509: (optional) pass a x509 certificate for PKINIT.
3905 :param x509key: (optional) pass the key of the x509 certificate for PKINIT.
3906 :param p12: (optional) use a pfx/p12 instead of x509 and x509key. In this case,
3907 'password' is the password of the p12.
3908 :param realm: (optional) the realm to use. Otherwise use the one from UPN.
3909 :param host: (optional) the host performing the AS-Req. WIN10 by default.
3910
3911 :return: returns a named tuple (asrep=<...>, sessionkey=<...>)
3912
3913 Example::
3914
3915 >>> # The KDC is found via DC Locator, we ask a TGT for user1
3916 >>> krb_as_req("user1@DOMAIN.LOCAL", password="Password1")
3917
3918 Equivalent::
3919
3920 >>> from scapy.libs.rfc3961 import Key, EncryptionType
3921 >>> key = Key(EncryptionType.AES256_CTS_HMAC_SHA1_96, key=hex_bytes("6d0748c546
3922 ...: f4e99205e78f8da7681d4ec5520ae4815543720c2a647c1ae814c9"))
3923 >>> krb_as_req("user1@DOMAIN.LOCAL", ip="192.168.122.17", key=key)
3924
3925 Example using PKINIT with a p12::
3926
3927 >>> krb_as_req("user1@DOMAIN.LOCAL", p12="./store.p12", password="password")
3928 """
3929 if realm is None:
3930 _, realm = _parse_upn(upn)
3931 if key is None and p12 is None and x509 is None:
3932 if password is None:
3933 try:
3934 from prompt_toolkit import prompt
3935
3936 password = prompt("Enter password: ", is_password=True)
3937 except ImportError:
3938 password = input("Enter password: ")
3939 cli = KerberosClient(
3940 mode=KerberosClient.MODE.AS_REQ,
3941 realm=realm,
3942 ip=ip,
3943 spn=spn or "krbtgt/" + realm,
3944 host=host,
3945 upn=upn,
3946 password=password,
3947 key=key,
3948 p12=p12,
3949 x509=x509,
3950 x509key=x509key,
3951 **kwargs,
3952 )
3953 cli.run()
3954 cli.stop()
3955 return cli.result
3956
3957
3958def krb_tgs_req(
3959 upn,
3960 spn,
3961 sessionkey,
3962 ticket,
3963 ip=None,
3964 renew=False,
3965 realm=None,
3966 additional_tickets=[],
3967 u2u=False,
3968 etypes=None,
3969 for_user=None,
3970 s4u2proxy=False,
3971 **kwargs,
3972):
3973 r"""
3974 Kerberos TGS-Req
3975
3976 :param upn: the user principal name formatted as "DOMAIN\user", "DOMAIN/user"
3977 or "user@DOMAIN"
3978 :param spn: the full service principal name (e.g. "cifs/srv1")
3979 :param sessionkey: the session key retrieved from the tgt
3980 :param ticket: the tgt ticket
3981 :param ip: the KDC ip. (optional. If not provided, Scapy will query the DNS for
3982 _kerberos._tcp.dc._msdcs.domain.local).
3983 :param renew: ask for renewal
3984 :param realm: (optional) the realm to use. Otherwise use the one from SPN.
3985 :param additional_tickets: (optional) a list of additional tickets to pass.
3986 :param u2u: (optional) if specified, enable U2U and request the ticket to be
3987 signed using the session key from the first additional ticket.
3988 :param etypes: array of EncryptionType values.
3989 By default: AES128, AES256, RC4, DES_MD5
3990 :param for_user: a user principal name to request the ticket for. This is the
3991 S4U2Self extension.
3992
3993 :return: returns a named tuple (tgsrep=<...>, sessionkey=<...>)
3994
3995 Example::
3996
3997 >>> # The KDC is on 192.168.122.17, we ask a TGT for user1
3998 >>> krb_as_req("user1@DOMAIN.LOCAL", "192.168.122.17", password="Password1")
3999
4000 Equivalent::
4001
4002 >>> from scapy.libs.rfc3961 import Key, EncryptionType
4003 >>> key = Key(EncryptionType.AES256_CTS_HMAC_SHA1_96, key=hex_bytes("6d0748c546
4004 ...: f4e99205e78f8da7681d4ec5520ae4815543720c2a647c1ae814c9"))
4005 >>> krb_as_req("user1@DOMAIN.LOCAL", "192.168.122.17", key=key)
4006 """
4007 cli = KerberosClient(
4008 mode=KerberosClient.MODE.TGS_REQ,
4009 realm=realm,
4010 upn=upn,
4011 ip=ip,
4012 spn=spn,
4013 key=sessionkey,
4014 ticket=ticket,
4015 renew=renew,
4016 additional_tickets=additional_tickets,
4017 u2u=u2u,
4018 etypes=etypes,
4019 for_user=for_user,
4020 s4u2proxy=s4u2proxy,
4021 **kwargs,
4022 )
4023 cli.run()
4024 cli.stop()
4025 return cli.result
4026
4027
4028def krb_as_and_tgs(upn, spn, ip=None, key=None, password=None, **kwargs):
4029 """
4030 Kerberos AS-Req then TGS-Req
4031 """
4032 res = krb_as_req(upn=upn, ip=ip, key=key, password=password, **kwargs)
4033 if not res:
4034 return
4035 return krb_tgs_req(
4036 upn=upn,
4037 spn=spn,
4038 sessionkey=res.sessionkey,
4039 ticket=res.asrep.ticket,
4040 ip=ip,
4041 **kwargs,
4042 )
4043
4044
4045def krb_get_salt(upn, ip=None, realm=None, host="WIN10", **kwargs):
4046 """
4047 Kerberos AS-Req only to get the salt associated with the UPN.
4048 """
4049 if realm is None:
4050 _, realm = _parse_upn(upn)
4051 cli = KerberosClient(
4052 mode=KerberosClient.MODE.GET_SALT,
4053 realm=realm,
4054 ip=ip,
4055 spn="krbtgt/" + realm,
4056 upn=upn,
4057 host=host,
4058 **kwargs,
4059 )
4060 cli.run()
4061 cli.stop()
4062 return cli.result
4063
4064
4065def kpasswd(
4066 upn,
4067 targetupn=None,
4068 ip=None,
4069 password=None,
4070 newpassword=None,
4071 key=None,
4072 ticket=None,
4073 realm=None,
4074 ssp=None,
4075 setpassword=None,
4076 timeout=3,
4077 port=464,
4078 debug=0,
4079 **kwargs,
4080):
4081 """
4082 Change a password using RFC3244's Kerberos Set / Change Password.
4083
4084 :param upn: the UPN to use for authentication
4085 :param targetupn: (optional) the UPN to change the password of. If not specified,
4086 same as upn.
4087 :param ip: the KDC ip. (optional. If not provided, Scapy will query the DNS for
4088 _kerberos._tcp.dc._msdcs.domain.local).
4089 :param key: (optional) pass the Key object.
4090 :param ticket: (optional) a ticket to use. Either a TGT or ST for kadmin/changepw.
4091 :param password: (optional) otherwise, pass the user's password
4092 :param realm: (optional) the realm to use. Otherwise use the one from UPN.
4093 :param setpassword: (optional) use "Set Password" mechanism.
4094 :param ssp: (optional) a Kerberos SSP for the service kadmin/changepw@REALM.
4095 If provided, you probably don't need anything else. Otherwise built.
4096 """
4097 from scapy.layers.ldap import dclocator
4098
4099 if not realm:
4100 _, realm = _parse_upn(upn)
4101 spn = "kadmin/changepw@%s" % realm
4102 if ip is None:
4103 ip = dclocator(
4104 realm,
4105 timeout=timeout,
4106 # Use connect mode instead of ldap for compatibility
4107 # with MIT kerberos servers
4108 mode="connect",
4109 port=port,
4110 debug=debug,
4111 ).ip
4112 if ssp is None and ticket is not None:
4113 tktspn = ticket.getSPN().split("/")[0]
4114 assert tktspn in ["krbtgt", "kadmin"], "Unexpected ticket type ! %s" % tktspn
4115 if tktspn == "krbtgt":
4116 log_runtime.info(
4117 "Using 'Set Password' mode. This only works with admin privileges."
4118 )
4119 setpassword = True
4120 resp = krb_tgs_req(
4121 upn=upn,
4122 spn=spn,
4123 ticket=ticket,
4124 sessionkey=key,
4125 ip=ip,
4126 debug=debug,
4127 )
4128 if resp is None:
4129 return
4130 ticket = resp.tgsrep.ticket
4131 key = resp.sessionkey
4132 if setpassword is None:
4133 setpassword = bool(targetupn)
4134 elif setpassword and targetupn is None:
4135 targetupn = upn
4136 assert setpassword or not targetupn, "Cannot use targetupn in changepassword mode !"
4137 # Get a ticket for kadmin/changepw
4138 if ssp is None:
4139 if ticket is None:
4140 # Get a ticket for kadmin/changepw through AS-REQ
4141 resp = krb_as_req(
4142 upn=upn,
4143 spn=spn,
4144 key=key,
4145 ip=ip,
4146 password=password,
4147 debug=debug,
4148 )
4149 if resp is None:
4150 return
4151 ticket = resp.asrep.ticket
4152 key = resp.sessionkey
4153 ssp = KerberosSSP(
4154 UPN=upn,
4155 SPN=spn,
4156 ST=ticket,
4157 KEY=key,
4158 DC_IP=ip,
4159 debug=debug,
4160 **kwargs,
4161 )
4162 Context, tok, negResult = ssp.GSS_Init_sec_context(
4163 None,
4164 req_flags=0, # No GSS_C_MUTUAL_FLAG
4165 )
4166 if negResult != GSS_S_CONTINUE_NEEDED:
4167 warning("SSP failed on initial GSS_Init_sec_context !")
4168 if tok:
4169 tok.show()
4170 return
4171 apreq = tok.innerToken.root
4172 # Connect
4173 sock = socket.socket()
4174 sock.settimeout(timeout)
4175 sock.connect((ip, port))
4176 sock = StreamSocket(sock, KpasswdTCPHeader)
4177 # Do KPASSWD request
4178 if newpassword is None:
4179 try:
4180 from prompt_toolkit import prompt
4181
4182 newpassword = prompt("Enter NEW password: ", is_password=True)
4183 except ImportError:
4184 newpassword = input("Enter NEW password: ")
4185 krbpriv = KRB_PRIV(encPart=EncryptedData())
4186 krbpriv.encPart.encrypt(
4187 Context.KrbSessionKey,
4188 EncKrbPrivPart(
4189 sAddress=HostAddress(
4190 addrType=ASN1_INTEGER(2), # IPv4
4191 address=ASN1_STRING(b"\xc0\xa8\x00e"),
4192 ),
4193 userData=ASN1_STRING(
4194 bytes(
4195 ChangePasswdData(
4196 newpasswd=newpassword,
4197 targname=PrincipalName.fromUPN(targetupn),
4198 targrealm=realm,
4199 )
4200 )
4201 if setpassword
4202 else newpassword
4203 ),
4204 timestamp=None,
4205 usec=None,
4206 seqNumber=Context.SendSeqNum,
4207 ),
4208 )
4209 resp = sock.sr1(
4210 KpasswdTCPHeader()
4211 / KPASSWD_REQ(
4212 pvno=0xFF80 if setpassword else 1,
4213 apreq=apreq,
4214 krbpriv=krbpriv,
4215 ),
4216 timeout=timeout,
4217 verbose=0,
4218 )
4219 # Verify KPASSWD response
4220 if not resp:
4221 raise TimeoutError("KPASSWD_REQ timed out !")
4222 if KPASSWD_REP not in resp:
4223 resp.show()
4224 raise ValueError("Invalid response to KPASSWD_REQ !")
4225 Context, tok, negResult = ssp.GSS_Init_sec_context(Context, resp.aprep)
4226 if negResult != GSS_S_COMPLETE:
4227 warning("SSP failed on subsequent GSS_Init_sec_context !")
4228 if tok:
4229 tok.show()
4230 return
4231 # Parse answer KRB_PRIV
4232 krbanswer = resp.krbpriv.encPart.decrypt(Context.KrbSessionKey)
4233 userRep = KPasswdRepData(krbanswer.userData.val)
4234 if userRep.resultCode != 0:
4235 warning(userRep.sprintf("KPASSWD failed !"))
4236 userRep.show()
4237 return
4238 print(userRep.sprintf("%resultCode%"))
4239
4240
4241# SSP
4242
4243
4244class KerberosSSP(SSP):
4245 """
4246 The KerberosSSP
4247
4248 Client settings:
4249
4250 :param ST: the service ticket to use for access.
4251 If not provided, will be retrieved
4252 :param SPN: the SPN of the service to use. If not provided, will use the
4253 target_name provided in the GSS_Init_sec_context
4254 :param UPN: The client UPN
4255 :param DC_IP: (optional) is ST+KEY are not provided, will need to contact
4256 the KDC at this IP. If not provided, will perform dc locator.
4257 :param TGT: (optional) pass a TGT to use to get the ST.
4258 :param KEY: the session key associated with the ST if it is provided,
4259 OR the session key associated with the TGT
4260 OR the kerberos key associated with the UPN
4261 :param PASSWORD: (optional) if a UPN is provided and not a KEY, this is the
4262 password of the UPN.
4263 :param U2U: (optional) use U2U when requesting the ST.
4264
4265 Server settings:
4266
4267 :param SPN: the SPN of the service to use.
4268 :param KEY: the kerberos key to use to decrypt the AP-req
4269 :param UPN: (optional) the UPN, if used in U2U mode.
4270 :param TGT: (optional) pass a TGT to use for U2U.
4271 :param DC_IP: (optional) if TGT is not provided, request one on the KDC at
4272 this IP using using the KEY when using U2U.
4273 """
4274
4275 oid = "1.2.840.113554.1.2.2"
4276 auth_type = 0x10
4277
4278 class STATE(SSP.STATE):
4279 INIT = 1
4280 CLI_SENT_TGTREQ = 2
4281 CLI_SENT_APREQ = 3
4282 CLI_RCVD_APREP = 4
4283 SRV_SENT_APREP = 5
4284 FAILED = -1
4285
4286 class CONTEXT(SSP.CONTEXT):
4287 __slots__ = [
4288 "SessionKey",
4289 "ServerHostname",
4290 "U2U",
4291 "KrbSessionKey", # raw Key object
4292 "STSessionKey", # raw ST Key object (for DCE_STYLE)
4293 "SeqNum", # for AP
4294 "SendSeqNum", # for MIC
4295 "RecvSeqNum", # for MIC
4296 "IsAcceptor",
4297 "SendSealKeyUsage",
4298 "SendSignKeyUsage",
4299 "RecvSealKeyUsage",
4300 "RecvSignKeyUsage",
4301 # server-only
4302 "UPN",
4303 "PAC",
4304 ]
4305
4306 def __init__(self, IsAcceptor, req_flags=None):
4307 self.state = KerberosSSP.STATE.INIT
4308 self.SessionKey = None
4309 self.ServerHostname = None
4310 self.U2U = False
4311 self.SendSeqNum = 0
4312 self.RecvSeqNum = 0
4313 self.KrbSessionKey = None
4314 self.STSessionKey = None
4315 self.IsAcceptor = IsAcceptor
4316 self.UPN = None
4317 self.PAC = None
4318 # [RFC 4121] sect 2
4319 if IsAcceptor:
4320 self.SendSealKeyUsage = 22
4321 self.SendSignKeyUsage = 23
4322 self.RecvSealKeyUsage = 24
4323 self.RecvSignKeyUsage = 25
4324 else:
4325 self.SendSealKeyUsage = 24
4326 self.SendSignKeyUsage = 25
4327 self.RecvSealKeyUsage = 22
4328 self.RecvSignKeyUsage = 23
4329 super(KerberosSSP.CONTEXT, self).__init__(req_flags=req_flags)
4330
4331 def clifailure(self):
4332 self.__init__(self.IsAcceptor, req_flags=self.flags)
4333
4334 def __repr__(self):
4335 if self.U2U:
4336 return "KerberosSSP-U2U"
4337 return "KerberosSSP"
4338
4339 def __init__(
4340 self,
4341 ST=None,
4342 UPN=None,
4343 PASSWORD=None,
4344 U2U=False,
4345 KEY=None,
4346 SPN=None,
4347 TGT=None,
4348 DC_IP=None,
4349 SKEY_TYPE=None,
4350 debug=0,
4351 **kwargs,
4352 ):
4353 self.ST = ST
4354 self.UPN = UPN
4355 self.KEY = KEY
4356 self.SPN = SPN
4357 self.TGT = TGT
4358 self.PASSWORD = PASSWORD
4359 self.U2U = U2U
4360 self.DC_IP = DC_IP
4361 self.debug = debug
4362 if SKEY_TYPE is None:
4363 from scapy.libs.rfc3961 import EncryptionType
4364
4365 SKEY_TYPE = EncryptionType.AES128_CTS_HMAC_SHA1_96
4366 self.SKEY_TYPE = SKEY_TYPE
4367 super(KerberosSSP, self).__init__(**kwargs)
4368
4369 def GSS_GetMICEx(self, Context, msgs, qop_req=0):
4370 """
4371 [MS-KILE] sect 3.4.5.6
4372
4373 - AES: RFC4121 sect 4.2.6.1
4374 """
4375 if Context.KrbSessionKey.etype in [17, 18]: # AES
4376 # Concatenate the ToSign
4377 ToSign = b"".join(x.data for x in msgs if x.sign)
4378 sig = KRB_InnerToken(
4379 TOK_ID=b"\x04\x04",
4380 root=KRB_GSS_MIC(
4381 Flags="AcceptorSubkey"
4382 + ("+SentByAcceptor" if Context.IsAcceptor else ""),
4383 SND_SEQ=Context.SendSeqNum,
4384 ),
4385 )
4386 ToSign += bytes(sig)[:16]
4387 sig.root.SGN_CKSUM = Context.KrbSessionKey.make_checksum(
4388 keyusage=Context.SendSignKeyUsage,
4389 text=ToSign,
4390 )
4391 else:
4392 raise NotImplementedError
4393 Context.SendSeqNum += 1
4394 return sig
4395
4396 def GSS_VerifyMICEx(self, Context, msgs, signature):
4397 """
4398 [MS-KILE] sect 3.4.5.7
4399
4400 - AES: RFC4121 sect 4.2.6.1
4401 """
4402 Context.RecvSeqNum = signature.root.SND_SEQ
4403 if Context.KrbSessionKey.etype in [17, 18]: # AES
4404 # Concatenate the ToSign
4405 ToSign = b"".join(x.data for x in msgs if x.sign)
4406 ToSign += bytes(signature)[:16]
4407 sig = Context.KrbSessionKey.make_checksum(
4408 keyusage=Context.RecvSignKeyUsage,
4409 text=ToSign,
4410 )
4411 else:
4412 raise NotImplementedError
4413 if sig != signature.root.SGN_CKSUM:
4414 raise ValueError("ERROR: Checksums don't match")
4415
4416 def GSS_WrapEx(self, Context, msgs, qop_req=0):
4417 """
4418 [MS-KILE] sect 3.4.5.4
4419
4420 - AES: RFC4121 sect 4.2.6.2 and [MS-KILE] sect 3.4.5.4.1
4421 - HMAC-RC4: RFC4757 sect 7.3 and [MS-KILE] sect 3.4.5.4.1
4422 """
4423 # Is confidentiality in use?
4424 confidentiality = (Context.flags & GSS_C_FLAGS.GSS_C_CONF_FLAG) and any(
4425 x.conf_req_flag for x in msgs
4426 )
4427 if Context.KrbSessionKey.etype in [17, 18]: # AES
4428 # Build token
4429 tok = KRB_InnerToken(
4430 TOK_ID=b"\x05\x04",
4431 root=KRB_GSS_Wrap(
4432 Flags="AcceptorSubkey"
4433 + ("+SentByAcceptor" if Context.IsAcceptor else "")
4434 + ("+Sealed" if confidentiality else ""),
4435 SND_SEQ=Context.SendSeqNum,
4436 RRC=0,
4437 ),
4438 )
4439 Context.SendSeqNum += 1
4440 # Real separation starts now: RFC4121 sect 4.2.4
4441 if confidentiality:
4442 # Confidentiality is requested (see RFC4121 sect 4.3)
4443 # {"header" | encrypt(plaintext-data | filler | "header")}
4444 # 0. Roll confounder
4445 Confounder = os.urandom(Context.KrbSessionKey.ep.blocksize)
4446 # 1. Concatenate the data to be encrypted
4447 Data = b"".join(x.data for x in msgs if x.conf_req_flag)
4448 DataLen = len(Data)
4449 # 2. Add filler
4450 # [MS-KILE] sect 3.4.5.4.1 - "For AES-SHA1 ciphers, the EC must not
4451 # be zero"
4452 tok.root.EC = ((-DataLen) % Context.KrbSessionKey.ep.blocksize) or 16
4453 Filler = b"\x00" * tok.root.EC
4454 Data += Filler
4455 # 3. Add first 16 octets of the Wrap token "header"
4456 PlainHeader = bytes(tok)[:16]
4457 Data += PlainHeader
4458 # 4. Build 'ToSign', exclusively used for checksum
4459 ToSign = Confounder
4460 ToSign += b"".join(x.data for x in msgs if x.sign)
4461 ToSign += Filler
4462 ToSign += PlainHeader
4463 # 5. Finalize token for signing
4464 # "The RRC field is [...] 28 if encryption is requested."
4465 tok.root.RRC = 28
4466 # 6. encrypt() is the encryption operation (which provides for
4467 # integrity protection)
4468 Data = Context.KrbSessionKey.encrypt(
4469 keyusage=Context.SendSealKeyUsage,
4470 plaintext=Data,
4471 confounder=Confounder,
4472 signtext=ToSign,
4473 )
4474 # 7. Rotate
4475 Data = strrot(Data, tok.root.RRC + tok.root.EC)
4476 # 8. Split (token and encrypted messages)
4477 toklen = len(Data) - DataLen
4478 tok.root.Data = Data[:toklen]
4479 offset = toklen
4480 for msg in msgs:
4481 msglen = len(msg.data)
4482 if msg.conf_req_flag:
4483 msg.data = Data[offset : offset + msglen]
4484 offset += msglen
4485 return msgs, tok
4486 else:
4487 # No confidentiality is requested
4488 # {"header" | plaintext-data | get_mic(plaintext-data | "header")}
4489 # 0. Concatenate the data
4490 Data = b"".join(x.data for x in msgs if x.sign)
4491 DataLen = len(Data)
4492 # 1. Add first 16 octets of the Wrap token "header"
4493 ToSign = Data
4494 ToSign += bytes(tok)[:16]
4495 # 2. get_mic() is the checksum operation for the required
4496 # checksum mechanism
4497 Mic = Context.KrbSessionKey.make_checksum(
4498 keyusage=Context.SendSealKeyUsage,
4499 text=ToSign,
4500 )
4501 # In Wrap tokens without confidentiality, the EC field SHALL be used
4502 # to encode the number of octets in the trailing checksum
4503 tok.root.EC = 12 # len(tok.root.Data) == 12 for AES
4504 # "The RRC field ([RFC4121] section 4.2.5) is 12 if no encryption
4505 # is requested"
4506 tok.root.RRC = 12
4507 # 3. Concat and pack
4508 for msg in msgs:
4509 if msg.sign:
4510 msg.data = b""
4511 Data = Data + Mic
4512 # 4. Rotate
4513 tok.root.Data = strrot(Data, tok.root.RRC)
4514 return msgs, tok
4515 elif Context.KrbSessionKey.etype in [23, 24]: # RC4
4516 from scapy.libs.rfc3961 import (
4517 Cipher,
4518 Hmac_MD5,
4519 _rfc1964pad,
4520 decrepit_algorithms,
4521 )
4522
4523 # Build token
4524 seq = struct.pack(">I", Context.SendSeqNum)
4525 tok = KRB_InnerToken(
4526 TOK_ID=b"\x02\x01",
4527 root=KRB_GSS_Wrap_RFC1964(
4528 SGN_ALG="HMAC",
4529 SEAL_ALG="RC4" if confidentiality else "none",
4530 SND_SEQ=seq
4531 + (
4532 # See errata
4533 b"\xff\xff\xff\xff"
4534 if Context.IsAcceptor
4535 else b"\x00\x00\x00\x00"
4536 ),
4537 ),
4538 )
4539 Context.SendSeqNum += 1
4540 # 0. Concatenate data
4541 ToSign = _rfc1964pad(b"".join(x.data for x in msgs if x.sign))
4542 ToEncrypt = b"".join(x.data for x in msgs if x.conf_req_flag)
4543 Kss = Context.KrbSessionKey.key
4544 # 1. Roll confounder
4545 Confounder = os.urandom(8)
4546 # 2. Compute the 'Kseq' key
4547 Klocal = strxor(Kss, len(Kss) * b"\xf0")
4548 if Context.KrbSessionKey.etype == 24: # EXP
4549 Kcrypt = Hmac_MD5(Klocal).digest(b"fortybits\x00" + b"\x00\x00\x00\x00")
4550 Kcrypt = Kcrypt[:7] + b"\xab" * 9
4551 else:
4552 Kcrypt = Hmac_MD5(Klocal).digest(b"\x00\x00\x00\x00")
4553 Kcrypt = Hmac_MD5(Kcrypt).digest(seq)
4554 # 3. Build SGN_CKSUM
4555 tok.root.SGN_CKSUM = Context.KrbSessionKey.make_checksum(
4556 keyusage=13, # See errata
4557 text=bytes(tok)[:8] + Confounder + ToSign,
4558 )[:8]
4559 # 4. Populate token + encrypt
4560 if confidentiality:
4561 # 'encrypt' is requested
4562 rc4 = Cipher(decrepit_algorithms.ARC4(Kcrypt), mode=None).encryptor()
4563 tok.root.CONFOUNDER = rc4.update(Confounder)
4564 Data = rc4.update(ToEncrypt)
4565 # Split encrypted data
4566 offset = 0
4567 for msg in msgs:
4568 msglen = len(msg.data)
4569 if msg.conf_req_flag:
4570 msg.data = Data[offset : offset + msglen]
4571 offset += msglen
4572 else:
4573 # 'encrypt' is not requested
4574 tok.root.CONFOUNDER = Confounder
4575 # 5. Compute the 'Kseq' key
4576 if Context.KrbSessionKey.etype == 24: # EXP
4577 Kseq = Hmac_MD5(Kss).digest(b"fortybits\x00" + b"\x00\x00\x00\x00")
4578 Kseq = Kseq[:7] + b"\xab" * 9
4579 else:
4580 Kseq = Hmac_MD5(Kss).digest(b"\x00\x00\x00\x00")
4581 Kseq = Hmac_MD5(Kseq).digest(tok.root.SGN_CKSUM)
4582 # 6. Encrypt 'SND_SEQ'
4583 rc4 = Cipher(decrepit_algorithms.ARC4(Kseq), mode=None).encryptor()
4584 tok.root.SND_SEQ = rc4.update(tok.root.SND_SEQ)
4585 # 7. Include 'InitialContextToken pseudo ASN.1 header'
4586 tok = KRB_GSSAPI_Token(
4587 MechType="1.2.840.113554.1.2.2", # Kerberos 5
4588 innerToken=tok,
4589 )
4590 return msgs, tok
4591 else:
4592 raise NotImplementedError
4593
4594 def GSS_UnwrapEx(self, Context, msgs, signature):
4595 """
4596 [MS-KILE] sect 3.4.5.5
4597
4598 - AES: RFC4121 sect 4.2.6.2
4599 - HMAC-RC4: RFC4757 sect 7.3
4600 """
4601 if Context.KrbSessionKey.etype in [17, 18]: # AES
4602 confidentiality = signature.root.Flags.Sealed
4603 # Real separation starts now: RFC4121 sect 4.2.4
4604 if confidentiality:
4605 # 0. Concatenate the data
4606 Data = signature.root.Data
4607 Data += b"".join(x.data for x in msgs if x.conf_req_flag)
4608 # 1. Un-Rotate
4609 Data = strrot(Data, signature.root.RRC + signature.root.EC, right=False)
4610
4611 # 2. Function to build 'ToSign', exclusively used for checksum
4612 def MakeToSign(Confounder, DecText):
4613 offset = 0
4614 # 2.a Confounder
4615 ToSign = Confounder
4616 # 2.b Messages
4617 for msg in msgs:
4618 msglen = len(msg.data)
4619 if msg.conf_req_flag:
4620 ToSign += DecText[offset : offset + msglen]
4621 offset += msglen
4622 elif msg.sign:
4623 ToSign += msg.data
4624 # 2.c Filler & Padding
4625 ToSign += DecText[offset:]
4626 return ToSign
4627
4628 # 3. Decrypt
4629 Data = Context.KrbSessionKey.decrypt(
4630 keyusage=Context.RecvSealKeyUsage,
4631 ciphertext=Data,
4632 presignfunc=MakeToSign,
4633 )
4634 # 4. Split
4635 Data, f16header = (
4636 Data[:-16],
4637 Data[-16:],
4638 )
4639 # 5. Check header
4640 hdr = signature.copy()
4641 hdr.root.RRC = 0
4642 if f16header != bytes(hdr)[:16]:
4643 raise ValueError("ERROR: Headers don't match")
4644 # 6. Split (and ignore filler)
4645 offset = 0
4646 for msg in msgs:
4647 msglen = len(msg.data)
4648 if msg.conf_req_flag:
4649 msg.data = Data[offset : offset + msglen]
4650 offset += msglen
4651 # Case without msgs
4652 if len(msgs) == 1 and not msgs[0].data:
4653 msgs[0].data = Data
4654 return msgs
4655 else:
4656 # No confidentiality is requested
4657 # 0. Concatenate the data
4658 Data = signature.root.Data
4659 Data += b"".join(x.data for x in msgs if x.sign)
4660 # 1. Un-Rotate
4661 Data = strrot(Data, signature.root.RRC, right=False)
4662 # 2. Split
4663 Data, Mic = Data[: -signature.root.EC], Data[-signature.root.EC :]
4664 # "Both the EC field and the RRC field in
4665 # the token header SHALL be filled with zeroes for the purpose of
4666 # calculating the checksum."
4667 ToSign = Data
4668 hdr = signature.copy()
4669 hdr.root.RRC = 0
4670 hdr.root.EC = 0
4671 # Concatenate the data
4672 ToSign += bytes(hdr)[:16]
4673 # 3. Calculate the signature
4674 sig = Context.KrbSessionKey.make_checksum(
4675 keyusage=Context.RecvSealKeyUsage,
4676 text=ToSign,
4677 )
4678 # 4. Compare
4679 if sig != Mic:
4680 raise ValueError("ERROR: Checksums don't match")
4681 # Case without msgs
4682 if len(msgs) == 1 and not msgs[0].data:
4683 msgs[0].data = Data
4684 return msgs
4685 elif Context.KrbSessionKey.etype in [23, 24]: # RC4
4686 from scapy.libs.rfc3961 import (
4687 Cipher,
4688 Hmac_MD5,
4689 _rfc1964pad,
4690 decrepit_algorithms,
4691 )
4692
4693 # Drop wrapping
4694 tok = signature.innerToken
4695
4696 # Detect confidentiality
4697 confidentiality = tok.root.SEAL_ALG != 0xFFFF
4698
4699 # 0. Concatenate data
4700 ToDecrypt = b"".join(x.data for x in msgs if x.conf_req_flag)
4701 Kss = Context.KrbSessionKey.key
4702 # 1. Compute the 'Kseq' key
4703 if Context.KrbSessionKey.etype == 24: # EXP
4704 Kseq = Hmac_MD5(Kss).digest(b"fortybits\x00" + b"\x00\x00\x00\x00")
4705 Kseq = Kseq[:7] + b"\xab" * 9
4706 else:
4707 Kseq = Hmac_MD5(Kss).digest(b"\x00\x00\x00\x00")
4708 Kseq = Hmac_MD5(Kseq).digest(tok.root.SGN_CKSUM)
4709 # 2. Decrypt 'SND_SEQ'
4710 rc4 = Cipher(decrepit_algorithms.ARC4(Kseq), mode=None).encryptor()
4711 seq = rc4.update(tok.root.SND_SEQ)[:4]
4712 # 3. Compute the 'Kcrypt' key
4713 Klocal = strxor(Kss, len(Kss) * b"\xf0")
4714 if Context.KrbSessionKey.etype == 24: # EXP
4715 Kcrypt = Hmac_MD5(Klocal).digest(b"fortybits\x00" + b"\x00\x00\x00\x00")
4716 Kcrypt = Kcrypt[:7] + b"\xab" * 9
4717 else:
4718 Kcrypt = Hmac_MD5(Klocal).digest(b"\x00\x00\x00\x00")
4719 Kcrypt = Hmac_MD5(Kcrypt).digest(seq)
4720 # 4. Decrypt
4721 if confidentiality:
4722 # 'encrypt' was requested
4723 rc4 = Cipher(decrepit_algorithms.ARC4(Kcrypt), mode=None).encryptor()
4724 Confounder = rc4.update(tok.root.CONFOUNDER)
4725 Data = rc4.update(ToDecrypt)
4726 # Split encrypted data
4727 offset = 0
4728 for msg in msgs:
4729 msglen = len(msg.data)
4730 if msg.conf_req_flag:
4731 msg.data = Data[offset : offset + msglen]
4732 offset += msglen
4733 else:
4734 # 'encrypt' was not requested
4735 Confounder = tok.root.CONFOUNDER
4736 # 5. Verify SGN_CKSUM
4737 ToSign = _rfc1964pad(b"".join(x.data for x in msgs if x.sign))
4738 Context.KrbSessionKey.verify_checksum(
4739 keyusage=13, # See errata
4740 text=bytes(tok)[:8] + Confounder + ToSign,
4741 cksum=tok.root.SGN_CKSUM,
4742 )
4743 return msgs
4744 else:
4745 raise NotImplementedError
4746
4747 def GSS_Init_sec_context(
4748 self,
4749 Context: CONTEXT,
4750 token=None,
4751 target_name: Optional[str] = None,
4752 req_flags: Optional[GSS_C_FLAGS] = None,
4753 chan_bindings: GssChannelBindings = GSS_C_NO_CHANNEL_BINDINGS,
4754 ):
4755 if Context is None:
4756 # New context
4757 Context = self.CONTEXT(IsAcceptor=False, req_flags=req_flags)
4758
4759 from scapy.libs.rfc3961 import Key
4760
4761 if Context.state == self.STATE.INIT and self.U2U:
4762 # U2U - Get TGT
4763 Context.state = self.STATE.CLI_SENT_TGTREQ
4764 return (
4765 Context,
4766 KRB_GSSAPI_Token(
4767 MechType="1.2.840.113554.1.2.2.3", # U2U
4768 innerToken=KRB_InnerToken(
4769 TOK_ID=b"\x04\x00",
4770 root=KRB_TGT_REQ(),
4771 ),
4772 ),
4773 GSS_S_CONTINUE_NEEDED,
4774 )
4775
4776 if Context.state in [self.STATE.INIT, self.STATE.CLI_SENT_TGTREQ]:
4777 if not self.UPN:
4778 raise ValueError("Missing UPN attribute")
4779 # Do we have a ST?
4780 if self.ST is None:
4781 # Client sends an AP-req
4782 if not self.SPN and not target_name:
4783 raise ValueError("Missing SPN/target_name attribute")
4784 additional_tickets = []
4785 if self.U2U:
4786 try:
4787 # GSSAPI / Kerberos
4788 tgt_rep = token.root.innerToken.root
4789 except AttributeError:
4790 try:
4791 # Kerberos
4792 tgt_rep = token.innerToken.root
4793 except AttributeError:
4794 return Context, None, GSS_S_DEFECTIVE_TOKEN
4795 if not isinstance(tgt_rep, KRB_TGT_REP):
4796 tgt_rep.show()
4797 raise ValueError("KerberosSSP: Unexpected token !")
4798 additional_tickets = [tgt_rep.ticket]
4799 if self.TGT is not None:
4800 if not self.KEY:
4801 raise ValueError("Cannot use TGT without the KEY")
4802 # Use TGT
4803 res = krb_tgs_req(
4804 upn=self.UPN,
4805 spn=self.SPN or target_name,
4806 ip=self.DC_IP,
4807 sessionkey=self.KEY,
4808 ticket=self.TGT,
4809 additional_tickets=additional_tickets,
4810 u2u=self.U2U,
4811 debug=self.debug,
4812 )
4813 else:
4814 # Ask for TGT then ST
4815 res = krb_as_and_tgs(
4816 upn=self.UPN,
4817 spn=self.SPN or target_name,
4818 ip=self.DC_IP,
4819 key=self.KEY,
4820 password=self.PASSWORD,
4821 additional_tickets=additional_tickets,
4822 u2u=self.U2U,
4823 debug=self.debug,
4824 )
4825 if not res:
4826 # Failed to retrieve the ticket
4827 return Context, None, GSS_S_FAILURE
4828 self.ST, self.KEY = res.tgsrep.ticket, res.sessionkey
4829 elif not self.KEY:
4830 raise ValueError("Must provide KEY with ST")
4831 Context.STSessionKey = self.KEY
4832
4833 # Save ServerHostname
4834 if len(self.ST.sname.nameString) == 2:
4835 Context.ServerHostname = self.ST.sname.nameString[1].val.decode()
4836
4837 # Build the KRB-AP
4838 apOptions = ASN1_BIT_STRING("000")
4839 if Context.flags & GSS_C_FLAGS.GSS_C_MUTUAL_FLAG:
4840 apOptions.set(2, "1") # mutual-required
4841 if self.U2U:
4842 apOptions.set(1, "1") # use-session-key
4843 Context.U2U = True
4844 ap_req = KRB_AP_REQ(
4845 apOptions=apOptions,
4846 ticket=self.ST,
4847 authenticator=EncryptedData(),
4848 )
4849
4850 # Get the current time
4851 now_time = datetime.now(timezone.utc).replace(microsecond=0)
4852 # Pick a random session key
4853 Context.KrbSessionKey = Key.new_random_key(
4854 self.SKEY_TYPE,
4855 )
4856
4857 # We use a random SendSeqNum
4858 Context.SendSeqNum = RandNum(0, 0x7FFFFFFF)._fix()
4859
4860 # Get the realm of the client
4861 _, crealm = _parse_upn(self.UPN)
4862
4863 # Build and encrypt the full KRB_Authenticator
4864 ap_req.authenticator.encrypt(
4865 Context.STSessionKey,
4866 KRB_Authenticator(
4867 crealm=crealm,
4868 cname=PrincipalName.fromUPN(self.UPN),
4869 # RFC 4121 checksum
4870 cksum=Checksum(
4871 cksumtype="KRB-AUTHENTICATOR",
4872 checksum=KRB_AuthenticatorChecksum(
4873 # RFC 4121 sect 4.1.1.2
4874 # "The Bnd field contains the MD5 hash of channel bindings"
4875 Bnd=(
4876 chan_bindings.digestMD5()
4877 if chan_bindings != GSS_C_NO_CHANNEL_BINDINGS
4878 else (b"\x00" * 16)
4879 ),
4880 Flags=int(Context.flags),
4881 ),
4882 ),
4883 ctime=ASN1_GENERALIZED_TIME(now_time),
4884 cusec=ASN1_INTEGER(0),
4885 subkey=EncryptionKey.fromKey(Context.KrbSessionKey),
4886 seqNumber=Context.SendSeqNum,
4887 encAuthorizationData=AuthorizationData(
4888 seq=[
4889 AuthorizationDataItem(
4890 adType="AD-IF-RELEVANT",
4891 adData=AuthorizationData(
4892 seq=[
4893 AuthorizationDataItem(
4894 adType="KERB-AUTH-DATA-TOKEN-RESTRICTIONS",
4895 adData=KERB_AD_RESTRICTION_ENTRY(
4896 restriction=LSAP_TOKEN_INFO_INTEGRITY(
4897 MachineID=bytes(RandBin(32)),
4898 PermanentMachineID=bytes(RandBin(32)), # noqa: E501
4899 )
4900 ),
4901 ),
4902 # This isn't documented, but sent on Windows :/
4903 AuthorizationDataItem(
4904 adType="KERB-LOCAL",
4905 adData=b"\x00" * 16,
4906 ),
4907 ]
4908 + (
4909 # Channel bindings
4910 [
4911 AuthorizationDataItem(
4912 adType="AD-AUTH-DATA-AP-OPTIONS",
4913 adData=KERB_AUTH_DATA_AP_OPTIONS(
4914 apOptions="KERB_AP_OPTIONS_CBT"
4915 ),
4916 )
4917 ]
4918 if chan_bindings != GSS_C_NO_CHANNEL_BINDINGS
4919 else []
4920 )
4921 ),
4922 )
4923 ]
4924 ),
4925 ),
4926 )
4927 Context.state = self.STATE.CLI_SENT_APREQ
4928 if Context.flags & GSS_C_FLAGS.GSS_C_DCE_STYLE:
4929 # Raw kerberos DCE-STYLE
4930 return Context, ap_req, GSS_S_CONTINUE_NEEDED
4931 else:
4932 # Kerberos wrapper
4933 return (
4934 Context,
4935 KRB_GSSAPI_Token(
4936 innerToken=KRB_InnerToken(
4937 root=ap_req,
4938 )
4939 ),
4940 GSS_S_CONTINUE_NEEDED,
4941 )
4942
4943 elif Context.state == self.STATE.CLI_SENT_APREQ:
4944 if isinstance(token, KRB_AP_REP):
4945 # Raw AP_REP was passed
4946 ap_rep = token
4947 else:
4948 try:
4949 # GSSAPI / Kerberos
4950 ap_rep = token.root.innerToken.root
4951 except AttributeError:
4952 try:
4953 # Kerberos
4954 ap_rep = token.innerToken.root
4955 except AttributeError:
4956 try:
4957 # Raw kerberos DCE-STYLE
4958 ap_rep = token.root
4959 except AttributeError:
4960 return Context, None, GSS_S_DEFECTIVE_TOKEN
4961 if not isinstance(ap_rep, KRB_AP_REP):
4962 return Context, None, GSS_S_DEFECTIVE_TOKEN
4963 # Retrieve SessionKey
4964 repPart = ap_rep.encPart.decrypt(Context.STSessionKey)
4965 if repPart.subkey is not None:
4966 Context.SessionKey = repPart.subkey.keyvalue.val
4967 Context.KrbSessionKey = repPart.subkey.toKey()
4968 # OK !
4969 Context.state = self.STATE.CLI_RCVD_APREP
4970 if Context.flags & GSS_C_FLAGS.GSS_C_DCE_STYLE:
4971 # [MS-KILE] sect 3.4.5.1
4972 # The client MUST generate an additional AP exchange reply message
4973 # exactly as the server would as the final message to send to the
4974 # server.
4975 now_time = datetime.now(timezone.utc).replace(microsecond=0)
4976 cli_ap_rep = KRB_AP_REP(encPart=EncryptedData())
4977 cli_ap_rep.encPart.encrypt(
4978 Context.STSessionKey,
4979 EncAPRepPart(
4980 ctime=ASN1_GENERALIZED_TIME(now_time),
4981 seqNumber=repPart.seqNumber,
4982 subkey=None,
4983 ),
4984 )
4985 return Context, cli_ap_rep, GSS_S_COMPLETE
4986 return Context, None, GSS_S_COMPLETE
4987 elif (
4988 Context.state == self.STATE.CLI_RCVD_APREP
4989 and Context.flags & GSS_C_FLAGS.GSS_C_DCE_STYLE
4990 ):
4991 # DCE_STYLE with SPNEGOSSP
4992 return Context, None, GSS_S_COMPLETE
4993 else:
4994 raise ValueError("KerberosSSP: Unknown state")
4995
4996 def GSS_Accept_sec_context(
4997 self,
4998 Context: CONTEXT,
4999 token=None,
5000 req_flags: Optional[GSS_S_FLAGS] = GSS_S_FLAGS.GSS_S_ALLOW_MISSING_BINDINGS,
5001 chan_bindings: GssChannelBindings = GSS_C_NO_CHANNEL_BINDINGS,
5002 ):
5003 if Context is None:
5004 # New context
5005 Context = self.CONTEXT(IsAcceptor=True, req_flags=req_flags)
5006
5007 from scapy.libs.rfc3961 import Key
5008 import scapy.layers.msrpce.mspac # noqa: F401
5009
5010 if Context.state == self.STATE.INIT:
5011 if self.UPN and self.SPN:
5012 raise ValueError("Cannot use SPN and UPN at the same time !")
5013 if self.SPN and self.TGT:
5014 raise ValueError("Cannot use TGT with SPN.")
5015 if self.UPN and not self.TGT:
5016 # UPN is provided: use U2U
5017 res = krb_as_req(
5018 self.UPN,
5019 self.DC_IP,
5020 key=self.KEY,
5021 password=self.PASSWORD,
5022 )
5023 self.TGT, self.KEY = res.asrep.ticket, res.sessionkey
5024
5025 # Server receives AP-req, sends AP-rep
5026 if isinstance(token, KRB_AP_REQ):
5027 # Raw AP_REQ was passed
5028 ap_req = token
5029 else:
5030 try:
5031 # GSSAPI/Kerberos
5032 ap_req = token.root.innerToken.root
5033 except AttributeError:
5034 try:
5035 # Kerberos
5036 ap_req = token.innerToken.root
5037 except AttributeError:
5038 try:
5039 # Raw kerberos
5040 ap_req = token.root
5041 except AttributeError:
5042 return Context, None, GSS_S_DEFECTIVE_TOKEN
5043
5044 if isinstance(ap_req, KRB_TGT_REQ):
5045 # Special U2U case
5046 Context.U2U = True
5047 return (
5048 None,
5049 KRB_GSSAPI_Token(
5050 MechType="1.2.840.113554.1.2.2.3", # U2U
5051 innerToken=KRB_InnerToken(
5052 TOK_ID=b"\x04\x01",
5053 root=KRB_TGT_REP(
5054 ticket=self.TGT,
5055 ),
5056 ),
5057 ),
5058 GSS_S_CONTINUE_NEEDED,
5059 )
5060 elif not isinstance(ap_req, KRB_AP_REQ):
5061 ap_req.show()
5062 raise ValueError("Unexpected type in KerberosSSP")
5063 if not self.KEY:
5064 raise ValueError("Missing KEY attribute")
5065
5066 now_time = datetime.now(timezone.utc).replace(microsecond=0)
5067
5068 # If using a UPN, require U2U
5069 if self.UPN and ap_req.apOptions.val[1] != "1": # use-session-key
5070 # Required but not provided. Return an error
5071 Context.U2U = True
5072 err = KRB_GSSAPI_Token(
5073 innerToken=KRB_InnerToken(
5074 TOK_ID=b"\x03\x00",
5075 root=KRB_ERROR(
5076 errorCode="KRB_AP_ERR_USER_TO_USER_REQUIRED",
5077 stime=ASN1_GENERALIZED_TIME(now_time),
5078 realm=ap_req.ticket.realm,
5079 sname=ap_req.ticket.sname,
5080 eData=KRB_TGT_REP(
5081 ticket=self.TGT,
5082 ),
5083 ),
5084 )
5085 )
5086 return Context, err, GSS_S_CONTINUE_NEEDED
5087
5088 # Validate the 'serverName' of the ticket.
5089 sname = ap_req.ticket.getSPN()
5090 our_sname = self.SPN or self.UPN
5091 if not _spn_are_equal(our_sname, sname):
5092 warning("KerberosSSP: bad server name: %s != %s" % (sname, our_sname))
5093 err = KRB_GSSAPI_Token(
5094 innerToken=KRB_InnerToken(
5095 TOK_ID=b"\x03\x00",
5096 root=KRB_ERROR(
5097 errorCode="KRB_AP_ERR_BADMATCH",
5098 stime=ASN1_GENERALIZED_TIME(now_time),
5099 realm=ap_req.ticket.realm,
5100 sname=ap_req.ticket.sname,
5101 eData=None,
5102 ),
5103 )
5104 )
5105 return Context, err, GSS_S_BAD_MECH
5106
5107 # Decrypt the ticket
5108 try:
5109 tkt = ap_req.ticket.encPart.decrypt(self.KEY)
5110 except ValueError as ex:
5111 warning("KerberosSSP: %s (bad KEY?)" % ex)
5112 err = KRB_GSSAPI_Token(
5113 innerToken=KRB_InnerToken(
5114 TOK_ID=b"\x03\x00",
5115 root=KRB_ERROR(
5116 errorCode="KRB_AP_ERR_MODIFIED",
5117 stime=ASN1_GENERALIZED_TIME(now_time),
5118 realm=ap_req.ticket.realm,
5119 sname=ap_req.ticket.sname,
5120 eData=None,
5121 ),
5122 )
5123 )
5124 return Context, err, GSS_S_DEFECTIVE_TOKEN
5125
5126 # Store information about the user in the Context
5127 if tkt.authorizationData and tkt.authorizationData.seq:
5128 # Get AD-IF-RELEVANT
5129 adIfRelevant = tkt.authorizationData.getAuthData(0x1)
5130 if adIfRelevant:
5131 # Get AD-WIN2K-PAC
5132 Context.PAC = adIfRelevant.getAuthData(0x80)
5133
5134 # Get AP-REQ session key
5135 Context.STSessionKey = tkt.key.toKey()
5136 authenticator = ap_req.authenticator.decrypt(Context.STSessionKey)
5137
5138 # Compute an application session key ([MS-KILE] sect 3.1.1.2)
5139 subkey = None
5140 if ap_req.apOptions.val[2] == "1": # mutual-required
5141 appkey = Key.new_random_key(
5142 self.SKEY_TYPE,
5143 )
5144 Context.KrbSessionKey = appkey
5145 Context.SessionKey = appkey.key
5146 subkey = EncryptionKey.fromKey(appkey)
5147 else:
5148 Context.KrbSessionKey = self.KEY
5149 Context.SessionKey = self.KEY.key
5150
5151 # Eventually process the "checksum"
5152 if authenticator.cksum and authenticator.cksum.cksumtype == 0x8003:
5153 # KRB-Authenticator
5154 authcksum = authenticator.cksum.checksum
5155 Context.flags = authcksum.Flags
5156 # Check channel bindings
5157 if (
5158 chan_bindings != GSS_C_NO_CHANNEL_BINDINGS
5159 and chan_bindings.digestMD5() != authcksum.Bnd
5160 and not (
5161 GSS_S_FLAGS.GSS_S_ALLOW_MISSING_BINDINGS in req_flags
5162 and authcksum.Bnd == GSS_C_NO_CHANNEL_BINDINGS
5163 )
5164 ):
5165 # Channel binding checks failed.
5166 return Context, None, GSS_S_BAD_BINDINGS
5167 elif (
5168 chan_bindings != GSS_C_NO_CHANNEL_BINDINGS
5169 and GSS_S_FLAGS.GSS_S_ALLOW_MISSING_BINDINGS not in req_flags
5170 ):
5171 # Uhoh, we required channel bindings
5172 return Context, None, GSS_S_BAD_BINDINGS
5173
5174 # Build response (RFC4120 sect 3.2.4)
5175 ap_rep = KRB_AP_REP(encPart=EncryptedData())
5176 ap_rep.encPart.encrypt(
5177 Context.STSessionKey,
5178 EncAPRepPart(
5179 ctime=authenticator.ctime,
5180 cusec=authenticator.cusec,
5181 seqNumber=None,
5182 subkey=subkey,
5183 ),
5184 )
5185 Context.state = self.STATE.SRV_SENT_APREP
5186 if Context.flags & GSS_C_FLAGS.GSS_C_DCE_STYLE:
5187 # [MS-KILE] sect 3.4.5.1
5188 return Context, ap_rep, GSS_S_CONTINUE_NEEDED
5189 return Context, ap_rep, GSS_S_COMPLETE # success
5190 elif (
5191 Context.state == self.STATE.SRV_SENT_APREP
5192 and Context.flags & GSS_C_FLAGS.GSS_C_DCE_STYLE
5193 ):
5194 # [MS-KILE] sect 3.4.5.1
5195 # The server MUST receive the additional AP exchange reply message and
5196 # verify that the message is constructed correctly.
5197 if not token:
5198 return Context, None, GSS_S_DEFECTIVE_TOKEN
5199 # Server receives AP-req, sends AP-rep
5200 if isinstance(token, KRB_AP_REP):
5201 # Raw AP_REP was passed
5202 ap_rep = token
5203 else:
5204 try:
5205 # GSSAPI/Kerberos
5206 ap_rep = token.root.innerToken.root
5207 except AttributeError:
5208 try:
5209 # Raw Kerberos
5210 ap_rep = token.root
5211 except AttributeError:
5212 return Context, None, GSS_S_DEFECTIVE_TOKEN
5213 # Decrypt the AP-REP
5214 try:
5215 ap_rep.encPart.decrypt(Context.STSessionKey)
5216 except ValueError as ex:
5217 warning("KerberosSSP: %s (bad KEY?)" % ex)
5218 return Context, None, GSS_S_DEFECTIVE_TOKEN
5219 return Context, None, GSS_S_COMPLETE # success
5220 else:
5221 raise ValueError("KerberosSSP: Unknown state %s" % repr(Context.state))
5222
5223 def GSS_Passive(
5224 self,
5225 Context: CONTEXT,
5226 token=None,
5227 req_flags: Optional[GSS_S_FLAGS] = GSS_S_FLAGS.GSS_S_ALLOW_MISSING_BINDINGS,
5228 ):
5229 if Context is None:
5230 Context = self.CONTEXT(True)
5231 Context.passive = True
5232
5233 if Context.state == self.STATE.INIT or (
5234 # In DCE/RPC, there's an extra AP-REP sent from the client.
5235 Context.state == self.STATE.SRV_SENT_APREP
5236 and req_flags & GSS_C_FLAGS.GSS_C_DCE_STYLE
5237 ):
5238 Context, _, status = self.GSS_Accept_sec_context(
5239 Context, token, req_flags=req_flags
5240 )
5241 if status in [GSS_S_CONTINUE_NEEDED, GSS_S_COMPLETE]:
5242 Context.state = self.STATE.CLI_SENT_APREQ
5243 else:
5244 Context.state = self.STATE.FAILED
5245 return Context, status
5246 elif Context.state == self.STATE.CLI_SENT_APREQ:
5247 Context, _, status = self.GSS_Init_sec_context(
5248 Context, token, req_flags=req_flags
5249 )
5250 if status == GSS_S_COMPLETE:
5251 Context.state = self.STATE.SRV_SENT_APREP
5252 else:
5253 Context.state == self.STATE.FAILED
5254 return Context, status
5255
5256 # Unknown state. Don't crash though.
5257 return Context, GSS_S_FAILURE
5258
5259 def GSS_Passive_set_Direction(self, Context: CONTEXT, IsAcceptor=False):
5260 if Context.IsAcceptor is not IsAcceptor:
5261 return
5262 # Swap everything
5263 Context.SendSealKeyUsage, Context.RecvSealKeyUsage = (
5264 Context.RecvSealKeyUsage,
5265 Context.SendSealKeyUsage,
5266 )
5267 Context.SendSignKeyUsage, Context.RecvSignKeyUsage = (
5268 Context.RecvSignKeyUsage,
5269 Context.SendSignKeyUsage,
5270 )
5271 Context.IsAcceptor = not Context.IsAcceptor
5272
5273 def LegsAmount(self, Context: CONTEXT):
5274 if Context.flags & GSS_C_FLAGS.GSS_C_DCE_STYLE:
5275 return 4
5276 else:
5277 return 2
5278
5279 def MaximumSignatureLength(self, Context: CONTEXT):
5280 if Context.flags & GSS_C_FLAGS.GSS_C_CONF_FLAG:
5281 # TODO: support DES
5282 if Context.KrbSessionKey.etype in [17, 18]: # AES
5283 return 76
5284 elif Context.KrbSessionKey.etype in [23, 24]: # RC4_HMAC
5285 return 45
5286 else:
5287 raise NotImplementedError
5288 else:
5289 return 28
5290
5291 def canMechListMIC(self, Context: CONTEXT):
5292 return bool(Context.KrbSessionKey)