Add Kerberos TGS-REP and AS-REP prefix detection

Adds $krb5tgs$ (Kerberoasting) and $krb5asrep$ (AS-REP roasting)
to PREFIX_RULES — both common in Active Directory attacks and
emitted by Impacket/Rubeus in hashcat format. Includes matching
tests for each prefix.
This commit is contained in:
Abhinav-004 2026-07-28 00:04:32 +05:30
parent 7822ef1d49
commit 87c8e2c003
2 changed files with 51 additions and 0 deletions

View File

@ -177,10 +177,26 @@ PREFIX_RULES: list[tuple[str, str, str]] = [
("{SHA}", "LDAP SHA", "LDAP SHA-1 (base64 payload)"),
("{SMD5}", "LDAP SMD5", "LDAP salted MD5 (base64 payload)"),
("{MD5}", "LDAP MD5", "LDAP MD5 (base64 payload)"),
("{CRYPT}", "LDAP CRYPT", "LDAP wrapping a crypt(3) hash"),
# Kerberos — Active Directory attack hashes. Both are emitted by
# Impacket's GetUserSPNs.py / GetNPUsers.py and Rubeus in
# "hashcat" format. The digit right after the second `$` is the
# Kerberos encryption type (23 = RC4, 17 = AES128, 18 = AES256)
# and decides which hashcat -m mode to crack it with, but it does
# not change WHAT the hash is, so we match on the prefix alone
("$krb5tgs$", "Kerberos TGS-REP (Kerberoasting)",
"service ticket hash from Kerberoasting; etype digit after $krb5tgs$ "
"(23=RC4, 17/18=AES) picks the hashcat mode"),
("$krb5asrep$", "Kerberos AS-REP (AS-REP Roasting)",
"pre-auth-disabled account hash from AS-REP roasting; etype digit "
"after $krb5asrep$ (23=RC4, 17/18=AES) picks the hashcat mode"),
]
# =============================================================================
# Length-and-hex rules — fallback when no prefix matched
# =============================================================================

View File

@ -141,6 +141,41 @@ def test_django_pbkdf2_prefix_is_recognized() -> None:
assert candidates[0].algorithm == "Django PBKDF2-SHA256"
def test_kerberos_tgs_prefix_is_recognized() -> None:
"""
Kerberoasting hashes start with `$krb5tgs$` and should be
reported as a Kerberos TGS-REP with HIGH confidence
Sample is a real-shaped RC4 (`etype 23`) service ticket hash,
the format Impacket's GetUserSPNs.py and Rubeus both emit.
identify() never decodes the ticket payload, so a short
fake tail after the two known `$`-delimited fields is enough
"""
# $krb5tgs$<etype>$*<user>$<realm>$<spn>*$<checksum>$<edata2>
sample = "$krb5tgs$23$*user$REALM.COM$cifs/host.realm.com*$checksumhere$edata2here"
candidates = identify(sample)
assert candidates
assert candidates[0].algorithm == "Kerberos TGS-REP (Kerberoasting)"
assert candidates[0].confidence == "high"
def test_kerberos_asrep_prefix_is_recognized() -> None:
"""
AS-REP roasting hashes start with `$krb5asrep$` and should be
reported as a Kerberos AS-REP with HIGH confidence
Sample mirrors what Impacket's GetNPUsers.py and Rubeus's
`asreproast` output for an account with Kerberos pre-auth
disabled. As with TGS-REP, identify() only inspects the prefix
"""
# $krb5asrep$<etype>$<user>@<realm>:<checksum>$<edata2>
sample = "$krb5asrep$23$user@REALM.COM:checksumhere$edata2here"
candidates = identify(sample)
assert candidates
assert candidates[0].algorithm == "Kerberos AS-REP (AS-REP Roasting)"
assert candidates[0].confidence == "high"
def test_apr1_prefix_is_recognized() -> None:
"""
Apache `.htpasswd` MD5 hashes start with $apr1$