Six years ago I wrote up my first Crack Me If You Can run as raw, unedited notes — hashcat commands pasted straight from the terminal, area-code wordlists built from scraped city lists, no polish at all. This year’s contest looked nothing like that one. Same competition, same organizer, same Street division — but this time the team was two members, and only one of us needed sleep: me, and Claude Code, an AI coding agent, running as a real teammate for 48 straight hours. We finished 7th overall and 3rd in Street, submitting 862 plaintexts with 760 accepted for roughly 1.43M points.
Want the technical reference sheet for this stuff too? Subscribe and get the Security Command Reference free — plus one new deep-dive article a week, no daily spam.
What Crack Me If You Can Actually Is
KoreLogic’s Crack Me If You Can runs every DEF CON weekend — 48 hours, a batch of password hashes across a dozen-plus hash types, and a leaderboard that updates as teams submit cracked plaintexts. The Street division is the open, no-prerequisites bracket; Pro is invite-only and plays a different, harder game entirely. Street 2026 handed out a mix of fast unsalted hashes (raw-md5, raw-sha384), classic salted crypts (md5crypt, sha512crypt), memory-hard KDFs (bcrypt, yescrypt, argon2id, gost_yescrypt), and two nasty curveballs: a custom KDF nobody had seen before, and encrypted disk images. Points are weighted by how hard a type is to crack, not by raw crack count — a single yescrypt hash was worth 47,000 points; a raw-md5 was worth 1.
The Division of Labor
I’ve read enough “AI did the pentest” posts to be skeptical of my own headline, so here’s who did what, plainly. I owned the hardware fleet and the vast.ai budget, made the go/no-go calls, and set the pace — nap crons, a 30-minute endgame loop. The single most useful thing I did was noticing a detail in the contest’s own usernames that nobody on the team had looked at closely enough yet. Claude ran the software side. It built the automation loop that pulled potfiles, filtered to Street-only hashes, and submitted over GPG-encrypted email. It turned my username observation into a working classifier and generated the multilingual wordlists. It also reverse-engineered a stripped AIX/PowerPC binary to reconstruct a KDF nobody had documented — I couldn’t have done that myself in the time we had.
Neither of us could have pulled this off solo. In practice the pattern was smaller and more boring than “AI teammate” makes it sound: I’d ask a question or make a call, Claude would go run with it and come back with a one-line answer, and I’d decide what to do next. Some of the calls that mattered most were plain operational judgment, nothing exotic — split the gost wordlist across every box and run it in parallel, recycle everything we’d already cracked against the other hash types. The agent’s real contribution wasn’t creativity. It was tirelessness — it ran the same clean analysis pass at hour 40 that it had at hour 4.
The Master Key: What the Usernames Were Actually Telling Us
Partway through the contest, KoreLogic tweeted a hint that if you thought they hadn’t given much away this year, you hadn’t looked closely enough at the usernames. The usernames were formatted as first-initial-plus-surname — zarajput, aplarsen, mmgoto — and once we lined up our early cracks against them, the pattern was unmistakable: each password was that hash type’s theme concept, expressed in the language of the user’s own surname, usually with a light transform like a reversal or basic leetspeak on top.
| Username | Password | What it decodes to |
|---|---|---|
| zarajput | Ispaw |
Hindi wapsi (“return”), reversed + capitalized |
| aplarsen | Negi |
Danish igen (“again”), reversed |
| mmgoto | Ureak |
Japanese kaeru (“return”), reversed |
| iekozlov | sn0va |
Russian snova (“again”) + leet |
| waroth | löschen |
German for “delete” — accent kept intact |
| mnyilmaz | güvenlik |
Turkish for “security” |
The theme itself changed by hash type — bcrypt, yescrypt, and gost_yescrypt all clustered around “return / comeback / recovery,” while argon2id ran a GDPR/”right to be forgotten” theme (forget, privacy, delete, consent). Once that was confirmed against 20 known bcrypt cracks, it stopped being a curiosity and became a targeting strategy: instead of spraying every hash against roughly 40 languages of wordlist, we could test each hash against only the language implied by its own username. That’s close to a 200x reduction in keyspace on hash types slow enough that brute-forcing the full multilingual set wasn’t realistic in 48 hours — which is exactly why it mattered most on the highest-value, most memory-hard types. We built a small classifier (surname pattern and script matching against language lexicons) that did this automatically per-hash, and it’s the single tool in the kit I’d point a future team at first.
Where the Points Actually Came From
The leaderboard math rewards judgment about which hashes to chase, not raw crack volume, and our own numbers make that uncomfortably obvious:
| Hash type | Cracked | Points each | Total points |
|---|---|---|---|
| yescrypt | 17 | 47,000 | 799,000 |
| argon2id | 10 | 35,000 | 350,000 |
| bcrypt | 31 | 7,500 | 232,500 |
| sm3crypt | 44 | 500 | 22,000 |
| md5crypt | 86 | 200 | 17,200 |
| raw-sha384 + raw-md5 + openssha | 663 | 1–20 | 1,725 |
Three hash types — yescrypt, argon2id, and bcrypt — drove roughly 96% of our score from a small fraction of our total cracks. We also spent real hours on two types we never cracked at all: a custom $cmiyc$ KDF that turned out to hide a scrypt-style memory-hard construction inside a stripped PowerPC binary (fully reverse-engineered, correctly reimplemented in Python, and still too slow without a GPU port — nobody in the entire Street division cracked one of these), and encrypted Apple disk images at 400k+ PBKDF2 iterations that were never going to fall in the time we had. Chasing the zero-value stuff was worth doing for what we learned, not for the leaderboard.
What Actually Went Wrong
We nearly abandoned gost_yescrypt entirely over what looked like proof our hashcat build was silently falling back to a broken DES mode. Turned out to be a self-inflicted testing error — an invalid hardcoded salt, plus a shell mangling the $ characters in a raw hash string before it ever reached the cracker. We lost the better part of an hour to that before we thought to test against an actual file instead of a copy-pasted terminal string. Separately, a teardown cron had the contest end time set an hour off from the real 11:00 AM PDT cutoff. I caught it only by double-checking the announced deadline against the timezone math myself — otherwise it would have either missed the final submission window or kept billing a cloud GPU box for nothing.
The Toolkit
We cleaned up and published the tools that actually mattered, sanitized and MIT-licensed, on GitHub: github.com/lanix13/cmiyc2026-iceberg10. The full technical writeup — every hash type, the complete reverse-engineering notes on the custom KDF, and all the gotchas that didn’t fit here — is also up as a readable page at lanix13.github.io/cmiyc2026-iceberg10.
Worth pulling directly if you compete in a future CMIYC or run your own password audits:
- target-by-surname.py — the classifier that turns the username→language pattern into an actual targeted attack. This is the one tool from the whole kit worth taking even if you skip everything else.
- comeback-expand.py / dataprot-expand.py — the multilingual wordlist generators (45+ and 25+ languages respectively) with leet, reversal, and accented native-form variants baked in.
- derived-theme.rule — a 48-rule hashcat/John rule set derived from analyzing our own successful cracks, rather than running a generic 60,000-line rule file against everything.
- cmiyc_hash.py — the reference reimplementation of the reverse-engineered
$cmiyc$KDF, plus the full RE notes on how it was pulled out of a stripped PowerPC binary. - vast-lib.sh — the vast.ai cloud-burst orchestration, including a CUDA library symlink fix that’s the difference between a real RTX 4090 and a silent, 1000x-slower CPU fallback with no error message at all.
Key Lessons for the Street Division
Read the usernames before you read anything else. The single biggest lever this year was hiding in plain sight in a field most teams treat as metadata.
Value-weight your hardware allocation, not your crack count. One yescrypt crack outweighed thousands of raw-md5 cracks. Point-per-hash should drive where GPUs get pointed, every time you rebalance.
Coverage beats generic rules. A tight, 48-rule set derived from what we’d actually cracked outperformed running a massive generic rule file, and it was cheap enough to still run on the slow hash types.
Never submit Pro hashes as Street. They come back invalid and cost nothing but time and a slightly embarrassing autoresponder reply — our only 83 invalid submissions all happened early, before we hard-filtered.
Six Years Later
Reading my own 2020 writeup back is a strange experience. A lot of the underlying instincts — spot the pattern in the hash format, build a targeted wordlist instead of brute-forcing blind, keep separate potfiles per hash type so you’re not re-testing what you already have — are exactly the same instincts that mattered this year. The strategy hasn’t really changed. What changed is that the grunt work that used to eat an entire evening — manual cewl scraping, hand-building rule files — happened in minutes this year instead, which just meant I had more attention left over for the parts a human still has to do: deciding what’s worth chasing, and catching it when a “critical” finding turns out to be a testing mistake instead of a bug. Great contest. See you in 2027.