Password Cracking and Wordlist Engineering Part 7: Cracking CONTOSO.LOCAL End to End

Parts 2 through 6 built a toolkit in isolation: hashcat’s engine, John’s niche, wordlists, rules, masks, hybrids. This post removes the isolation, the same way Part 9 of the Impacket series did for the attack chain that produced these hashes in the first place. Two targets, both real artifacts from that series, both cracked here using nothing but the techniques this series already covered.

Stage 0: the inputs

Part 3 of the Impacket series ran GetNPUsers.py against CONTOSO.LOCAL and got this back for svc-legacy-scan, an account with Kerberos pre-authentication disabled:

$krb5asrep$23$svc-legacy-scan@CONTOSO.LOCAL:a1f3c9e0b7d24e118f...

Part 4 ran GetUserSPNs.py and got this for svc-backup, an account with a registered SPN:

$krb5tgs$23$*svc-backup$CONTOSO.LOCAL$MSSQLSvc/FS01.CONTOSO.LOCAL~1433*$8a3f...

Both posts named the cracked plaintexts — Sc4nn3r2019! and B4ckup$SQL2024 — without showing the work. This is the work.

Stage 1: the baseline attempt, and why it fails

Every real cracking session starts cheap before it starts clever. Part 2’s straight attack, rockyou plus best64.rule, against both hashes:

$ hashcat -m 18200 -a 0 -r rules/best64.rule asrep.txt rockyou.txt
$ hashcat -m 13100 -a 0 -r rules/best64.rule spns.txt rockyou.txt

Both come back with no match. That’s not a failure of the tooling — 14.3 million real breached passwords plus 64 mutation rules is a genuinely strong first pass, and it wins a large fraction of real-world cracks in seconds. It fails here for the same reason Part 4 predicted: Sc4nn3r2019! and B4ckup$SQL2024 aren’t leaked consumer passwords, they’re service-account naming conventions specific to one organization. rockyou was never going to contain them. This negative result is itself useful information — it says the target’s password generation is organization-specific, not generically weak, and rules out spending more time on bigger stock wordlists in favor of building a targeted one.

Stage 2: building the targeted vocabulary

Part 4’s approach: scrape the organization’s own language and pattern-generate the rest. A cewl pass against CONTOSO’s public-facing material (in a real engagement — the careers page, IT documentation, internal wiki if accessible) and a review of the account names themselves surface the obvious candidates directly from the account names and their function: svc-legacy-scan implies “scanner” or “scan,” svc-backup on FS01 running MSSQLSvc implies “backup” and “sql.” A three-line seed wordlist:

$ cat > seed-words.txt << 'EOF'
scanner
backup
sql
EOF

Small, deliberately — the point of Parts 5 and 6 is that structure and mutation do the heavy lifting from here, not wordlist size.

Stage 3: cracking svc-legacy-scan with a custom rule

Part 5 built exactly the rule this hash needs — best64.rule covers e→3 but not a→4, and neither covers “capitalize, then leetspeak, then append a plausible year, then append !”:

$ cat > contoso-pattern.rule << 'EOF'
c sa4 se3 $2 $0 $1 $9 $!
c sa4 se3 $2 $0 $2 $0 $!
c sa4 se3 $2 $0 $2 $1 $!
EOF
$ hashcat -m 18200 -a 0 -r contoso-pattern.rule asrep.txt seed-words.txt

scanner run through c sa4 se3 $2 $0 $1 $9 $! becomes: capitalize (Scanner), leetspeak the a and e (Sc4nn3r), append 2, 0, 1, 9 (Sc4nn3r2019), append ! (Sc4nn3r2019!) — an exact match. $krb5asrep$23$svc-legacy-scan@CONTOSO.LOCAL:... cracks to Sc4nn3r2019!, confirming what Part 3 of the Impacket series stated and showing the actual mechanism behind it.

Stage 4: cracking svc-backup with a combinator-plus-hybrid chain

B4ckup$SQL2024 doesn’t fit a single-word-plus-suffix pattern the way the AS-REP hash did — it’s two organization-relevant words (backup, sql) joined by a literal $, each mutated differently, followed by a year. Part 6’s combinator mode (-a 1), with a rule applied to each side independently via -j/-k, builds exactly that shape:

$ hashcat -a 1 -j 'c sa4' -k '^$ c' --stdout seed-words.txt seed-words.txt | sort -u > combined-base.txt

-j 'c sa4' capitalizes and leetspeaks the left word (backupBackupB4ckup); -k '^$ c' prepends a literal $ and capitalizes the right word (sql$sql$Sql, close enough for the next stage to finish). The combinator engine pairs every left word with every right word, so this run also produces junk combinations alongside the useful one — sort -u after --stdout is worth doing before committing the result to a real attack run, exactly as Part 4 recommended for combining wordlist sources generally. combined-base.txt now contains B4ckup$Sql among its candidates — close, but the case on SQL and the year are both still missing. A second-stage hybrid attack (-a 6, Part 6) closes both gaps in one pass:

$ hashcat -m 13100 -a 6 -r <(echo 'u') spns.txt combined-base.txt ?d?d?d?d

The inline rule uppercases the whole candidate before the mask appends four digits — B4ckup$SqlB4CKUP$SQL isn’t quite right either, since only SQL needs uppercasing, not backup — the honest version of this step in a real engagement is two or three short iterations, not one perfect command on the first try, which is exactly how cracking work actually goes. Adjusting -k to ^$ u (prepend $, uppercase the whole right word, rather than capitalize) fixes it directly at the combinator stage: sql$sql$SQL. Re-run:

$ hashcat -a 1 -j 'c sa4' -k '^$ u' --stdout seed-words.txt seed-words.txt | sort -u > combined-base.txt
$ hashcat -m 13100 -a 6 spns.txt combined-base.txt ?d?d?d?d

B4ckup$SQL plus the mask’s four digits, 2024, is an exact match. $krb5tgs$23$*svc-backup$CONTOSO.LOCAL$MSSQLSvc/FS01.CONTOSO.LOCAL~1433*$... cracks to B4ckup$SQL2024 — again, confirming what Part 4 of the Impacket series stated, this time with the mechanism.

Stage 5: what this actually took

Every stage above, run against Part 2’s real Kerberoast/AS-REP throughput figures (3.48–3.56 GH/s on a single rented RTX 4090), is a fraction of a second of GPU time per attempt — the actual bottleneck across this entire walkthrough was never compute, it was the two or three iterations of human reasoning about what pattern to try next after the generic wordlist came back empty. That’s the real lesson underneath all six prior parts: fast-hash formats like these make raw compute functionally free, which means the wordlist and rule engineering — Parts 4 through 6, not Part 2’s GPU numbers — is where a real assessment’s time actually goes, and where a defender’s actual leverage is, too.

Where this leaves the series

Both hashes started this post as ciphertext with a stated-but-unexplained answer; both end it fully reproduced from first principles, using nothing invented for this walkthrough — every technique came from Parts 2 through 6. Part 8 turns this whole series around: given everything above, what actually would have stopped it — not generic “use a strong password” advice, but the specific, load-bearing controls (KDF choice, gMSA migration, pre-auth enforcement) that make an account’s password irrelevant to whether it’s crackable at all.