cryptography

Performing Hash Cracking with Hashcat

Hash cracking is an essential skill for penetration testers and security auditors to evaluate password strength. Hashcat is the world's fastest password recovery tool, supporting over 300 hash types with GPU acceleration. This skill covers using hashcat for authorized password auditing, understanding attack modes, creating effective rule sets, and generating hash analysis reports. This is strictly for authorized penetration testing and password policy assessment.

cryptographyhash-crackinghashcatpassword-securitypenetration-testing
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

Hash cracking is an essential skill for penetration testers and security auditors to evaluate password strength. Hashcat is the world's fastest password recovery tool, supporting over 300 hash types with GPU acceleration. This skill covers using hashcat for authorized password auditing, understanding attack modes, creating effective rule sets, and generating hash analysis reports. This is strictly for authorized penetration testing and password policy assessment.

When to Use

  • When conducting security assessments that involve performing hash cracking with hashcat
  • When following incident response procedures for related security events
  • When performing scheduled security testing or auditing activities
  • When validating security controls through hands-on testing

Prerequisites

  • Familiarity with cryptography concepts and tools
  • Access to a test or lab environment for safe execution
  • Python 3.8+ with required dependencies installed
  • Appropriate authorization for any testing activities

Objectives

  • Identify hash types from captured hashes
  • Execute dictionary, brute-force, and rule-based attacks
  • Create custom hashcat rules for targeted cracking
  • Analyze password strength from cracking results
  • Generate compliance reports on password policy effectiveness
  • Benchmark GPU performance for hash cracking

Key Concepts

Hashcat Attack Modes

Mode Flag Description Use Case
Dictionary -a 0 Wordlist attack Known password patterns
Combination -a 1 Combine two wordlists Compound passwords
Brute-force -a 3 Mask-based enumeration Short passwords
Rule-based -a 0 -r Dictionary + transformation rules Complex variations
Hybrid -a 6/7 Wordlist + mask Passwords with appended numbers

Common Hash Types

Hash Mode Type Example Use
0 MD5 Legacy web apps
100 SHA-1 Legacy systems
1000 NTLM Windows credentials
1800 sha512crypt Linux /etc/shadow
3200 bcrypt Modern web apps
13100 Kerberos TGS-REP Active Directory

Security Considerations

  • Only perform hash cracking with explicit written authorization
  • Secure all captured hash data in transit and at rest
  • Report all cracked passwords immediately to asset owners
  • Use results to improve password policies, not exploit users
  • Destroy cracked password data after engagement concludes
  • Follow rules of engagement for penetration test scope

Validation Criteria

  • Hash type identification is correct
  • Dictionary attack cracks weak passwords
  • Rule-based attack cracks policy-compliant passwords
  • Mask attack cracks short passwords
  • Results report shows password strength distribution
  • All operations performed within authorized scope
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md1.6 KB

API Reference — Performing Hash Cracking with Hashcat

Libraries Used

  • subprocess: Execute hashcat with various attack modes
  • hashlib: Generate test hashes (MD5, SHA1, SHA256, SHA512)
  • re: Pattern matching for hash type identification
  • pathlib: Read hash files and potfiles

CLI Interface

python agent.py identify --hash <hash_string>
python agent.py identify --file hashes.txt
python agent.py crack --hash-file hashes.txt --mode 0 --attack dictionary --wordlist rockyou.txt [--rules best64.rule]
python agent.py crack --hash-file hashes.txt --mode 1000 --attack brute --mask "?u?l?l?l?d?d?d?s"
python agent.py parse --potfile hashcat.potfile
python agent.py gen --text "password123" --algo sha256

Core Functions

identify_hash(hash_string) — Detect hash type and hashcat mode

Matches against 12 patterns: MD5 (0), SHA1 (100), SHA256 (1400), SHA512 (1700), NTLM (1000), bcrypt (3200), sha512crypt (1800), md5crypt (500), NetNTLMv2 (5600), Kerberos TGS (13100), Kerberos AS-REP (18200).

run_hashcat(hash_file, mode, attack, wordlist, rules, mask) — Execute hashcat

Attack modes: dictionary (-a 0), brute (-a 3), combinator (-a 1).

parse_hashcat_status(potfile) — Analyze cracked passwords

Returns length distribution, charset analysis, top passwords from potfile.

generate_hash(plaintext, algorithm) — Create test hashes

Supported: md5, sha1, sha256, sha512.

Hashcat Mask Charsets

  • ?l lowercase, ?u uppercase, ?d digits, ?s special, ?a all

Dependencies

System: hashcat (GPU-accelerated hash cracking) No Python packages required — standard library only.

standards.md1.0 KB

Standards and References - Hash Cracking with Hashcat

Tools

Hashcat

Wordlists

Standards

NIST SP 800-63B - Digital Identity Guidelines (Authentication)

OWASP Password Storage Cheat Sheet

Legal Framework

  • Computer Fraud and Abuse Act (CFAA) - US
  • Computer Misuse Act 1990 - UK
  • Always obtain written authorization before testing
  • Follow penetration testing rules of engagement
workflows.md1.3 KB

Workflows - Hash Cracking with Hashcat

Workflow 1: Hash Identification and Cracking

[Captured Hashes]
      |
[Identify Hash Type]
(hashcat --identify, hashid, haiti)
      |
[Select Attack Strategy]:
  1. Dictionary attack (rockyou.txt)
  2. Dictionary + rules (best64.rule)
  3. Mask attack (brute-force)
  4. Hybrid (wordlist + mask)
      |
[Execute Hashcat]
      |
[Analyze Results]
(cracked count, time, patterns)
      |
[Generate Password Strength Report]

Workflow 2: Hashcat Command Examples

# Dictionary attack
hashcat -m 0 -a 0 hashes.txt rockyou.txt
 
# Dictionary + rules
hashcat -m 0 -a 0 hashes.txt rockyou.txt -r best64.rule
 
# Mask (brute-force 8-char lowercase)
hashcat -m 0 -a 3 hashes.txt ?l?l?l?l?l?l?l?l
 
# Hybrid (wordlist + 4 digits)
hashcat -m 0 -a 6 hashes.txt rockyou.txt ?d?d?d?d
 
# Show cracked passwords
hashcat -m 0 hashes.txt --show

Workflow 3: Password Audit Report

[Cracking Results]
      |
[Categorize]:
  - Cracked in < 1 min: CRITICAL
  - Cracked in < 1 hour: HIGH
  - Cracked in < 1 day: MEDIUM
  - Not cracked: Acceptable
      |
[Pattern Analysis]:
  - Common base words
  - Character set distribution
  - Length distribution
  - Policy compliance
      |
[Generate Report with Recommendations]

Scripts 2

agent.py7.0 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for performing hash cracking with hashcat — hash identification, attack management, and result analysis."""

import json
import argparse
import subprocess
import hashlib
import re
from collections import Counter
from pathlib import Path


HASH_PATTERNS = {
    "MD5": (r"^[a-f0-9]{32}$", 0),
    "SHA1": (r"^[a-f0-9]{40}$", 100),
    "SHA256": (r"^[a-f0-9]{64}$", 1400),
    "SHA512": (r"^[a-f0-9]{128}$", 1700),
    "NTLM": (r"^[a-f0-9]{32}$", 1000),
    "bcrypt": (r"^\$2[aby]?\$\d+\$.{53}$", 3200),
    "sha512crypt": (r"^\$6\$[^\$]+\$[a-zA-Z0-9./]{86}$", 1800),
    "sha256crypt": (r"^\$5\$[^\$]+\$[a-zA-Z0-9./]{43}$", 7400),
    "md5crypt": (r"^\$1\$[^\$]+\$[a-zA-Z0-9./]{22}$", 500),
    "NetNTLMv2": (r"^[^:]+::\S+:[a-f0-9]{16}:[a-f0-9]{32}:[a-f0-9]+$", 5600),
    "Kerberos_TGS": (r"^\$krb5tgs\$", 13100),
    "Kerberos_AS": (r"^\$krb5asrep\$", 18200),
}


def identify_hash(hash_string):
    """Identify hash type and return hashcat mode."""
    candidates = []
    for name, (pattern, mode) in HASH_PATTERNS.items():
        if re.match(pattern, hash_string.strip(), re.IGNORECASE):
            candidates.append({"type": name, "hashcat_mode": mode})
    return {"hash": hash_string[:40] + "..." if len(hash_string) > 40 else hash_string, "candidates": candidates}


def identify_hashes_file(hash_file):
    """Identify hash types from a file of hashes."""
    hashes = Path(hash_file).read_text(encoding="utf-8", errors="replace").strip().splitlines()
    results = []
    for h in hashes[:100]:
        h = h.strip()
        if h:
            results.append(identify_hash(h))
    types = {}
    for r in results:
        for c in r["candidates"]:
            types[c["type"]] = types.get(c["type"], 0) + 1
    return {"total_hashes": len(results), "type_distribution": types, "samples": results[:5]}


def run_hashcat(hash_file, mode, attack="dictionary", wordlist=None, rules=None, mask=None):
    """Execute hashcat with specified attack mode."""
    cmd = ["hashcat", "-m", str(mode), "--quiet", "--potfile-disable", "-o", "/tmp/hashcat_out.txt"]
    if attack == "dictionary":
        if not wordlist:
            return {"error": "wordlist required for dictionary attack"}
        cmd += ["-a", "0", hash_file, wordlist]
        if rules:
            cmd += ["-r", rules]
    elif attack == "brute":
        m = mask or "?a?a?a?a?a?a?a?a"
        cmd += ["-a", "3", hash_file, m]
    elif attack == "combinator":
        if not wordlist:
            return {"error": "two wordlists required (comma-separated)"}
        wl = wordlist.split(",")
        if len(wl) != 2:
            return {"error": "combinator needs two wordlists: list1.txt,list2.txt"}
        cmd += ["-a", "1", hash_file, wl[0], wl[1]]
    else:
        return {"error": f"Unknown attack type: {attack}"}
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=3600)
        cracked = []
        out_file = Path("/tmp/hashcat_out.txt")
        if out_file.exists():
            for line in out_file.read_text().strip().splitlines():
                parts = line.split(":", 1)
                if len(parts) == 2:
                    cracked.append({"hash": parts[0][:30] + "...", "plain": parts[1]})
        return {
            "attack": attack, "mode": mode, "cracked_count": len(cracked),
            "cracked": cracked[:50], "return_code": result.returncode,
            "stderr_snippet": result.stderr[:300] if result.stderr else "",
        }
    except FileNotFoundError:
        return {"error": "hashcat not found in PATH"}
    except subprocess.TimeoutExpired:
        return {"error": "hashcat timed out after 3600s"}


def parse_hashcat_status(potfile):
    """Parse hashcat potfile for cracked results."""
    cracked = []
    try:
        for line in Path(potfile).read_text(encoding="utf-8", errors="replace").strip().splitlines():
            parts = line.split(":", 1)
            if len(parts) == 2:
                cracked.append({"hash": parts[0], "plain": parts[1]})
    except FileNotFoundError:
        return {"error": f"Potfile not found: {potfile}"}
    passwords = [c["plain"] for c in cracked]
    length_dist = {}
    for p in passwords:
        l = len(p)
        bucket = f"{l}" if l <= 8 else "9-12" if l <= 12 else "13+"
        length_dist[bucket] = length_dist.get(bucket, 0) + 1
    charset = {"lowercase_only": 0, "uppercase_mixed": 0, "with_digits": 0, "with_special": 0}
    for p in passwords:
        if re.match(r"^[a-z]+$", p):
            charset["lowercase_only"] += 1
        elif re.search(r"\d", p):
            charset["with_digits"] += 1
        elif re.search(r"[!@#$%^&*()_+=\-\[\]{};:'\",.<>?/\\|`~]", p):
            charset["with_special"] += 1
        else:
            charset["uppercase_mixed"] += 1
    return {
        "total_cracked": len(cracked),
        "length_distribution": length_dist,
        "charset_analysis": charset,
        "top_passwords": [p for p, _ in Counter(passwords).most_common(10)],
    }


def generate_hash(plaintext, algorithm="sha256"):
    """Generate hash of a plaintext string for testing."""
    algos = {"md5": hashlib.md5, "sha1": hashlib.sha1, "sha256": hashlib.sha256, "sha512": hashlib.sha512}
    if algorithm not in algos:
        return {"error": f"Unsupported: {algorithm}. Use: {list(algos.keys())}"}
    h = algos[algorithm](plaintext.encode()).hexdigest()
    return {"plaintext": plaintext, "algorithm": algorithm, "hash": h}


def main():
    parser = argparse.ArgumentParser(description="Hashcat Hash Cracking Agent")
    sub = parser.add_subparsers(dest="command")
    i = sub.add_parser("identify", help="Identify hash type")
    i.add_argument("--hash", help="Single hash string")
    i.add_argument("--file", help="File containing hashes")
    c = sub.add_parser("crack", help="Run hashcat")
    c.add_argument("--hash-file", required=True)
    c.add_argument("--mode", type=int, required=True, help="Hashcat mode number")
    c.add_argument("--attack", default="dictionary", choices=["dictionary", "brute", "combinator"])
    c.add_argument("--wordlist", help="Wordlist path (or two comma-separated for combinator)")
    c.add_argument("--rules", help="Hashcat rules file")
    c.add_argument("--mask", help="Brute force mask")
    p = sub.add_parser("parse", help="Parse potfile results")
    p.add_argument("--potfile", required=True)
    g = sub.add_parser("gen", help="Generate test hash")
    g.add_argument("--text", required=True)
    g.add_argument("--algo", default="sha256")
    args = parser.parse_args()
    if args.command == "identify":
        result = identify_hashes_file(args.file) if args.file else identify_hash(args.hash) if args.hash else {"error": "provide --hash or --file"}
    elif args.command == "crack":
        result = run_hashcat(args.hash_file, args.mode, args.attack, args.wordlist, args.rules, args.mask)
    elif args.command == "parse":
        result = parse_hashcat_status(args.potfile)
    elif args.command == "gen":
        result = generate_hash(args.text, args.algo)
    else:
        parser.print_help()
        return
    print(json.dumps(result, indent=2, default=str))


if __name__ == "__main__":
    main()
process.py9.5 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
Hash Cracking Analysis and Hashcat Automation Tool

Provides hash identification, hashcat command generation, result analysis,
and password strength reporting for authorized security assessments.

Requirements:
    pip install passlib argon2-cffi

Usage:
    python process.py identify --hash "5f4dcc3b5aa765d61d8327deb882cf99"
    python process.py generate-cmd --hash-file hashes.txt --mode 0 --attack dictionary
    python process.py analyze-results --potfile hashcat.potfile --hash-file hashes.txt
    python process.py create-test-hashes --output test_hashes.txt
"""

import os
import re
import sys
import json
import hashlib
import argparse
import logging
from pathlib import Path
from typing import Dict, List, Optional, Tuple
from collections import Counter

logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)

HASH_PATTERNS = {
    r"^[a-f0-9]{32}$": [("MD5", 0), ("NTLM", 1000)],
    r"^[a-f0-9]{40}$": [("SHA-1", 100)],
    r"^[a-f0-9]{64}$": [("SHA-256", 1400)],
    r"^[a-f0-9]{128}$": [("SHA-512", 1700)],
    r"^\$2[aby]\$\d{2}\$.{53}$": [("bcrypt", 3200)],
    r"^\$6\$": [("sha512crypt", 1800)],
    r"^\$5\$": [("sha256crypt", 7400)],
    r"^\$argon2(i|d|id)\$": [("Argon2", 32600)],
    r"^\$1\$": [("md5crypt", 500)],
    r"^\$apr1\$": [("Apache APR1", 1600)],
    r"^[a-f0-9]{32}:[a-f0-9]+$": [("MD5 salted", 10)],
    r"^\$krb5tgs\$": [("Kerberos TGS-REP", 13100)],
    r"^[a-f0-9]{32}:[a-f0-9]{32}$": [("NetNTLMv1", 5500)],
}


def identify_hash(hash_value: str) -> List[Dict]:
    """Identify the type of a hash value."""
    hash_value = hash_value.strip()
    matches = []

    for pattern, hash_types in HASH_PATTERNS.items():
        if re.match(pattern, hash_value, re.IGNORECASE):
            for name, mode in hash_types:
                matches.append({
                    "hash_type": name,
                    "hashcat_mode": mode,
                    "confidence": "high" if len(hash_types) == 1 else "medium",
                })

    if not matches:
        matches.append({
            "hash_type": "Unknown",
            "hashcat_mode": None,
            "confidence": "none",
        })

    return matches


def generate_hashcat_command(
    hash_file: str,
    mode: int,
    attack: str = "dictionary",
    wordlist: str = "rockyou.txt",
    rules: Optional[str] = None,
    mask: Optional[str] = None,
    output_file: Optional[str] = None,
) -> str:
    """Generate a hashcat command string."""
    cmd_parts = ["hashcat"]
    cmd_parts.extend(["-m", str(mode)])

    if attack == "dictionary":
        cmd_parts.extend(["-a", "0"])
        cmd_parts.append(hash_file)
        cmd_parts.append(wordlist)
        if rules:
            cmd_parts.extend(["-r", rules])
    elif attack == "bruteforce":
        cmd_parts.extend(["-a", "3"])
        cmd_parts.append(hash_file)
        cmd_parts.append(mask or "?a?a?a?a?a?a?a?a")
    elif attack == "hybrid_wm":
        cmd_parts.extend(["-a", "6"])
        cmd_parts.append(hash_file)
        cmd_parts.append(wordlist)
        cmd_parts.append(mask or "?d?d?d?d")
    elif attack == "hybrid_mw":
        cmd_parts.extend(["-a", "7"])
        cmd_parts.append(hash_file)
        cmd_parts.append(mask or "?d?d?d?d")
        cmd_parts.append(wordlist)

    if output_file:
        cmd_parts.extend(["-o", output_file])

    cmd_parts.extend(["--force", "--status", "--status-timer=10"])
    return " ".join(cmd_parts)


def create_test_hashes(output_path: str) -> Dict:
    """Create a set of test password hashes for practice."""
    test_passwords = [
        "password", "123456", "admin", "letmein", "welcome",
        "monkey", "dragon", "master", "qwerty", "login",
        "P@ssw0rd", "Summer2024!", "company123", "test1234",
        "hunter2", "trustno1", "batman", "shadow", "sunshine",
        "iloveyou",
    ]

    hashes = {"md5": [], "sha1": [], "sha256": []}
    for pwd in test_passwords:
        pwd_bytes = pwd.encode("utf-8")
        hashes["md5"].append(hashlib.md5(pwd_bytes).hexdigest())
        hashes["sha1"].append(hashlib.sha1(pwd_bytes).hexdigest())
        hashes["sha256"].append(hashlib.sha256(pwd_bytes).hexdigest())

    output = Path(output_path)
    for hash_type, hash_list in hashes.items():
        path = output.parent / f"{output.stem}_{hash_type}{output.suffix}"
        path.write_text("\n".join(hash_list) + "\n")
        logger.info(f"Created {path} with {len(hash_list)} hashes")

    return {
        "test_passwords_count": len(test_passwords),
        "hash_types": list(hashes.keys()),
        "output_directory": str(output.parent),
        "note": "These are intentionally weak passwords for authorized testing only",
    }


def analyze_cracked_results(potfile_path: str, hash_file_path: str) -> Dict:
    """Analyze hashcat results from potfile."""
    potfile = Path(potfile_path)
    hash_file = Path(hash_file_path)

    if not potfile.exists():
        return {"error": f"Potfile not found: {potfile_path}"}

    total_hashes = 0
    if hash_file.exists():
        total_hashes = sum(1 for line in hash_file.read_text().strip().split("\n") if line.strip())

    cracked_passwords = []
    for line in potfile.read_text().strip().split("\n"):
        if ":" in line:
            parts = line.rsplit(":", 1)
            if len(parts) == 2:
                cracked_passwords.append(parts[1])

    cracked_count = len(cracked_passwords)
    crack_rate = (cracked_count / total_hashes * 100) if total_hashes > 0 else 0

    # Password analysis
    lengths = [len(p) for p in cracked_passwords]
    charset_analysis = {"lowercase_only": 0, "with_uppercase": 0, "with_digits": 0, "with_special": 0}

    for pwd in cracked_passwords:
        has_upper = any(c.isupper() for c in pwd)
        has_digit = any(c.isdigit() for c in pwd)
        has_special = any(not c.isalnum() for c in pwd)
        if has_special:
            charset_analysis["with_special"] += 1
        elif has_digit and has_upper:
            charset_analysis["with_digits"] += 1
        elif has_upper:
            charset_analysis["with_uppercase"] += 1
        else:
            charset_analysis["lowercase_only"] += 1

    # Strength categories
    strength = {"critical": 0, "weak": 0, "moderate": 0}
    for pwd in cracked_passwords:
        if len(pwd) < 8:
            strength["critical"] += 1
        elif len(pwd) < 12 or not any(not c.isalnum() for c in pwd):
            strength["weak"] += 1
        else:
            strength["moderate"] += 1

    common_words = Counter()
    for pwd in cracked_passwords:
        base = re.sub(r"[^a-zA-Z]", "", pwd).lower()
        if len(base) >= 3:
            common_words[base] += 1

    return {
        "total_hashes": total_hashes,
        "cracked_count": cracked_count,
        "crack_rate_percent": round(crack_rate, 1),
        "uncracked_count": total_hashes - cracked_count,
        "password_length": {
            "min": min(lengths) if lengths else 0,
            "max": max(lengths) if lengths else 0,
            "avg": round(sum(lengths) / len(lengths), 1) if lengths else 0,
        },
        "charset_distribution": charset_analysis,
        "strength_distribution": strength,
        "top_base_words": dict(common_words.most_common(10)),
        "recommendations": [
            "Enforce minimum 12-character passwords",
            "Require mixed character sets (upper, lower, digit, special)",
            "Block common dictionary words and patterns",
            "Implement password breach checking (Have I Been Pwned API)",
            "Use Argon2id or bcrypt for password hashing (not MD5/SHA)",
            "Consider passphrase-based policies over complexity rules",
        ],
    }


def main():
    parser = argparse.ArgumentParser(description="Hash Cracking Analysis Tool")
    subparsers = parser.add_subparsers(dest="command")

    ident = subparsers.add_parser("identify", help="Identify hash type")
    ident.add_argument("--hash", required=True, help="Hash value to identify")

    gen = subparsers.add_parser("generate-cmd", help="Generate hashcat command")
    gen.add_argument("--hash-file", required=True, help="Hash file path")
    gen.add_argument("--mode", type=int, required=True, help="Hashcat hash mode")
    gen.add_argument("--attack", choices=["dictionary", "bruteforce", "hybrid_wm", "hybrid_mw"], default="dictionary")
    gen.add_argument("--wordlist", default="rockyou.txt")
    gen.add_argument("--rules", help="Rules file")
    gen.add_argument("--mask", help="Mask pattern")

    create = subparsers.add_parser("create-test-hashes", help="Create test hashes")
    create.add_argument("--output", "-o", default="test_hashes.txt")

    analyze = subparsers.add_parser("analyze-results", help="Analyze cracking results")
    analyze.add_argument("--potfile", required=True, help="Hashcat potfile")
    analyze.add_argument("--hash-file", required=True, help="Original hash file")

    args = parser.parse_args()

    if args.command == "identify":
        result = identify_hash(args.hash)
        print(json.dumps(result, indent=2))
    elif args.command == "generate-cmd":
        cmd = generate_hashcat_command(args.hash_file, args.mode, args.attack, args.wordlist, args.rules, args.mask)
        print(cmd)
    elif args.command == "create-test-hashes":
        result = create_test_hashes(args.output)
        print(json.dumps(result, indent=2))
    elif args.command == "analyze-results":
        result = analyze_cracked_results(args.potfile, args.hash_file)
        print(json.dumps(result, indent=2))
    else:
        parser.print_help()


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 1.1 KB
Keep exploring