Lab 02 — Kerberos / Identity Exposure Analyzer
Lens: The directory's Kerberos and identity weaknesses, account-by-account. WARMUP: Chapters 5–8 (the Kerberos dance, Kerberoasting, AS-REP roasting, the three delegations). Engagement tie-in: Operation Cedar Lattice, Phase 05. Lab 01 found the path through Meridian's directory graph; this analyzer finds the exposed accounts that make the path possible — the Kerberoastable service account whose crack opens the first door, the unconstrained-delegation server that hands you a Domain Admin's ticket — and pairs each with its detection and fix.
The problem
Lab 01 walks the directory graph. This lab reads the directory facts and asks the question a red team answers in the first hour and a defender should have answered first: which accounts are exposed, by which technique, and how do I detect and remediate each one? These are the canonical Kerberos and identity weaknesses:
- Kerberoasting (T1558.003) — a user account with a Service Principal Name. Any domain user
can request a service ticket (TGS) for it; the reply is encrypted with the account's
password-derived key, so it can be cracked offline. RC4 encryption plus an old/weak
password makes the crack trivial. A
gMSA(128-char auto-rotated key) is effectively immune. - AS-REP roasting (T1558.004) — an account with Kerberos pre-authentication disabled. The KDC returns an AS-REP encrypted with the account's key without proof of identity, so anyone can request and crack it offline.
- Unconstrained delegation — a computer that caches the TGT of every user who authenticates to it. Compromise it (or coerce a DC to authenticate to it, PetitPotam-style) and you replay a captured TGT, including a Domain Admin's. The single highest-severity delegation flag.
- RBCD-writable — an object whose
msDS-AllowedToActOnBehalfOfOtherIdentityan attacker can write. That grants resource-based constrained delegation: impersonate any user to the object. - Privileged account with an SPN — a Domain/Enterprise Admin (or
adminCount=1) carrying an SPN is a Kerberoastable account whose crack yields domain-level privilege directly — the worst case.
What you build (lab.py)
The Account dataclass, the thresholds (OLD_PASSWORD_DAYS, WEAK_ETYPES), the privileged-group
set, and the _is_privileged / _uses_weak_etype helpers are given. You implement the checks:
kerberoastable(directory)→ findings for SPN user accounts (gMSA exempt); risk rises with RC4, an old password, and privilege.asrep_roastable(directory)→ findings for pre-auth-disabled accounts.delegation_risks(directory)→ unconstrained-delegation computers and RBCD-writable objects.privileged_with_spn(directory)→ the high-value Kerberoast, flagged separately at top risk.analyze(directory)→ all findings, sorted by(risk desc, technique, account, title).
Every finding is a dict carrying at least technique, title, account, risk, detection
(the Kerberos event ids), and hardening (the fix).
Why RC4 vs AES decides the crack
| RC4 (etype 0x17) | AES256 (etype 0x12) | |
|---|---|---|
| Key derivation | NTLM hash (MD4 of the password) — fast | PBKDF2, 4096 iterations + salt — slow |
| Offline crack | billions/sec on a GPU | orders of magnitude slower |
| In a roast | the cheap-crack red flag | makes Kerberoasting expensive, often infeasible |
| Telemetry | 4769 with ticket encryption type 0x17 | 4769 with 0x11/0x12 |
This is why the analyzer ranks an RC4 + old-password Kerberoastable account above an AES + fresh one, and why "AES-only + gMSA" is the durable fix. The detection — 4769 with encryption type 0x17, or a spike of 4769 across many SPNs from one principal — is the SOC's signal that roasting is underway.
Attack cases the tests cover
- Each exposure is detected: an RC4/old-password Kerberoastable account, an AES/fresh one, an AS-REP-roastable (pre-auth-off) account, an unconstrained-delegation computer, an RBCD-writable object, and a privileged account with an SPN.
- The hardened account is clean: a gMSA, AES-only, fresh-password service account and a plain user appear in no finding (no false positives).
- RC4 + old password ranks higher than AES + fresh among Kerberoast findings;
analyzereturns findings sorted by risk descending, top-severity (unconstrained delegation / privileged-SPN) first. - Every finding carries a detection and a hardening string; the 4769/4768/5136 event ids appear where they belong.
- Empty input is handled; output is deterministic.
Run
pip install -r requirements.txt
LAB_MODULE=solution pytest -q # reference passes (11 tests)
pytest -q # your implementation after the TODOs
Hardening / detection (the break-the-chain pairing)
The analyzer is the hardening report. Each finding names the fix at the mechanism level:
- Kerberoast → gMSA (auto-rotated 128-char key) or a long random password + AES-only; remove unused SPNs; never run services as a Domain Admin. Detect 4769 with etype 0x17.
- AS-REP roast → re-enable pre-authentication; if a legacy app forces it off, AES-only + long password + monitor 4768 for the account.
- Unconstrained delegation → remove
TRUSTED_FOR_DELEGATION; move to constrained/RBCD; put DAs in Protected Users and mark them "sensitive — cannot be delegated". - RBCD-writable → audit the object's DACL, remove unexpected write principals, alert on 5136 to
msDS-AllowedToActOnBehalfOfOtherIdentity.
These map straight to the choke edges Lab 01 finds: fixing the exposure removes the graph edge.
Extensions (your own isolated, owned range)
- Parse a real SharpHound/PingCastle/PurpleKnight export from an owned GOAD-style forest and run the same checks over collected facts (never a production domain).
- Add ADCS ESC1–ESC8 template-misconfiguration checks (vulnerable enrollment + SAN supply).
- Add DCSync rights detection (principals with
DS-Replication-Get-Changes-All) and golden- ticket pre-conditions (KRBTGT password age). - Emit Sigma rules for each finding's detection (e.g. 4769 etype 0x17 frequency) so the blue team keeps a durable control.
- Score the whole directory with a single identity-exposure index and trend it across collections.
Interview / resume
"Built a Kerberos/identity exposure analyzer over synthetic directory facts: flags Kerberoastable SPN accounts (ranking RC4 + old-password highest), AS-REP-roastable accounts, unconstrained- delegation and RBCD-writable objects, and privileged accounts carrying SPNs — each finding paired with the exact Kerberos event id that detects it (4769 etype 0x17, 4768, 5136) and the mechanism- level fix (gMSA, AES-only, pre-auth, Protected Users)."
Limitations: a representative set of exposures, not the full SpecterOps/PingCastle catalog; risk scores are ordinal heuristics, not CVSS; encryption-type and password-age thresholds are illustrative defaults; reasons over the given synthetic facts only — it does not collect them, request tickets, or crack anything; no live AD, no weaponization.