9 min read

Cryptographic Failures (OWASP A04)

Cryptographic failures (formerly "Sensitive Data Exposure") encompass weak encryption, poor key management, insufficient transport security, and plaintext storage of sensitive data. It ranks A04 in the OWASP Top 10:2025, down from #2 in 2021, though the impact is undimmed — cryptographic failures lead directly to data breaches.

A04:2025 groups four distinct failures, and they are tested differently. Weak algorithms: MD5 or SHA-1 in a password hash or signature, DES or RC4 in a cipher suite, ECB mode where a block cipher is used. Hard-coded keys: an encryption key or signing secret in a config file, bundle or repository. Missing transport validation: a client that accepts any certificate, so TLS protects the bytes without authenticating the peer. Insecure randomness: tokens built from Math.random(), rand(), timestamps or counters. Each has its own test and evidence artefact; do not report them as one category.

The shared precondition is an observable artefact — a certificate, a hash, a token, a ciphertext. Where you cannot observe one, the result is a configuration-review item, and should be phrased that way.

TLS/SSL Configuration Testing

testssl.sh answers the protocol and cipher questions in one pass: which versions are enabled, which suites are offered, whether the chain validates. Read the severity markers rather than the raw list — a modern stack still advertises CBC suites for compatibility, which is a note unless the client actually negotiates one.

tls-testing.sh
# Test SSL/TLS configuration:

# Use testssl.sh (comprehensive):
testssl.sh https://target.com

# Or sslyze:
sslyze target.com

# Quick nmap scan:
nmap --script ssl-enum-ciphers -p 443 target.com

# Check for specific weaknesses:

# Test for SSLv2/SSLv3 (should be disabled) — modern OpenSSL removed the
# -ssl2 probe, so use testssl.sh for SSLv2:
openssl s_client -ssl3 -connect target.com:443 2>&1 | head -5

# Test for TLS 1.0/1.1 (should be disabled):
openssl s_client -tls1 -connect target.com:443 2>&1 | head -5
openssl s_client -tls1_1 -connect target.com:443 2>&1 | head -5

# Check certificate details:
openssl s_client -connect target.com:443 2>/dev/null | openssl x509 -noout -text

# Check for weak ciphers:
nmap --script ssl-enum-ciphers -p 443 target.com | grep -E 'RC4|DES|NULL|EXPORT|anon'

# Check for HSTS header:
curl -sI https://target.com | grep -i strict-transport

Missing Certificate Validation

Server-side clients fail this quietly. A service that calls an HTTPS API with verification disabled (verify=False in requests, NODE_TLS_REJECT_UNAUTHORIZED=0, a trust-all certificate manager) will connect to an expired or self-signed certificate, and nothing in the response says so. Test through a URL the application fetches: point the parameter at a host with a deliberately invalid certificate and see whether the fetch succeeds. A success is the finding.

tls-validation-test.sh
# A host whose certificate is deliberately invalid for this purpose
# (self-signed.badssl.com serves a self-signed certificate)
curl -s -o /dev/null -w '%{http_code}\n' \
  'https://target.com/fetch?url=https://self-signed.badssl.com/'

# 200 with content from that host = the application does not validate certs
# 500 / "certificate verify failed" = validation happens
# If you control a domain, use it instead and watch for the request landing.

Hard-Coded Key Material

A hard-coded key is a cryptographic failure only when you can say what it protects. Locate it first — the retrieval paths are on the secrets exposure guide — then check whether the same value appears across environments. A key compiled into every build cannot be rotated per tenant, and one shared between staging and production makes the staging breach a production breach.

Password Storage Analysis

If you obtain password hashes (via SQL injection, database leak, backup files), first identify the algorithm, then assess its strength. The hash format prefix reveals the algorithm:

Hash Prefix / LengthAlgorithmStrength
$argon2id$...Argon2idStrongest
$2b$ / $2y$ / $2a$...bcryptStrong
$7$...scrypt / yescryptStrong
$6$...SHA-512 cryptWeak (fast hash)
$5$...SHA-256 cryptWeak (fast hash)
$1$...MD5 cryptWeak
32 hex charsMD5Very weak
40 hex charsSHA-1Weak
64 hex charsSHA-256Weak if unsalted

Quick indicators of bad password storage:

  • The application can email you your existing password → stored in plaintext or reversible encryption
  • Admin panel can display decrypted passwords → reversible encryption, not hashing
  • Two users with the same password have the same hash → no salt
Hashcat Commands for Cracking Weak Hashes
hashcat-examples.sh
hashcat -m 0 hashes.txt /usr/share/wordlists/rockyou.txt    # MD5
hashcat -m 100 hashes.txt /usr/share/wordlists/rockyou.txt  # SHA-1
hashcat -m 3200 hashes.txt /usr/share/wordlists/rockyou.txt # bcrypt

Data in Transit

data-in-transit-checks.sh
# Check for mixed content (HTTPS page loading HTTP resources):
curl -s https://target.com | grep -i 'http://' | grep -v 'https://'

# Check if HTTP redirects to HTTPS:
curl -sI http://target.com
# Should return 301/302 to https://
# If it returns 200 over HTTP → sensitive data sent in cleartext

# Check for sensitive data in URLs:
# URLs are logged in server logs, browser history, proxy logs
# BAD: https://target.com/login?token=abc123
# BAD: https://target.com/reset?email=user@example.com

# Check API authentication over HTTP:
curl -sI http://target.com/api/users -H 'Authorization: Bearer TOKEN'
# If API works over HTTP → credentials sent in cleartext

# Check for sensitive cookies without Secure flag:
curl -s -D - -o /dev/null -X POST -d 'user=test&pass=test' https://target.com/login | grep -i set-cookie
# Cookies should have: Secure; HttpOnly; SameSite=Lax

Data at Rest

Check whether sensitive data is stored without encryption. PCI DSS requires encryption of stored cardholder data, and applications should never store full card numbers (only last 4 digits).

Where to Check for Unencrypted Sensitive Data

  • • Database backups — are they encrypted?
  • • Log files — do they contain passwords, tokens, PII?
  • • Error messages — do they reveal database data?
  • • Browser localStorage / sessionStorage — plaintext tokens or PII?
  • • Cookies — are sensitive values encrypted?
data-at-rest-checks.sh
# Check for sensitive data in HTML source:
curl -s https://target.com/account | grep -iE 'ssn|credit.card|password|secret'

# Check for autocomplete on sensitive fields:
curl -s https://target.com/login | grep -i autocomplete
# Login fields should use autocomplete="current-password"; new-password fields
# should use autocomplete="new-password" (autocomplete="off" is widely ignored by browsers)

Weak Cryptographic Implementations

Predictable tokens are tested by collection, not inspection. Request twenty reset tokens in quick succession, sort them, and look at the deltas: timestamps produce near-constant gaps, counters advance by one, and Math.random() values cluster in ways a CSPRNG's do not. Base64-decode a token and see whether the bytes are a number. If a token is signed but its random component is weak, the signature does not save it.

ECB mode encryption preserves patterns — if the same input always produces the same output (e.g., in encrypted cookies), the application likely uses ECB mode. Also check for predictable tokens: if password reset tokens are sequential or time-based, they can be guessed.

To test ECB directly, submit two plaintexts that differ in one block and compare ciphertext blocks: identical input blocks produce identical output, which is the leak. A static IV in CBC produces the same first-block repetition, so check whether the prefix of a ciphertext changes when the first bytes of the plaintext do.

weak-crypto-tests.sh
# Generate multiple password reset tokens and look for patterns:
for i in $(seq 1 10); do
  curl -s -X POST https://target.com/forgot-password \
    -d 'email=test@example.com' \
    -c - | grep token
done

# Padding oracle test:
# If error messages differ between bad padding and bad data,
# a padding oracle attack is possible.
padbuster https://target.com/api ENCRYPTED_COOKIE 8 -encoding 0

Information

Tokens should use a CSPRNG (crypto.randomBytes in Node.js, secrets.token_hex in Python). If tokens appear to be based on timestamps, PIDs, or sequential counters, report as a predictable token vulnerability.

Testing Checklist

  • ☐ 1. Test TLS configuration (versions, ciphers, certificate validity)
  • ☐ 2. Check HTTP→HTTPS redirect and HSTS header
  • ☐ 3. Verify cookie security flags (Secure, HttpOnly, SameSite)
  • ☐ 4. Test password storage (hash algorithm, salting, strength)
  • ☐ 5. Check for sensitive data in URLs, logs, and error messages
  • ☐ 6. Check for sensitive data in browser storage
  • ☐ 7. Test token/nonce predictability
  • ☐ 8. Check for deprecated crypto (MD5, SHA-1, DES, RC4)
  • ☐ 9. Test for padding oracle vulnerabilities
  • ☐ 10. Test missing certificate validation by making the app fetch a host with an invalid certificate
  • ☐ 11. Collect 20 reset tokens and check for timestamp or counter structure
  • ☐ 12. Submit chosen plaintext and compare ciphertext blocks for ECB or a static IV

Evidence Collection

TLS Report: testssl.sh or sslyze output showing weak configuration

Hash Analysis: Identified hash algorithm and demonstrated weakness

Data Exposure: Screenshots of sensitive data in cleartext

Certificate Validation Test: The URL the application was asked to fetch, the invalid certificate it presented, and whether the fetch succeeded

Token Sample: Ten to twenty tokens collected in sequence, with the deltas or decoded integer shown (redact usable values)

Ciphertext Comparison: Two chosen plaintexts and their ciphertexts, highlighting the identical block or unchanged prefix

CVSS Range: Weak TLS: 5.3–7.5 | Plaintext passwords: 7.5–9.1 | No encryption on PII: 7.5–8.6

Remediation

  • • TLS 1.2+ only: Disable SSLv2, SSLv3, TLS 1.0, and TLS 1.1. Use strong cipher suites.
  • • HSTS: Enable Strict-Transport-Security with max-age=31536000 and includeSubDomains.
  • • bcrypt/Argon2: Hash passwords with bcrypt (cost 12+) or Argon2id. Never use MD5, SHA-1, or unsalted hashes.
  • • Encrypt PII at rest: Use AES-256-GCM for sensitive data encryption. Manage keys via KMS/HSM.
  • • CSPRNG: Use cryptographically secure random number generators for all tokens and secrets.
  • • Verify certificates on the client: Leave verification on for every outbound HTTPS call. A disabled check turns TLS into an encrypted channel to an unauthenticated peer.
  • • Separate keys per environment: Production key material must not exist in a staging build or a developer's .env. Load it from KMS or a secrets manager at runtime.

False Positive Identification

  • SHA-256 for non-password hashing: SHA-256 is inappropriate for passwords but perfectly fine for integrity checking, HMAC, and file hashing — assess usage context.
  • Short RSA keys in test environments: 1024-bit RSA keys in development/test may be intentional for speed — verify the production deployment uses appropriate key sizes.
  • CBC mode with proper IV: AES-CBC is not inherently broken — only report if the IV is static/predictable or if a padding oracle is exploitable.
  • A pinned self-signed certificate: An internal service may legitimately use a self-signed certificate with pinning at the client. The finding is a client that accepts any certificate, not one that trusts a specific one.
  • A token that only looks sequential: Base64 makes structured values look random and random values look structured. Decode before claiming a pattern, and collect enough samples that a coincidence is unlikely.

Cryptographic Attack Practice

Break weak cryptography hands-on rather than only reading about it.

CryptoHack Custom Lab hard
Padding oracleWeak hashingInsecure randomness
OWASP Juice Shop Custom Lab medium
Weak token generationExposed key material