<!-- Copyright (c) 2026 defconxt. All rights reserved. -->
<!-- Licensed under AGPL-3.0 — see LICENSE file for details. -->
---
name: performing-password-hashing-audit
description: >-
  Audit password hashing implementations for strength, algorithm selection,
  and resistance to offline attacks. Covers bcrypt, scrypt, argon2, PBKDF2
  parameter validation, hash identification, and cracking resistance testing
  with hashcat and john for compliance and penetration testing.
domain: cybersecurity
subdomain: cryptography
tags:
  - password-hashing
  - bcrypt
  - argon2
  - hashcat
  - john
  - pbkdf2
  - credential-audit
version: "1.0"
author: defconxt
license: AGPL-3.0
metadata:
  mitre-attack: ["T1110"]
---

# Performing Password Hashing Audit

## Overview

Password hashing audits verify that stored credentials use appropriate
algorithms and parameters to resist offline brute-force attacks. Weak hashing
(MD5, SHA-1, unsalted SHA-256) enables mass credential recovery. This skill
covers hash identification, parameter strength assessment, and cracking
resistance testing with hashcat and John the Ripper.

## Prerequisites

| Requirement | Install |
|---|---|
| hashcat | `apt install hashcat` |
| john | `apt install john` |
| Python 3.10+ | For agent tooling |
| OpenSSL 3.x | `apt install openssl` |

## Key Concepts

### Hash Identification

```bash
# Identify hash type from a sample
hashcat --identify hash.txt

# Common hash format patterns
# $2b$12$...           -> bcrypt (cost 12)
# $argon2id$v=19$...   -> argon2id
# $6$rounds=5000$...   -> sha512crypt
# $pbkdf2-sha256$...   -> PBKDF2-SHA256
# 5f4dcc3b5aa765...    -> MD5 (unsalted, 32 hex chars)
# e38ad214943da...     -> SHA-1 (unsalted, 40 hex chars)

# Check hashcat mode for identified type
hashcat --help | grep -i bcrypt
```

### Algorithm Strength Assessment

```bash
# Test bcrypt cost factor timing
python3 -c "
import bcrypt, time
for cost in range(10, 16):
    start = time.time()
    bcrypt.hashpw(b'test', bcrypt.gensalt(rounds=cost))
    print(f'bcrypt cost {cost}: {time.time() - start:.3f}s')
"

# Test argon2 parameter timing
python3 -c "
from argon2 import PasswordHasher
import time
for mem in [65536, 131072, 262144]:
    ph = PasswordHasher(memory_cost=mem, time_cost=3, parallelism=4)
    start = time.time()
    ph.hash('test')
    print(f'argon2id memory={mem}KB: {time.time() - start:.3f}s')
"
```

### Cracking Resistance Testing

```bash
# Benchmark hashcat performance for various algorithms
hashcat -b -m 0    # MD5
hashcat -b -m 100  # SHA-1
hashcat -b -m 3200 # bcrypt

# Dictionary attack against bcrypt hashes
hashcat -m 3200 -a 0 hashes.txt /usr/share/wordlists/rockyou.txt

# Rule-based attack
hashcat -m 10900 -a 0 hashes.txt wordlist.txt -r rules/best64.rule

# John the Ripper with format detection
john --format=bcrypt hashes.txt --wordlist=/usr/share/wordlists/rockyou.txt
```

### OWASP Parameter Recommendations (2023)

| Algorithm | Minimum Parameters |
|---|---|
| Argon2id | 19 MiB memory, 2 iterations, 1 parallelism |
| bcrypt | Cost factor 10 (12+ recommended) |
| scrypt | N=2^17, r=8, p=1 |
| PBKDF2-SHA256 | 600,000 iterations |

## Common Failures

| Failure | Cause | Fix |
|---|---|---|
| MD5/SHA-1 in production | Legacy app never migrated | Progressive rehashing on login |
| bcrypt cost too low | Default cost 10 insufficient | Increase to cost 12+ and benchmark |
| Missing salt | Rainbow table vulnerable | Ensure unique per-user salts |
| PBKDF2 iterations too low | Set years ago, never updated | Increase to ≥600K per OWASP |

## Workflow

1. **Identify** — Extract and classify all password hash types
2. **Assess** — Evaluate algorithm and parameter strength
3. **Test** — Run controlled cracking attempts
4. **Report** — Document weak hashes with severity
5. **Remediate** — Implement progressive rehashing
6. **Verify** — Confirm new hashes meet target parameters

## Verification

| Check | Method |
|---|---|
| No weak algorithms | No MD5, SHA-1, or unsalted hashes |
| Parameters adequate | bcrypt ≥12, argon2id ≥19MB, PBKDF2 ≥600K |
| Unique salts | Every hash uses a unique per-user salt |
| Cracking resistant | Hashcat benchmark shows adequate time-per-guess |

## References

- [OWASP Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html)
- [hashcat Documentation](https://hashcat.net/wiki/)
- [NIST SP 800-63B — Digital Identity Guidelines](https://pages.nist.gov/800-63-3/sp800-63b.html)
- [RFC 9106 — Argon2 Memory-Hard Function](https://datatracker.ietf.org/doc/html/rfc9106)
