Password Attacks & Cryptography · intermediate · ~11 min

Password cracking concepts (authorized labs only)

**What you will learn** - Explain the difference between **offline** and **online** password attacks, and why offline cracking is fast and silent. - Describe the four main offline guessing strategies — dictionary, rule-based, mask/brute force, and hybrid — and when each is practical. - Recognise the role of the common tools (Hashcat, John the Ripper, CeWL) at a conceptual level, without needing to run an attack against anyone. - Reason about **cracking economics**: how hash speed, password length, and character set decide whether a password falls in seconds or centuries. - Recommend and **verify** the defences that break cracking: slow KDFs, length-based policy, breach screening, and MFA. - State the legal and ethical boundary: this is authorized-lab-only work, and know how to log and detect real cracking attempts against your own systems.

Overview

Security objective. The asset is your users' passwords — really, the secret they reuse across many sites. The threat is an attacker who has already stolen your password database (a breach) and now tries to recover the plaintext offline. In this lesson you learn to think like the attacker so you can defend: you will be able to estimate how quickly a stolen hash falls, and prove that a proper storage design keeps it standing.

A hash is the scrambled, fixed-length value a system stores in place of the real password. A good system never stores the password itself; at login it hashes what you typed and compares. Offline cracking is the process of guessing passwords, hashing each guess, and looking for a hash that matches one in the stolen file. Because it runs entirely on the attacker's own hardware, there is no account lockout, no rate limit, and no network noise to slow it down — only raw compute.

This builds directly on the prerequisite lesson "Password storage: salts, slow hashes, and work factors" (pw-storage). There you learned how passwords should be stored — salted, with a slow key-derivation function (KDF) like bcrypt, scrypt, or Argon2, tuned by a work factor. This lesson shows the attack those defences exist to defeat, so the reasons behind salt and slow hashing become concrete rather than abstract.

There are four main guessing strategies — dictionary, rule-based, mask/brute force, and hybrid — and a small set of standard tools. Everything here is lab-only and tied to explicit authorization. We deliberately teach detection, remediation, and verification in more depth than the attack itself, because on a real job the defensive half is where you add value.

Why it matters

In authorized professional work — a penetration test, a red-team engagement, or an internal password-strength audit — you are frequently handed a set of hashes (from a lab AD dump, a test database, or a client-approved sample) and asked one question: how bad is it? Cracking a controlled sample tells the organization, in plain numbers, how many of its accounts use guessable passwords and how fast an attacker who breached them would succeed.

The finding is only useful because it drives a fix. "37% of the sample cracked in under an hour against MD5" is a sentence that gets a slow-KDF migration funded. Understanding the mechanics lets you write that sentence honestly and then verify the remediation: re-run the same effort against the new bcrypt/Argon2 store and show the crack rate collapse.

It also protects you from a dangerous misconception several beginners hold: that a password policy of "8 characters, one uppercase, one number, one symbol" is strong. Once you have watched rule-based cracking turn Password1! into a five-second hit, you understand why modern guidance (NIST SP 800-63B) favours length and breach screening over composition rules. Finally, the same knowledge lets you build detection: knowing what an attacker does with a stolen hash tells you which logs and alerts reveal that a database was exfiltrated in the first place.

Core concepts

1. Offline vs online attacks

Definition. An offline attack runs against hashes the attacker already holds; an online attack sends guesses to a live login service.

Plain explanation. If you have the hashed password file on your own machine, you can guess as fast as your hardware allows and nobody notices. If instead you type guesses into a real login form, every attempt crosses the network, gets logged, and trips rate limits or account lockout.

How it works. Offline: guess -> hash(guess) -> compare to stolen hash, repeated billions of times per second on a GPU for a fast hash. Online: guess -> HTTP request -> server checks -> response, maybe a few per second before you are throttled or locked out.

When / when-not. Offline cracking only applies after a breach has exposed the hashes; you cannot do it without the hash file. Online attacks (spraying, brute force) are the next lesson.

Pitfall. People assume a breach of "hashed passwords, not plaintext" is harmless. If the hashes are fast (MD5, SHA-1, unsalted SHA-256), offline cracking recovers most weak passwords quickly. Hashing is not the same as safety — the speed of the hash is what matters.

2. The four guessing strategies

Strategy What it tries Best against Cost
Dictionary Entries from a wordlist (common/breached passwords) Reused and common passwords Very cheap, high yield
Rule-based Wordlist entries mutated by rules (password -> P@ssw0rd!) Passwords built from a base word plus predictable tweaks Cheap
Mask / brute force Every combination of a defined pattern (e.g. 8 chars from a set) Short or structured passwords Explodes with length; often infeasible
Hybrid Wordlist combined with a mask (sunshine + 2024) Word-plus-suffix patterns Moderate

Insecure assumption each exploits. Users pick memorable, reused, or lightly-modified passwords. Rules encode exactly the tricks people think make a password strong (a->@, add ! at the end, capitalise the first letter).

Pitfall. Brute force feels unstoppable but scales terribly. Each added character multiplies the search space by the size of the character set, so a truly random 16-character password is out of reach even for offline GPUs — which is the whole point of length-based policy.

3. Hash speed and cracking economics

Definition. Cracking economics is the relationship between hash speed, password entropy, and time-to-crack.

How it works. Guesses-to-find depends on the password; guesses-per-second depends on the hash. A fast hash lets a GPU try billions/sec; a slow KDF with a high work factor deliberately drops that to thousands/sec, multiplying the attacker's time by a factor of a million or more.

When / when-not. This is why the storage choice (previous lesson) dominates outcomes far more than any composition rule. A weak password behind Argon2 can still outlast a strong password behind unsalted MD5.

Pitfall. Salts stop precomputation (rainbow tables) and stop one crack from revealing all identical passwords, but a per-hash salt does not slow down a targeted guess against a single account. Only a slow KDF does that. Confusing "salted" with "slow" is a common beginner error.

4. Tools (conceptual only)

  • Hashcat — GPU-accelerated cracker; applies all four strategies to recovered hashes.
  • John the Ripper — CPU/GPU cracker with strong rule support and format autodetection.
  • CeWL — spiders a website you are authorized to test and builds a target-specific wordlist from its words.
  • Hydra — an online login attacker (belongs to the next lesson); lab-only.

You do not need to run any of these to complete this lesson. Understanding what they do is enough to reason about defence.

Threat model

                          TRUST BOUNDARY (attacker's own lab machine)
                          |
  [ Stolen password DB ]  |   ENTRY POINT: hash file already exfiltrated
   user | salt | hash --->|--> [ Cracking rig: CPU/GPU ]
                          |          |
  ASSET PROTECTED:        |          v
   the plaintext          |     guess -> hash(guess) -> compare
   passwords (reused       |          |
   across many sites)     |     match? --> recovered plaintext --> credential reuse elsewhere
                          |
  DEFENDER'S BOUNDARY (the breached system, BEFORE theft):
   - slow KDF + high work factor  (raises cost per guess)
   - unique per-user salt         (blocks rainbow tables / bulk cracking)
   - breach screening at set-time (rejects already-leaked passwords)
   - exfil detection + MFA        (limits value of any cracked password)

Knowledge check.

  1. What asset is ultimately protected here — the hash, or the plaintext password behind it? (The plaintext; the hash is only a container, and a reused password unlocks other systems.)
  2. Where is the trust boundary in offline cracking, and why can't the defender rate-limit it? (It happens entirely on the attacker's machine after theft; the defender no longer controls the compute, so only pre-theft storage choices help.)
  3. What insecure assumption makes rule-based cracking so effective? (That predictable human mutations — a->@, capitalise, append ! — add real strength; they don't.)

Syntax notes

This is a concept lesson, so the "syntax" that matters is the shape of an offline cracking loop and the shape of the defensive verification. No live attack tooling is required.

The core mechanic, in pseudocode:

# Offline cracking loop (conceptual)
for guess in wordlist:                 # dictionary source
    for candidate in apply_rules(guess):  # optional: rule-based mutation
        if hash(candidate) == stolen_hash: # same hash function the target used
            report(candidate)              # plaintext recovered

Key structural points, annotated:

  • hash(candidate) must be the exact algorithm the target used (including salt, if the file provides one). A cracker autodetects or is told the format.
  • The loop cost is len(wordlist) * rules_per_word / guesses_per_second. You lower attacker success by lowering the denominator (slow KDF) or removing weak candidates from users' choices (breach screening).
  • A defensive verification loop has the same shape but a different goal: run a fixed, time-boxed cracking budget against a test store and count hits, before and after a storage upgrade.

Lesson

When you legally recover password hashes — for example, from a lab, or from a breach you are authorized to assess — offline cracking tests guesses against them at high speed.

A hash is the scrambled value a system stores in place of the real password. Offline cracking is strictly lab-only and always tied to explicit authorization.

Offline vs online

  • Offline — you hold the hashes and guess locally at GPU speed. No lockout, no network noise. Fast hashes fall quickly; slow KDFs resist.
  • Online — you guess against a live login. This is slow, noisy, and prone to lockout. (That is spraying and brute force, covered in the next lesson.)

Approaches

  • Dictionary attack — try entries from a wordlist, such as common or breached passwords. This is the fastest approach and usually has the highest yield.
  • Rule-based — apply transformations to wordlist entries (for example, password becomes P@ssw0rd!). This mimics how people modify passwords.
  • Mask / brute force — try every combination of a pattern (for example, 8 characters from a given character set). It is exhaustive, and practical only for short or structured passwords.
  • Hybrid — combine a wordlist with a mask.

Tools (conceptual)

  • Hashcat (GPU) and John the Ripper apply the approaches above to recovered hashes.
  • CeWL builds a target-specific wordlist.
  • Hydra runs online attacks (lab-only).

The defensive takeaway

Cracking works because of two things together: weak passwords and fast hashes.

The defenses you would recommend are:

  • Slow KDFs (covered in the prior lesson).
  • A strong, length-based password policy.
  • Breached-password screening.
  • MFA, so a cracked password alone is not enough.

This track's C exercise implements a tiny dictionary-cracking loop, so you can feel the mechanic firsthand.

Code examples

The example below is a defensive demonstration, not an attack tool. It shows, in one self-contained C11 program, why fast hashes are dangerous and how a slow, salted KDF changes the economics. It cracks only hashes it generates itself from a tiny built-in wordlist — there is no external target, no network, and no real credential.

(1) WARNING: intentionally vulnerable — use only in a local, isolated, authorized lab. Do not deploy.

This models a system that stored passwords with a fast, unsalted hash (here a toy 32-bit hash standing in for MD5/SHA-1). A dictionary attack against it is trivial.

#include <stdio.h>
#include <string.h>
#include <stdint.h>

/* Toy fast hash (stand-in for MD5/SHA-1: fast + unsalted = crackable). */
static uint32_t fast_hash(const char *s) {
    uint32_t h = 2166136261u;            /* FNV-1a basis */
    for (; *s; s++) { h ^= (uint8_t)*s; h *= 16777619u; }
    return h;
}

int main(void) {
    /* A stored hash the attacker "stole". No salt, so bulk cracking is easy. */
    uint32_t stolen = fast_hash("summer2024");

    /* Attacker's dictionary of common passwords. */
    const char *wordlist[] = { "password", "123456", "summer2024",
                              "letmein", "qwerty", NULL };

    for (int i = 0; wordlist[i] != NULL; i++) {
        if (fast_hash(wordlist[i]) == stolen) {
            printf("INSECURE: cracked in %d guesses -> %s\n", i + 1, wordlist[i]);
            return 0;
        }
    }
    printf("INSECURE: not found in this tiny wordlist\n");
    return 0;
}

Expected output: INSECURE: cracked in 3 guesses -> summer2024

The password fell almost instantly because the hash is fast and the password is a common word. This is the failure the previous lesson's storage advice prevents.

(2) SECURE fix

Proper systems don't invent hashes — they use a vetted slow KDF (bcrypt/scrypt/Argon2) with a unique per-user salt and a tuned work factor. The snippet below simulates the two properties that matter: a per-user salt and a deliberately slow, iterated derivation. In production you would call a library (e.g. crypt_r with a $2b$ bcrypt setting, or libargon2's argon2id_hash_encoded), never roll your own.

#include <stdio.h>
#include <string.h>
#include <stdint.h>

/* Toy KDF: models a SLOW, SALTED derivation (do NOT use in production;
   use bcrypt/scrypt/Argon2). The loop count stands in for a work factor. */
static uint32_t slow_kdf(const char *salt, const char *pw, unsigned rounds) {
    uint32_t h = 2166136261u;
    for (unsigned r = 0; r < rounds; r++) {
        for (const char *s = salt; *s; s++) { h ^= (uint8_t)*s; h *= 16777619u; }
        for (const char *p = pw;   *p; p++) { h ^= (uint8_t)*p; h *= 16777619u; }
    }
    return h;
}

int main(void) {
    const char *salt = "u17-9f3a";          /* unique per user, stored alongside */
    const unsigned WORK = 200000;            /* work factor: tune to ~250ms real KDF */
    uint32_t stored = slow_kdf(salt, "summer2024", WORK);

    /* Same tiny attacker wordlist as before. */
    const char *wordlist[] = { "password", "123456", "summer2024",
                              "letmein", "qwerty", NULL };

    long work_done = 0;
    for (int i = 0; wordlist[i] != NULL; i++) {
        work_done += WORK;                    /* each guess now costs WORK rounds */
        if (slow_kdf(salt, wordlist[i], WORK) == stored) {
            printf("SECURE: still crackable here, but each guess cost %u rounds; "
                   "cumulative work = %ld rounds\n", WORK, work_done);
            return 0;
        }
    }
    printf("SECURE: not found\n");
    return 0;
}

Expected output (illustrative): SECURE: still crackable here, but each guess cost 200000 rounds; cumulative work = 600000 rounds

The point is not that the weak password summer2024 became uncrackable — a weak password is still weak. The point is that every guess now costs hundreds of thousands of times more work. Against a large real wordlist plus rules, that multiplier is the difference between minutes and years. Combine it with breach screening (which would have rejected summer2024 at set-time) and the attack largely collapses.

(3) VERIFY — prove the fix rejects bad input and accepts good input

A defensive control worth shipping is breach screening: refuse passwords known to be leaked, and refuse short ones. This test shows it rejects a known-bad password and accepts a strong one.

#include <stdio.h>
#include <string.h>
#include <stdbool.h>

/* Minimal policy: reject if too short OR on the known-breached list. */
static bool password_allowed(const char *pw) {
    static const char *breached[] = { "password", "123456", "summer2024",
                                     "letmein", "qwerty", NULL };
    if (strlen(pw) < 12) return false;                 /* length first */
    for (int i = 0; breached[i]; i++)
        if (strcmp(pw, breached[i]) == 0) return false; /* breach screen */
    return true;
}

int main(void) {
    struct { const char *pw; bool expect; } cases[] = {
        { "summer2024",             false },  /* breached -> reject */
        { "short1!",                false },  /* too short -> reject */
        { "correct-horse-battery", true  },  /* long, not breached -> accept */
    };
    int failures = 0;
    for (int i = 0; i < 3; i++) {
        bool got = password_allowed(cases[i].pw);
        bool ok  = (got == cases[i].expect);
        printf("%-24s expected=%-5s got=%-5s %s\n",
               cases[i].pw, cases[i].expect ? "allow" : "deny",
               got ? "allow" : "deny", ok ? "PASS" : "FAIL");
        if (!ok) failures++;
    }
    printf("%s\n", failures == 0 ? "ALL CHECKS PASSED" : "CHECKS FAILED");
    return failures == 0 ? 0 : 1;
}

Expected output:

summer2024               expected=deny  got=deny  PASS
short1!                  expected=deny  got=deny  PASS
correct-horse-battery    expected=allow got=allow PASS
ALL CHECKS PASSED

Compile any of these with cc -std=c11 -Wall -Wextra file.c -o demo && ./demo.

Line by line

Walkthrough of the three-part demo.

Insecure program (part 1).

Step What happens Why it matters
fast_hash("summer2024") Computes a single fast, unsalted hash and calls it stolen Models the breached file the attacker holds
loop over wordlist Hashes each common password and compares to stolen This is the entire offline dictionary attack
i == 2 (summer2024) fast_hash matches stolen; prints and returns Found on the 3rd guess — the password was common and the hash was fast

Because there is no salt and the hash is fast, comparing is instant and one crack of summer2024 would crack every account that reused it. Trace: guesses 1 (password) no, 2 (123456) no, 3 (summer2024) match.

Secure program (part 2).

Step What happens Why it matters
slow_kdf(salt, "summer2024", WORK) Derives the stored value using a per-user salt and WORK iterations Salt blocks rainbow tables; iterations impose cost
work_done += WORK per guess Accumulates the attacker's cost Makes the economics visible: each guess is WORK rounds, not one
match on summer2024 Still cracks (weak password!) but after paying WORK rounds The defence multiplies cost, it does not make weak passwords safe

The key value that changes is work_done: with a real KDF tuned to ~250ms/guess, a wordlist of 10 million entries would take weeks instead of seconds. That multiplier is the entire value of a slow KDF.

Verification program (part 3).

Case Path taken Result
summer2024 length 10 < 12 -> rejected on length (also on breach list) deny (correct)
short1! length 7 < 12 -> rejected deny (correct)
correct-horse-battery length 21, not on list -> allowed allow (correct)

The test asserts each outcome against an expected value and exits non-zero if any case fails, so it can gate a build. This is what "verify the mitigation" looks like in practice: bad input is provably rejected, good input provably accepted.

Common mistakes

Mistake 1 — treating "hashed" as "safe."

  • Wrong: "We hash passwords, so a breach is fine."
  • Why wrong: Fast, unsalted hashes (MD5, SHA-1, plain SHA-256) fall to dictionary and rule attacks in seconds.
  • Corrected: Store with a slow, salted KDF (bcrypt/scrypt/Argon2) and a work factor tuned to make each guess expensive.
  • Recognise/prevent: Audit which algorithm your storage uses; if it's a general-purpose hash, it's wrong.

Mistake 2 — confusing salt with slowness.

  • Wrong: "We salt, so cracking is hard."
  • Why wrong: Salt stops precomputation and bulk cracking of identical passwords, but does not slow a targeted guess against one account.
  • Corrected: Salt AND use a slow KDF. They solve different problems.
  • Recognise/prevent: Ask "how long does one guess take?" If it's microseconds, you have salt without slowness.

Mistake 3 — believing composition rules make passwords strong.

  • Wrong: "P@ssw0rd1! is strong — it has upper, lower, number, symbol."
  • Why wrong: Rule-based cracking encodes exactly these substitutions; such passwords are near the top of every cracking ruleset.
  • Corrected: Favour length and breach screening (NIST SP 800-63B) over composition mandates.
  • Recognise/prevent: Run a lab rule-based crack against a sample; watch the "complex" ones fall first.

Mistake 4 — assuming brute force always wins eventually.

  • Wrong: "Given enough GPUs, any password cracks."
  • Why wrong: The search space grows exponentially with length; a random 16-char password is infeasible even offline.
  • Corrected: Push length in policy; length is the cheapest defence that beats brute force.

Mistake 5 — cracking without authorization.

  • Wrong: Testing a hash you found online or a colleague's account "to prove a point."
  • Why wrong: Cracking credentials you are not authorized to test is illegal (e.g. computer-misuse / unauthorized-access laws), regardless of intent.
  • Corrected: Only ever crack in a lab you own or under a signed authorization/scope. See the practice tasks' authorization checklist.

Debugging tips

When the crack "finds nothing" in the lab.

  • Wrong hash format: the cracker must use the exact algorithm and salt scheme of the target. Ask: is this MD5, NTLM, bcrypt, sha512crypt? A format mismatch guarantees zero hits.
  • Missing salt: if the file has salts and you hash without them, nothing matches.
  • Wordlist too small or wrong language: a target-specific list (or CeWL output from an authorized site) often outperforms a generic one.

When your defensive demo behaves unexpectedly.

  • If part 2 still cracks instantly in wall-clock time, remember the toy KDF is fast by design — the work_done counter, not the clock, is the lesson. A real KDF's work factor is calibrated against measured time.
  • If the verification test prints FAIL, check the expected-value table: is the length threshold what you intended? Off-by-one on strlen(pw) < 12 is common.

Questions to ask when a defence seems ineffective.

  1. How many guesses per second can an attacker make against this hash? (If millions+, the hash is too fast.)
  2. Would breach screening have rejected this password at set-time?
  3. Is MFA in place so a single cracked password is insufficient to log in?
  4. Are the salts unique per user, or shared/empty?

Detecting real cracking upstream. You cannot see offline cracking directly — it happens on the attacker's machine. So debug your detection of the breach that precedes it: unusual bulk reads of the user table, database exports at odd hours, and anomalous egress. If those alerts never fire in a tabletop exercise, your detection is the bug.

Memory safety

Security & safety — detection and logging.

Offline cracking is invisible to the victim, so your defensive leverage is (a) preventing the breach that supplies the hashes and (b) making any cracked password useless. Both depend on good logging.

What to log (authentication and data-access events):

  • Timestamp (UTC), source IP / host, and a stable user identifier (not the password).
  • The resource touched (e.g. users table export, credential-store read) and the result (allow/deny, rows returned).
  • The security decision and its reason (MFA challenged, password-set rejected by breach screen, lockout triggered).
  • A correlation ID so a login, its MFA step, and later actions can be stitched into one story.

What to NEVER log:

  • Passwords or password guesses, in any form.
  • Password hashes, session tokens, cookies, API keys, or private keys.
  • Full PANs or other regulated data; unneeded PII.

Events that signal abuse (of the surrounding system, since cracking itself is offline):

  • Bulk or full reads/exports of the user/credential table, especially off-hours or from new hosts.
  • A spike in successful logins from many accounts shortly after a suspected breach (cracked credentials being used).
  • Many password resets or sets being rejected by breach screening in a short window (someone testing).
  • Impossible-travel or new-device logins following a data-exfil alert.

How false positives arise:

  • Legitimate DBA maintenance or backups can look like a bulk table read — baseline and allowlist known jobs.
  • A new corporate proxy or VPN egress can trip "new host" rules — keep an approved-source inventory.
  • Bulk logins after an SSO change can mimic credential reuse — correlate with change tickets before alerting.

Tune thresholds against a known-good baseline so real signals aren't buried, and review alerts with the correlation ID rather than in isolation.

Real-world uses

Authorized real-world use case. During a scoped internal password audit, a security team is given a sanitised, in-scope export of Active Directory hashes on an isolated workstation. They run a time-boxed crack (dictionary + a standard ruleset) and report: crack rate, top password patterns (e.g. Season+Year+!), and how many accounts reused a breached password. Leadership uses this to justify migrating storage to a slow KDF, enforcing a length-based policy, and rolling out MFA. The team then re-tests after remediation to verify the crack rate dropped.

Professional best-practice habits.

Habit Beginner Advanced
Authorization Get written scope before touching any hash Maintain signed rules-of-engagement; log every action against the scope
Validation Confirm the hash format before cracking Verify sample provenance and sanitisation; ensure lab isolation (air-gapped/containers)
Least privilege Work on a dedicated, isolated machine Ephemeral, network-segmented cracking rigs; destroy artifacts after
Secure defaults (defence) Recommend bcrypt/Argon2 + salt Tune work factors to hardware; add breach screening + MFA + pepper where appropriate
Logging Note what you ran and found Deliver detection guidance: which logs reveal the breach and credential reuse
Error handling Stop if scope is unclear Escalate discovered plaintext handling per the engagement's data-handling rules

Misconceptions to correct in reports. Passing an automated password-strength scanner does not prove the store is secure. "Salted" does not imply "slow." And you should never claim a system is "completely secure" — report residual risk and the conditions under which it holds.

Practice tasks

All tasks are lab-only. Run them on hardware you own or in a container/VM, using only hashes you generate yourself or that a CTF/lab explicitly provides. Authorization checklist before any task: (1) Do I own this system or hold written authorization? (2) Is it network-isolated (localhost/container/air-gapped)? (3) Are all credentials synthetic/self-generated? (4) Do I have a cleanup/reset plan? If any answer is "no," stop.

Beginner 1 — Feel the mechanic.

  • Objective: Adapt the part-1 insecure demo so it reports how many guesses it took.
  • Requirements: Generate your own stolen hash from a word you place somewhere in a 20-entry wordlist; print the guess count.
  • Input/output: Output a single line with the recovered word and its position.
  • Constraints: No external files, no network; self-generated hash only.
  • Hints: Reuse fast_hash; increment a counter in the loop.
  • Concepts: dictionary attack, fast hashes.

Beginner 2 — Cost visualisation.

  • Objective: Modify the part-2 secure demo to print the ratio of secure-work to insecure-work per guess.
  • Requirements: Compute WORK rounds vs 1 round and print the multiplier.
  • Output: e.g. each guess is 200000x more expensive.
  • Constraints: Do not add real KDF libraries; the toy model is enough here.
  • Hints: The multiplier is just WORK.
  • Concepts: work factor, cracking economics.

Intermediate 1 — Breach-screen policy.

  • Objective: Extend the part-3 verifier with two more rules: reject passwords equal to the username, and reject any that are all digits.
  • Requirements: Add test cases proving each new rule rejects bad input and still accepts a strong passphrase.
  • Input/output: A PASS/FAIL table like the lesson's.
  • Constraints: Deterministic; exit non-zero on any failure.
  • Hints: A helper all_digits(pw) keeps password_allowed readable.
  • Concepts: breach screening, mitigation verification.
  • Defensive conclusion: State which rule blocked each bad password and how you verified good input still passes.

Intermediate 2 — Rule impact on yield (analysis).

  • Objective: Using a small self-made wordlist and your own generated hashes, measure how many more hits you get when you also try three mutations per word (append 1, append !, capitalise first letter).
  • Requirements: Report base hits vs mutated hits; no external targets.
  • Constraints: All hashes self-generated in the lab.
  • Hints: Apply mutations in an inner loop; count matches.
  • Concepts: rule-based attack, why composition rules fail.
  • Defensive conclusion: Recommend length + breach screening and explain why they beat the mutations you tested.

Challenge — Before/after remediation report.

  • Objective: Produce a mini written finding comparing a fast-hash store to a slow-KDF-plus-breach-screening store, using only your lab demos as evidence.
  • Requirements: Include title, severity (justified by exploitability + impact, not "critical by default"), affected component, safe reproduction (your lab steps), evidence (your demo outputs), impact, remediation (slow KDF + salt + breach screen + MFA), and a retest section showing the crack rate/cost change.
  • Constraints: No real targets, IPs, or secrets — use placeholders like API_KEY=<development-placeholder>; lab data only.
  • Hints: Severity should reflect that a fast-hash breach exposes reused passwords across other systems.
  • Concepts: reporting, mitigation verification, detection/logging.
  • Defensive conclusion + cleanup: End with detection guidance (which logs reveal the breach and credential reuse) and a cleanup/reset: delete generated hashes and wordlists, wipe the scratch directory, and shut down the isolated VM/container.

Summary

  • Offline cracking guesses passwords against stolen hashes on the attacker's own hardware — no lockout, no network noise — using four strategies: dictionary, rule-based, mask/brute force, and hybrid.
  • It is trivial when the hash is fast (MD5/SHA-1) and the password is weak or reused; it is defeated by making each guess expensive and each password unpredictable.
  • Cracking economics = hash speed x password entropy. A slow, salted KDF (bcrypt/scrypt/Argon2, tuned work factor) multiplies attacker cost by a million or more; salt alone stops precomputation but does not slow targeted guesses.
  • Key tools (conceptual): Hashcat and John the Ripper (offline), CeWL (wordlists), Hydra (online, next lesson).
  • Common mistakes: equating "hashed" with "safe," confusing salt with slowness, trusting composition rules, and — critically — cracking without authorization.
  • Defence-in-depth to recommend and verify: slow KDF + unique salt, length-based policy, breach screening, and MFA. Prove it by re-running a time-boxed lab crack after remediation and watching the crack rate collapse.
  • Detection: cracking itself is invisible; detect the breach that precedes it (bulk user-table exports, anomalous egress) and the reuse that follows it (impossible-travel logins). Log timestamps, source, resource, result, security decision, and a correlation ID; never log passwords, hashes, or tokens.
  • Remember: never crack outside an owned or explicitly authorized, isolated lab; decoding is not verifying, a passing scanner is not proof of security, and nothing is ever "completely secure."

Practice with these exercises