2222 NL_AUTH_MESSAGE ,
2323 NL_AUTH_SIGNATURE ,
2424)
25+ from scapy .layers .kerberos import KerberosSSP , _parse_upn
2526from scapy .layers .gssapi import (
2627 GSS_C_FLAGS ,
2728 GSS_C_NO_CHANNEL_BINDINGS ,
2829 GSS_S_COMPLETE ,
2930 GSS_S_CONTINUE_NEEDED ,
3031 GSS_S_FAILURE ,
3132 GSS_S_FLAGS ,
33+ SSP ,
3234)
33- from scapy .layers .ntlm import RC4 , RC4K , RC4Init , SSP
35+ from scapy .layers .ntlm import RC4 , RC4K , RC4Init , MD4le
3436
3537from scapy .layers .msrpce .rpcclient import (
3638 DCERPC_Client ,
4042from scapy .layers .msrpce .raw .ms_nrpc import (
4143 NetrServerAuthenticate3_Request ,
4244 NetrServerAuthenticate3_Response ,
45+ NetrServerAuthenticateKerberos_Request ,
46+ NetrServerAuthenticateKerberos_Response ,
4347 NetrServerReqChallenge_Request ,
4448 NetrServerReqChallenge_Response ,
4549 NETLOGON_SECURE_CHANNEL_TYPE ,
114118 0x00200000 : "RODC-passthrough" ,
115119 # W: Supports Advanced Encryption Standard (AES) encryption and SHA2 hashing.
116120 0x01000000 : "AES" ,
117- # Supports Kerberos as the security support provider for secure channel setup .
118- 0x20000000 : "Kerberos " ,
121+ # Not used. MUST be ignored on receipt .
122+ 0x20000000 : "X " ,
119123 # Y: Supports Secure RPC.
120124 0x40000000 : "SecureRPC" ,
121- # Not used. MUST be ignored on receipt .
122- 0x80000000 : "Z " ,
125+ # Supports Kerberos as the security support provider for secure channel setup .
126+ 0x80000000 : "Kerberos " ,
123127}
124128_negotiateFlags = FlagsField ("" , 0 , - 32 , _negotiateFlags ).names
125129
130+ # -- CRYPTO
131+
126132
127133# [MS-NRPC] sect 3.1.4.3.1
128134@crypto_validator
@@ -569,8 +575,8 @@ class NetlogonClient(DCERPC_Client):
569575 >>> cli = NetlogonClient()
570576 >>> cli.connect_and_bind("192.168.0.100")
571577 >>> cli.establish_secure_channel(
572- ... domainname="DOMAIN", computername=" WIN10",
573- ... HashNT =bytes.fromhex("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
578+ ... UPN=" WIN10@DOMAIN ",
579+ ... HASHNT =bytes.fromhex("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
574580 ... )
575581 """
576582
@@ -583,26 +589,25 @@ def __init__(
583589 ** kwargs ,
584590 ):
585591 self .interface = find_dcerpc_interface ("logon" )
586- self .ndr64 = False # Netlogon doesn't work with NDR64
587592 self .SessionKey = None
588593 self .ClientStoredCredential = None
589594 self .supportAES = supportAES
590595 super (NetlogonClient , self ).__init__ (
591596 DCERPC_Transport .NCACN_IP_TCP ,
592597 auth_level = auth_level ,
593- ndr64 = self .ndr64 ,
594598 verb = verb ,
595599 ** kwargs ,
596600 )
597601
598- def connect_and_bind (self , remoteIP ):
602+ def connect (self , host , ** kwargs ):
599603 """
600604 This calls DCERPC_Client's connect_and_bind to bind the 'logon' interface.
601605 """
602- super (NetlogonClient , self ).connect_and_bind (remoteIP , self .interface )
603-
604- def alter_context (self ):
605- return super (NetlogonClient , self ).alter_context (self .interface )
606+ super (NetlogonClient , self ).connect (
607+ host = host ,
608+ interface = self .interface ,
609+ ** kwargs ,
610+ )
606611
607612 def create_authenticator (self ):
608613 """
@@ -653,9 +658,12 @@ def validate_authenticator(self, auth):
653658
654659 def establish_secure_channel (
655660 self ,
656- computername : str ,
657- domainname : str ,
658- HashNt : bytes ,
661+ UPN : str ,
662+ DC_FQDN : str ,
663+ HASHNT : Optional [bytes ] = None ,
664+ PASSWORD : Optional [str ] = None ,
665+ KEY = None ,
666+ ssp : Optional [KerberosSSP ] = None ,
659667 mode = NETLOGON_SECURE_CHANNEL_METHOD .NetrServerAuthenticate3 ,
660668 secureChannelType = NETLOGON_SECURE_CHANNEL_TYPE .WorkstationSecureChannel ,
661669 ):
@@ -667,39 +675,34 @@ def establish_secure_channel(
667675
668676 :param mode: one of NETLOGON_SECURE_CHANNEL_METHOD. This defines which method
669677 to use to establish the secure channel.
670- :param computername: the netbios computer account name that is used to establish
671- the secure channel. (e.g. WIN10)
672- :param domainname: the netbios domain name to connect to (e.g. DOMAIN)
673- :param HashNt: the HashNT of the computer account.
678+ :param UPN: the UPN of the computer account name that is used to establish
679+ the secure channel. (e.g. WIN10@domain.local)
680+ :param DC_FQDN: the FQDN name of the DC.
681+
682+ The function then requires one of the following:
683+
684+ :param HASHNT: the HashNT of the computer account (in Authenticate3 mode).
685+ :param KEY: a Kerberos key to use (in Kerberos mode)
686+ :param PASSWORD: the password of the computer account (any mode).
687+ :param ssp: a KerberosSSP to use (in Kerberos mode)
674688 """
675- # Flow documented in 3.1.4 Session-Key Negotiation
676- # and sect 3.4.5.2 for specific calls
677- clientChall = os .urandom (8 )
678-
679- # Step 1: NetrServerReqChallenge
680- netr_server_req_chall_response = self .sr1_req (
681- NetrServerReqChallenge_Request (
682- PrimaryName = None ,
683- ComputerName = computername ,
684- ClientChallenge = PNETLOGON_CREDENTIAL (
685- data = clientChall ,
686- ),
687- ndr64 = self .ndr64 ,
688- ndrendian = self .ndrendian ,
689- )
690- )
691- if (
692- NetrServerReqChallenge_Response not in netr_server_req_chall_response
693- or netr_server_req_chall_response .status != 0
694- ):
695- print (
696- conf .color_theme .fail (
697- "! %s"
698- % STATUS_ERREF .get (netr_server_req_chall_response .status , "Failure" )
689+ computername , domainname = _parse_upn (UPN )
690+
691+ if mode == NETLOGON_SECURE_CHANNEL_METHOD .NetrServerAuthenticate3 :
692+ if ssp or KEY :
693+ raise ValueError ("Cannot use 'ssp' on 'KEY' in Authenticate3 mode !" )
694+ if not HASHNT :
695+ if PASSWORD :
696+ HASHNT = MD4le (PASSWORD )
697+ else :
698+ raise ValueError ("Missing either 'PASSWORD' or 'HASHNT' !" )
699+ if "." in domainname :
700+ raise ValueError (
701+ "The UPN in Authenticate3 must have a NETBIOS domain name !"
699702 )
700- )
701- netr_server_req_chall_response . show ()
702- raise ValueError
703+ else :
704+ if HASHNT :
705+ raise ValueError ( "Cannot use 'HASHNT' in Kerberos mode !" )
703706
704707 # Calc NegotiateFlags
705708 NegotiateFlags = FlagValue (
@@ -712,23 +715,61 @@ def establish_secure_channel(
712715 # We are either using NetrServerAuthenticate3 or NetrServerAuthenticateKerberos
713716 if mode == NETLOGON_SECURE_CHANNEL_METHOD .NetrServerAuthenticate3 :
714717 # We use the legacy NetrServerAuthenticate3 function (NetlogonSSP)
715- # Step 2: Build the session key
718+
719+ # Make sure the interface is bound
720+ if not self .bind_or_alter (self .interface ):
721+ raise ValueError ("Bind failed !" )
722+
723+ # Flow documented in 3.1.4 Session-Key Negotiation
724+ # and sect 3.4.5.2 for specific calls
725+ clientChall = os .urandom (8 )
726+
727+ # Perform NetrServerReqChallenge request
728+ netr_server_req_chall_response = self .sr1_req (
729+ NetrServerReqChallenge_Request (
730+ PrimaryName = None ,
731+ ComputerName = computername ,
732+ ClientChallenge = PNETLOGON_CREDENTIAL (
733+ data = clientChall ,
734+ ),
735+ ndr64 = self .ndr64 ,
736+ ndrendian = self .ndrendian ,
737+ )
738+ )
739+ if (
740+ NetrServerReqChallenge_Response not in netr_server_req_chall_response
741+ or netr_server_req_chall_response .status != 0
742+ ):
743+ print (
744+ conf .color_theme .fail (
745+ "! %s"
746+ % STATUS_ERREF .get (
747+ netr_server_req_chall_response .status , "Failure"
748+ )
749+ )
750+ )
751+ netr_server_req_chall_response .show ()
752+ raise ValueError ("NetrServerReqChallenge failed !" )
753+
754+ # Build the session key
716755 serverChall = netr_server_req_chall_response .ServerChallenge .data
717756 if self .supportAES :
718- SessionKey = ComputeSessionKeyAES (HashNt , clientChall , serverChall )
757+ SessionKey = ComputeSessionKeyAES (HASHNT , clientChall , serverChall )
719758 self .ClientStoredCredential = ComputeNetlogonCredentialAES (
720759 clientChall , SessionKey
721760 )
722761 else :
723762 SessionKey = ComputeSessionKeyStrongKey (
724- HashNt , clientChall , serverChall
763+ HASHNT , clientChall , serverChall
725764 )
726765 self .ClientStoredCredential = ComputeNetlogonCredentialDES (
727766 clientChall , SessionKey
728767 )
768+
769+ # Perform Authenticate3 request
729770 netr_server_auth3_response = self .sr1_req (
730771 NetrServerAuthenticate3_Request (
731- PrimaryName = None ,
772+ PrimaryName = " \\ \\ " + DC_FQDN ,
732773 AccountName = computername + "$" ,
733774 SecureChannelType = secureChannelType ,
734775 ComputerName = computername ,
@@ -740,10 +781,7 @@ def establish_secure_channel(
740781 ndrendian = self .ndrendian ,
741782 )
742783 )
743- if (
744- NetrServerAuthenticate3_Response not in netr_server_auth3_response
745- or netr_server_auth3_response .status != 0
746- ):
784+ if netr_server_auth3_response .status != 0 :
747785 # An error occurred.
748786 NegotiatedFlags = None
749787 if NetrServerAuthenticate3_Response in netr_server_auth3_response :
@@ -758,20 +796,8 @@ def establish_secure_channel(
758796 % (NegotiatedFlags ^ NegotiateFlags )
759797 )
760798 )
799+ raise ValueError ("NetrServerAuthenticate3 failed !" )
761800
762- # Show the error
763- print (
764- conf .color_theme .fail (
765- "! %s"
766- % STATUS_ERREF .get (netr_server_auth3_response .status , "Failure" )
767- )
768- )
769-
770- # If error is unknown, show the packet entirely
771- if netr_server_auth3_response .status not in STATUS_ERREF :
772- netr_server_auth3_response .show ()
773-
774- raise ValueError
775801 # Check Server Credential
776802 if self .supportAES :
777803 if (
@@ -798,10 +824,44 @@ def establish_secure_channel(
798824 domainname = domainname ,
799825 computername = computername ,
800826 )
827+
828+ # Finally alter context (to use the SSP)
829+ if not self .alter_context (self .interface ):
830+ raise ValueError ("Bind failed !" )
831+
801832 elif mode == NETLOGON_SECURE_CHANNEL_METHOD .NetrServerAuthenticateKerberos :
833+ # We use the brand new NetrServerAuthenticateKerberos function
802834 NegotiateFlags += "Kerberos"
803- # TODO
804- raise NotImplementedError
805835
806- # Finally alter context (to use the SSP)
807- self .alter_context ()
836+ # Set KerberosSSP and alter context
837+ if ssp :
838+ self .ssp = self .sock .session .ssp = ssp
839+ else :
840+ self .ssp = self .sock .session .ssp = KerberosSSP (
841+ UPN = UPN ,
842+ SPN = "netlogon/" + DC_FQDN ,
843+ PASSWORD = PASSWORD ,
844+ KEY = KEY ,
845+ )
846+ if not self .bind_or_alter (self .interface ):
847+ raise ValueError ("Bind failed !" )
848+
849+ # Send AuthenticateKerberos request
850+ netr_server_authkerb_response = self .sr1_req (
851+ NetrServerAuthenticateKerberos_Request (
852+ PrimaryName = "\\ \\ " + DC_FQDN ,
853+ AccountName = computername + "$" ,
854+ AccountType = secureChannelType ,
855+ ComputerName = computername ,
856+ NegotiateFlags = int (NegotiateFlags ),
857+ ndr64 = self .ndr64 ,
858+ ndrendian = self .ndrendian ,
859+ )
860+ )
861+ if netr_server_authkerb_response .status != 0 :
862+ # An error occured
863+ netr_server_authkerb_response .show ()
864+ raise ValueError ("NetrServerAuthenticateKerberos failed !" )
865+
866+ # The NRPC session key is in this case the kerberos one
867+ self .SessionKey = self .sspcontext .SessionKey
0 commit comments