1818 PADATA_TYPE , PA_PAC_REQUEST , PA_ENC_TS_ENC , EncryptedData , krb5_pvno , KDC_REQ_BODY , \
1919 AS_REQ , TGS_REP , KDCOptions , PrincipalName , EncASRepPart , EncTGSRepPart , PrincipalName , Realm , \
2020 Checksum , APOptions , Authenticator , Ticket , AP_REQ , TGS_REQ , CKSUMTYPE , \
21- PA_FOR_USER_ENC , PA_PAC_OPTIONS , PA_PAC_OPTIONSTypes
21+ PA_FOR_USER_ENC , PA_PAC_OPTIONS , PA_PAC_OPTIONSTypes , EncTicketPart , AD_IF_RELEVANT
2222
2323from minikerberos .protocol .errors import KerberosErrorCode , KerberosError
2424from minikerberos .protocol .encryption import Key , _enctype_table , _HMACMD5 , Enctype , _checksum_table
@@ -40,6 +40,7 @@ def __init__(self, ccred:KerberosCredential, target:KerberosTarget):
4040 self .target = target
4141 self .ksoc = AIOKerberosClientSocket (self .target )
4242 self .ccache = CCACHE () if self .usercreds .ccache is None else self .usercreds .ccache
43+ self .pkinit_tkey = None
4344 self .kerberos_session_key = None
4445 self .kerberos_TGT = None
4546 self .kerberos_TGT_encpart = None
@@ -465,6 +466,78 @@ async def get_TGS(self, spn_user, override_etype = None, is_linux = False):
465466 logger .debug ('Got valid TGS reply' )
466467 self .kerberos_TGS = tgs
467468 return tgs , encTGSRepPart , key
469+
470+ async def U2U (self , kdcopts = ['forwardable' ,'renewable' ,'canonicalize' , 'enc-tkt-in-skey' ], supp_enc_methods = [EncryptionType .DES_CBC_CRC ,EncryptionType .DES_CBC_MD4 ,EncryptionType .DES_CBC_MD5 ,EncryptionType .DES3_CBC_SHA1 ,EncryptionType .ARCFOUR_HMAC_MD5 ,EncryptionType .AES256_CTS_HMAC_SHA1_96 ,EncryptionType .AES128_CTS_HMAC_SHA1_96 ]):
471+ if not self .kerberos_TGT :
472+ logger .debug ('[U2U] TGT is not available! Fetching TGT...' )
473+ await self .get_TGT ()
474+
475+ supp_enc = self .usercreds .get_preferred_enctype (supp_enc_methods )
476+ now = datetime .datetime .now (datetime .timezone .utc )
477+ authenticator_data = {}
478+ authenticator_data ['authenticator-vno' ] = krb5_pvno
479+ authenticator_data ['crealm' ] = Realm (self .kerberos_TGT ['crealm' ])
480+ authenticator_data ['cname' ] = self .kerberos_TGT ['cname' ]
481+ authenticator_data ['cusec' ] = now .microsecond
482+ authenticator_data ['ctime' ] = now .replace (microsecond = 0 )
483+
484+
485+ authenticator_data_enc = self .kerberos_cipher .encrypt (self .kerberos_session_key , 7 , Authenticator (authenticator_data ).dump (), None )
486+
487+ ap_req = {}
488+ ap_req ['pvno' ] = krb5_pvno
489+ ap_req ['msg-type' ] = MESSAGE_TYPE .KRB_AP_REQ .value
490+ ap_req ['ap-options' ] = APOptions (set ())
491+ ap_req ['ticket' ] = Ticket (self .kerberos_TGT ['ticket' ])
492+ ap_req ['authenticator' ] = EncryptedData ({'etype' : self .kerberos_cipher_type , 'cipher' : authenticator_data_enc })
493+
494+ pa_data_auth = {}
495+ pa_data_auth ['padata-type' ] = PaDataType .TGS_REQ .value
496+ pa_data_auth ['padata-value' ] = AP_REQ (ap_req ).dump ()
497+
498+
499+ krb_tgs_body = {}
500+ krb_tgs_body ['kdc-options' ] = KDCOptions (set (kdcopts ))
501+ krb_tgs_body ['sname' ] = PrincipalName ({'name-type' : NAME_TYPE .PRINCIPAL .value , 'name-string' : [self .usercreds .username ]})
502+ krb_tgs_body ['realm' ] = self .usercreds .domain .upper ()
503+ krb_tgs_body ['till' ] = (now + datetime .timedelta (days = 1 )).replace (microsecond = 0 )
504+ krb_tgs_body ['nonce' ] = secrets .randbits (31 )
505+ krb_tgs_body ['etype' ] = [23 ] # dunno why it must be 23?
506+ krb_tgs_body ['additional-tickets' ] = [Ticket (self .kerberos_TGT ['ticket' ])]
507+
508+
509+ krb_tgs_req = {}
510+ krb_tgs_req ['pvno' ] = krb5_pvno
511+ krb_tgs_req ['msg-type' ] = MESSAGE_TYPE .KRB_TGS_REQ .value
512+ krb_tgs_req ['padata' ] = [pa_data_auth ] #pa_for_user
513+ krb_tgs_req ['req-body' ] = KDC_REQ_BODY (krb_tgs_body )
514+
515+
516+
517+ req = TGS_REQ (krb_tgs_req )
518+ logger .debug ('[U2U] Sending request to server' )
519+
520+ reply = await self .ksoc .sendrecv (req .dump ())
521+ if reply .name == 'KRB_ERROR' :
522+ emsg = '[U2U] failed!'
523+ if reply .native ['error-code' ] == 16 :
524+ emsg = '[U2U] Failed to get U2U! Error code (16) indicates that delegation is not enabled for this account!'
525+ raise KerberosError (reply , emsg )
526+
527+ logger .debug ('[U2U] Got reply, decrypting...' )
528+ tgs = reply .native
529+
530+ cipher = _enctype_table [int (tgs ['ticket' ]['enc-part' ]['etype' ])]
531+ encticket = tgs ['ticket' ]['enc-part' ]['cipher' ]
532+ decdata = cipher .decrypt (self .kerberos_session_key , 2 , encticket )
533+ decticket = EncTicketPart .load (decdata ).native
534+
535+ encTGSRepPart = EncTGSRepPart .load (self .kerberos_cipher .decrypt (self .kerberos_session_key , 8 , tgs ['enc-part' ]['cipher' ])).native
536+ key = Key (encTGSRepPart ['key' ]['keytype' ], encTGSRepPart ['key' ]['keyvalue' ])
537+ self .ccache .add_tgs (tgs , encTGSRepPart )
538+ logger .debug ('[U2U] Got valid TGS reply' )
539+
540+ return tgs , encTGSRepPart , key , decticket
468541
469542 #https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-sfu/6a8dfc0c-2d32-478a-929f-5f9b1b18a169
470543 async def S4U2self (self , user_to_impersonate , spn_user = None , kdcopts = ['forwardable' ,'renewable' ,'canonicalize' ], supp_enc_methods = [EncryptionType .DES_CBC_CRC ,EncryptionType .DES_CBC_MD4 ,EncryptionType .DES_CBC_MD5 ,EncryptionType .DES3_CBC_SHA1 ,EncryptionType .ARCFOUR_HMAC_MD5 ,EncryptionType .AES256_CTS_HMAC_SHA1_96 ,EncryptionType .AES128_CTS_HMAC_SHA1_96 ]):
@@ -474,7 +547,7 @@ async def S4U2self(self, user_to_impersonate, spn_user = None, kdcopts = ['forwa
474547
475548 if not self .kerberos_TGT :
476549 logger .debug ('[S4U2self] TGT is not available! Fetching TGT...' )
477- self .get_TGT ()
550+ await self .get_TGT ()
478551
479552 supp_enc = self .usercreds .get_preferred_enctype (supp_enc_methods )
480553 auth_package_name = 'Kerberos'
@@ -498,6 +571,7 @@ async def S4U2self(self, user_to_impersonate, spn_user = None, kdcopts = ['forwa
498571 ap_req ['ticket' ] = Ticket (self .kerberos_TGT ['ticket' ])
499572 ap_req ['authenticator' ] = EncryptedData ({'etype' : self .kerberos_cipher_type , 'cipher' : authenticator_data_enc })
500573
574+
501575 pa_data_auth = {}
502576 pa_data_auth ['padata-type' ] = PaDataType .TGS_REQ .value
503577 pa_data_auth ['padata-value' ] = AP_REQ (ap_req ).dump ()
@@ -770,18 +844,58 @@ def truncate_key(value, keysize):
770844 etype = as_rep ['enc-part' ]['etype' ]
771845 cipher = _enctype_table [etype ]
772846 if etype == Enctype .AES256 :
773- t_key = truncate_key (fullKey , 32 )
847+ self . pkinit_tkey = truncate_key (fullKey , 32 )
774848 elif etype == Enctype .AES128 :
775- t_key = truncate_key (fullKey , 16 )
849+ self . pkinit_tkey = truncate_key (fullKey , 16 )
776850 elif etype == Enctype .RC4 :
777851 raise NotImplementedError ('RC4 key truncation documentation missing. it is different from AES' )
778- #t_key = truncate_key(fullKey, 16)
852+ #self.pkinit_tkey = truncate_key(fullKey, 16)
779853
780854
781- key = Key (cipher .enctype , t_key )
855+ key = Key (cipher .enctype , self . pkinit_tkey )
782856 enc_data = as_rep ['enc-part' ]['cipher' ]
783857 dec_data = cipher .decrypt (key , 3 , enc_data )
784858 encasrep = EncASRepPart .load (dec_data ).native
785859 cipher = _enctype_table [ int (encasrep ['key' ]['keytype' ])]
786860 session_key = Key (cipher .enctype , encasrep ['key' ]['keyvalue' ])
787- return encasrep , session_key , cipher
861+ return encasrep , session_key , cipher
862+
863+
864+ def get_NT_from_PAC (self , decticket :EncTicketPart , truncated_keydata = None ):
865+ from minikerberos .protocol .external .rpcrt import TypeSerialization1
866+ from minikerberos .protocol .external .pac import PACTYPE , PAC_INFO_BUFFER , \
867+ PAC_CREDENTIAL_INFO , PAC_CREDENTIAL_DATA , NTLM_SUPPLEMENTAL_CREDENTIAL
868+
869+
870+ adIfRelevant = AD_IF_RELEVANT .load (decticket ['authorization-data' ][0 ]['ad-data' ])
871+ if truncated_keydata is None :
872+ truncated_keydata = self .pkinit_tkey
873+ if truncated_keydata is None :
874+ raise Exception ("Missing tkey! Is this a PKINIT session?" )
875+ key = Key (18 , truncated_keydata )
876+ pacType = PACTYPE (adIfRelevant .native [0 ]['ad-data' ])
877+ buff = pacType ['Buffers' ]
878+ creds = []
879+ for bufferN in range (pacType ['cBuffers' ]):
880+ infoBuffer = PAC_INFO_BUFFER (buff )
881+ data = pacType ['Buffers' ][infoBuffer ['Offset' ]- 8 :][:infoBuffer ['cbBufferSize' ]]
882+ logger .debug ("TYPE 0x%x" % infoBuffer ['ulType' ])
883+ if infoBuffer ['ulType' ] == 2 :
884+ credinfo = PAC_CREDENTIAL_INFO (data )
885+ newCipher = _enctype_table [credinfo ['EncryptionType' ]]
886+
887+ out = newCipher .decrypt (key , 16 , credinfo ['SerializedData' ])
888+ type1 = TypeSerialization1 (out )
889+ # I'm skipping here 4 bytes with its the ReferentID for the pointer
890+ newdata = out [len (type1 )+ 4 :]
891+ pcc = PAC_CREDENTIAL_DATA (newdata )
892+ for cred in pcc ['Credentials' ]:
893+ credstruct = NTLM_SUPPLEMENTAL_CREDENTIAL (b'' .join (cred ['Credentials' ]))
894+ if credstruct ['NtPassword' ] != b'\x00 ' * 16 :
895+ creds .append (('NT' , credstruct ['NtPassword' ].hex ()))
896+ if credstruct ['LmPassword' ] != b'\x00 ' * 16 :
897+ creds .append (('LM' , credstruct ['LmPassword' ].hex ()))
898+
899+ buff = buff [len (infoBuffer ):]
900+
901+ return creds
0 commit comments