#!/bin/bash # Authentication & Identity Hardening # Tests JWT abuse, session entropy, cookie security flags, credential hygiene SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../config.sh" OUT="$OUT/auth_hardening" mkdir -p "$OUT" echo '=== AUTHENTICATION & IDENTITY HARDENING ===' | tee "$OUT/summary.txt" url_encode() { printf '%s' "$1" | python3 -c "import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()))"; } BROWSER="$BROWSER_UA" # ── 1. Session Cookie Security Flags ──────────────────────────────────────── echo '' | tee -a "$OUT/summary.txt" echo '--- Session Cookie Security Flags ---' | tee -a "$OUT/summary.txt" cookie_hdr=$(curl -sk -I --max-time 10 -A "$BROWSER" "$TARGET/" | grep -i 'set-cookie') if [ -n "$cookie_hdr" ]; then echo "$cookie_hdr" | tee -a "$OUT/summary.txt" echo "$cookie_hdr" | grep -qi 'httponly' && echo " [PASS] HttpOnly present" || echo " [WARN] HttpOnly MISSING" | tee -a "$OUT/summary.txt" echo "$cookie_hdr" | grep -qi 'secure' && echo " [PASS] Secure present" || echo " [WARN] Secure MISSING" | tee -a "$OUT/summary.txt" echo "$cookie_hdr" | grep -qi 'samesite' && echo " [PASS] SameSite present" || echo " [WARN] SameSite MISSING" | tee -a "$OUT/summary.txt" echo "$cookie_hdr" | grep -qi '__Secure-\|__Host-' && echo " [PASS] Cookie prefix hardened" || echo " [INFO] No __Secure-/__Host- prefix" | tee -a "$OUT/summary.txt" else echo " [INFO] No Set-Cookie header on homepage" | tee -a "$OUT/summary.txt" fi # ── 2. JWT Algorithm Confusion (alg:none) ──────────────────────────────────── echo '' | tee -a "$OUT/summary.txt" echo '--- JWT Algorithm Confusion ---' | tee -a "$OUT/summary.txt" # Craft a token with alg:none and arbitrary claims NONE_HDR=$(printf '{"alg":"none","typ":"JWT"}' | python3 -c "import sys,base64; d=sys.stdin.read(); print(base64.urlsafe_b64encode(d.encode()).rstrip(b'=').decode())") CLAIMS=$(printf '{"sub":"admin","role":"admin","iat":1700000000,"exp":9999999999}' | python3 -c "import sys,base64; d=sys.stdin.read(); print(base64.urlsafe_b64encode(d.encode()).rstrip(b'=').decode())") NONE_TOKEN="${NONE_HDR}.${CLAIMS}." for ep in "/encrypt" "/decrypt" "/keys/generate" "/admin" "/users" "/config"; do resp=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 8 -A "$BROWSER" \ -H "Authorization: Bearer $NONE_TOKEN" "$API_TARGET$ep") [ "$resp" = "200" ] && echo " [!!!] JWT alg:none ACCEPTED on $ep" || echo " [PASS] $ep → $resp (rejected)" | tee -a "$OUT/summary.txt" done # ── 3. JWT kid Header Injection ────────────────────────────────────────────── echo '' | tee -a "$OUT/summary.txt" echo '--- JWT kid Header Injection ---' | tee -a "$OUT/summary.txt" for kid_val in "../../etc/passwd" "' OR 1=1--" "/dev/null" "../../../../dev/null"; do KID_HDR=$(printf "{\"alg\":\"HS256\",\"typ\":\"JWT\",\"kid\":\"$kid_val\"}" | python3 -c "import sys,base64; d=sys.stdin.read(); print(base64.urlsafe_b64encode(d.encode()).rstrip(b'=').decode())") KID_TOKEN="${KID_HDR}.${CLAIMS}.fakesig" resp=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 8 -A "$BROWSER" \ -H "Authorization: Bearer $KID_TOKEN" "$API_TARGET/encrypt") [ "$resp" = "200" ] && echo " [!!!] kid injection ACCEPTED: $kid_val" || echo " [PASS] kid='$kid_val' → $resp" | tee -a "$OUT/summary.txt" done # ── 4. Weak JWT Secret Brute Force (common secrets) ───────────────────────── echo '' | tee -a "$OUT/summary.txt" echo '--- JWT Weak Secret Check ---' | tee -a "$OUT/summary.txt" WEAK_SECRETS=("secret" "password" "123456" "jwt_secret" "changeme" "supersecret" "qwerty") WEAK_CLAIMS=$(printf '{"sub":"1","role":"user","exp":9999999999}') for secret in "${WEAK_SECRETS[@]}"; do token=$(python3 -c " import hmac, hashlib, base64, json h = base64.urlsafe_b64encode(b'{\"alg\":\"HS256\",\"typ\":\"JWT\"}').rstrip(b'=').decode() p = base64.urlsafe_b64encode(b'{\"sub\":\"1\",\"role\":\"user\",\"exp\":9999999999}').rstrip(b'=').decode() sig = hmac.new('$secret'.encode(), f'{h}.{p}'.encode(), hashlib.sha256).digest() s = base64.urlsafe_b64encode(sig).rstrip(b'=').decode() print(f'{h}.{p}.{s}') " 2>/dev/null) [ -n "$token" ] && { resp=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 8 -A "$BROWSER" \ -H "Authorization: Bearer $token" "$API_TARGET/encrypt") [ "$resp" = "200" ] && echo " [!!!] WEAK SECRET '$secret' ACCEPTED" || echo " [PASS] secret='$secret' → $resp" | tee -a "$OUT/summary.txt" } done # ── 5. Credential in URL / Referer Leakage ─────────────────────────────────── echo '' | tee -a "$OUT/summary.txt" echo '--- Credential in URL ---' | tee -a "$OUT/summary.txt" resp=$(curl -sk -o "$OUT/cred_url.txt" -w '%{http_code}' --max-time 8 -A "$BROWSER" \ "$API_TARGET/encrypt?api_key=test_key_12345&password=supersecret") grep -qi 'api_key\|password\|token\|secret' "$OUT/cred_url.txt" && \ echo " [WARN] Credentials in URL reflected in response" || echo " [PASS] Credentials in URL not reflected ($resp)" | tee -a "$OUT/summary.txt" # ── 6. Password Policy — Login Brute Force Without Lockout ────────────────── echo '' | tee -a "$OUT/summary.txt" echo '--- Login Brute Force (10 attempts, expect lockout or 401) ---' | tee -a "$OUT/summary.txt" lock=0 for i in $(seq 1 10); do resp=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 5 -A "$BROWSER" \ -X POST -H 'Content-Type: application/json' \ -d "{\"username\":\"admin\",\"password\":\"wrong$i\"}" \ "$API_TARGET/auth/login" 2>/dev/null) [ "$resp" = "429" ] || [ "$resp" = "423" ] || [ "$resp" = "403" ] && { lock=1; echo " [PASS] Lockout triggered at attempt $i ($resp)"; break; } done [ "$lock" -eq 0 ] && echo " [WARN] No lockout after 10 failed attempts (last: $resp)" | tee -a "$OUT/summary.txt" # ── 7. Refresh Token Replay ────────────────────────────────────────────────── echo '' | tee -a "$OUT/summary.txt" echo '--- Refresh Token Replay ---' | tee -a "$OUT/summary.txt" resp=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 8 -A "$BROWSER" \ -X POST -H 'Content-Type: application/json' \ -d '{"refresh_token":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIn0.fakesig"}' \ "$API_TARGET/auth/refresh") [ "$resp" = "200" ] && echo " [!!!] Fake refresh token ACCEPTED" || echo " [PASS] Fake refresh token rejected ($resp)" | tee -a "$OUT/summary.txt" # ── 8. HTTP Auth Header Enumeration ───────────────────────────────────────── echo '' | tee -a "$OUT/summary.txt" echo '--- Auth Scheme Enumeration ---' | tee -a "$OUT/summary.txt" for scheme in "Basic YWRtaW46YWRtaW4=" "Bearer null" "Bearer undefined" "Bearer 0" "Token admin"; do resp=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 8 -A "$BROWSER" \ -H "Authorization: $scheme" "$API_TARGET/encrypt") echo " [$resp] Authorization: $scheme" | tee -a "$OUT/summary.txt" done cat "$OUT/summary.txt" # ── 2FA / MFA Bypass ───────────────────────────────────────────────────────── OUT_FILE="$OUT/19_auth_hardening.txt" echo "" | tee -a "$OUT_FILE" echo "--- 2FA / MFA Bypass ---" | tee -a "$OUT_FILE" # OTP brute force — try common 6-digit codes (000000, 123456, 000001) echo " OTP brute force (sample codes):" | tee -a "$OUT_FILE" for OTP in 000000 123456 000001 111111 999999 123123 654321; do CODE=$(curl -sk --http2 -o /dev/null -w '%{http_code}' --max-time 8 \ -A "$BROWSER_UA" -X POST \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d "otp=$OTP&username=admin" \ "$TARGET/admin/2fa" 2>/dev/null || \ curl -sk --http2 -o /dev/null -w '%{http_code}' --max-time 8 \ -A "$BROWSER_UA" -X POST -H 'Content-Type: application/json' \ -d "{\"otp\":\"$OTP\",\"username\":\"admin\"}" \ "$API_TARGET/auth/verify-otp" 2>/dev/null) printf ' [%s] OTP brute force: %s\n' "$CODE" "$OTP" | tee -a "$OUT_FILE" done # OTP replay — submit same code twice OTP_REPLAY="123456" CODE1=$(curl -sk --http2 -o /dev/null -w '%{http_code}' --max-time 8 \ -A "$BROWSER_UA" -X POST -H 'Content-Type: application/json' \ -d "{\"otp\":\"$OTP_REPLAY\"}" "$API_TARGET/auth/verify-otp") CODE2=$(curl -sk --http2 -o /dev/null -w '%{http_code}' --max-time 8 \ -A "$BROWSER_UA" -X POST -H 'Content-Type: application/json' \ -d "{\"otp\":\"$OTP_REPLAY\"}" "$API_TARGET/auth/verify-otp") printf ' [%s→%s] OTP replay (same code submitted twice — both should fail)\n' "$CODE1" "$CODE2" | tee -a "$OUT_FILE" # Backup code enumeration echo " Backup code enumeration:" | tee -a "$OUT_FILE" for BACKUP in "00000000" "12345678" "AAAAAAAA" "backup1" "recovery1"; do CODE=$(curl -sk --http2 -o /dev/null -w '%{http_code}' --max-time 8 \ -A "$BROWSER_UA" -X POST -H 'Content-Type: application/json' \ -d "{\"backup_code\":\"$BACKUP\",\"username\":\"admin\"}" \ "$API_TARGET/auth/backup-code" 2>/dev/null || echo "404") printf ' [%s] Backup code: %s\n' "$CODE" "$BACKUP" | tee -a "$OUT_FILE" done # MFA skip — try to access protected resource with only step-1 auth token CODE=$(curl -sk --http2 -o /dev/null -w '%{http_code}' --max-time 8 \ -A "$BROWSER_UA" \ -H 'Authorization: Bearer PARTIAL_AUTH_STEP1_TOKEN' \ "$API_TARGET/encrypt") printf ' [%s] MFA skip — step-1 token on protected endpoint (should be 401)\n' "$CODE" | tee -a "$OUT_FILE" # 2FA endpoint enumeration echo " 2FA endpoint discovery:" | tee -a "$OUT_FILE" for EP in /2fa /mfa /verify /otp /totp /auth/verify /auth/otp /auth/mfa /auth/totp /api/auth/verify; do CODE=$(curl -sk --http2 -o /dev/null -w '%{http_code}' --max-time 5 -A "$BROWSER_UA" "$TARGET$EP") [ "$CODE" != "404" ] && [ "$CODE" != "000" ] && \ printf ' [%s] %s\n' "$CODE" "$EP" | tee -a "$OUT_FILE" done echo "" | tee -a "$OUT_FILE"