Cryptographic Failures
Theory
Formerly called Sensitive Data Exposure, this was renamed in 2021 to identify the root cause: weak or missing cryptography. It sits at #2 because almost every application stores or transmits passwords, tokens, or PII โ and the difference between a safe and a catastrophic breach often comes down to a single algorithm choice.
What Goes Wrong
- Passwords hashed with MD5 or SHA-1 โ these are general-purpose hashing algorithms, not password hashing functions. They are brutally fast (billions per second on a GPU), making every password in a leaked database crackable within hours
- Base64 used as "encryption" โ base64 is reversible encoding, not encryption.
atob("dXNlcjoxMjM=")in any browser returns the original string immediately - Data sent over HTTP โ credentials and session tokens travel in plaintext; any network observer captures them
- Hardcoded secrets in source code โ API keys and JWT secrets committed to git leak to anyone with repo access
- ECB mode symmetric encryption โ identical plaintext blocks produce identical ciphertext, leaking data patterns (see the famous ECB penguin image)
- Weak random number generation โ using
random.randint()or a timestamp as a secret/token is predictable
Why MD5 is Broken for Passwords
# MD5 of "admin123" = 0192023a7bbd73250516f069df18b500 # This exact hash is in every publicly available rainbow table. # Speed on a modern GPU: ~10 BILLION MD5 hashes per second. # The entire rockyou.txt wordlist (14 million passwords) takes < 1 second: $ hashcat -m 0 0192023a7bbd73250516f069df18b500 rockyou.txt Cracked: admin123 (0.4 seconds) # Compare bcrypt (cost 12) on the same hardware: # Speed: ~20,000 bcrypt hashes per second # Time to crack same wordlist: ~700 seconds per password (not per list)
Password Hashing Algorithm Comparison
Algorithm GPU Speed Salted? Designed for passwords? ----------- --------------- -------- ----------------------- MD5 10 billion/s No No โ general hashing SHA-256 3 billion/s No No โ general hashing bcrypt 20,000/s Yes Yes โ deliberately slow, adjustable cost Argon2id 1,000/s Yes Yes โ memory-hard; current best practice scrypt 10,000/s Yes Yes โ memory-hard alternative
Vulnerable Code Pattern
import hashlib, base64, json
# VULNERABLE โ MD5 is not a password hashing function
def store_password(password: str) -> str:
return hashlib.md5(password.encode()).hexdigest() # crackable in seconds
# VULNERABLE โ base64 is encoding, not encryption
def create_session(user_id: int, role: str) -> str:
data = json.dumps({"user_id": user_id, "role": role})
return base64.b64encode(data.encode()).decode() # trivially reversible
Fixed Code
import bcrypt, secrets
# FIXED โ bcrypt with cost factor 12 (~100ms per hash; brute-force infeasible)
def store_password(password: str) -> str:
return bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12)).decode()
def verify_password(plain: str, hashed: str) -> bool:
return bcrypt.checkpw(plain.encode(), hashed.encode())
# FIXED โ cryptographically random 256-bit session token
def create_session() -> str:
token = secrets.token_hex(32) # 32 bytes = 256 bits; stored server-side
session_store[token] = {"user_id": user.id, "role": user.role}
return token # only the opaque token goes in the Set-Cookie header
Real-World Breaches
- RockYou (2009) โ 32 million passwords stored in plaintext; became the source of the most-used password cracking wordlist ever
- LinkedIn (2012) โ 6.5 million unsalted SHA-1 password hashes leaked; all were cracked within days
- Adobe (2013) โ 153 million accounts; passwords encrypted with 3DES in ECB mode. The hints column let researchers decrypt millions without the key
- Ashley Madison (2015) โ 36 million accounts; many passwords stored in MD5; cracked and published publicly
How to Fix โ Checklist
- Passwords โ use bcrypt (cost โฅ 12), Argon2id, or scrypt. Never MD5, SHA-1, or SHA-256 alone
- Session tokens โ generate with
secrets.token_hex(32)(Python) orcrypto.randomBytes(32)(Node). The token is an opaque reference; all data lives server-side - Sensitive data at rest โ AES-256-GCM with authenticated encryption; store keys separately from data (HSM or secrets manager)
- TLS everywhere โ force HTTPS; set
Strict-Transport-Security: max-age=31536000; includeSubDomains - Secrets in config โ environment variables or a dedicated secrets manager (HashiCorp Vault, AWS Secrets Manager). Never commit secrets to git
Challenge 1
Weak Cookie โ Base64-encoded session
Log in as alice. Inspect the Set-Cookie header โ the session is just base64-encoded JSON. Decode it, change user_id to 1, re-encode, then call /web/a02/profile with your modified cookie.
Hint
echo '{"user_id": 2, "role": "user"}' | base64 โ modify user_id to 1
Challenge 2
MD5 Password Hashes Exposed in API
The user listing API returns MD5-hashed passwords. Crack the admin password hash using a rainbow table or hashcat.
Hint
MD5 is not a password hashing function โ /web/a02/api/users returns all hashes.