| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
|
| 6 |
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 7 |
source "$SCRIPT_DIR/../config.sh" |
| 8 |
|
| 9 |
OUT_FILE="$OUT/23_client_side_js.txt" |
| 10 |
echo "=== 23. Client-Side & JavaScript Security ===" | tee "$OUT_FILE" |
| 11 |
echo "Target: $TARGET" | tee -a "$OUT_FILE" |
| 12 |
echo "" | tee -a "$OUT_FILE" |
| 13 |
|
| 14 |
W() { printf '%s' "$1" | python3 -c "import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()))"; } |
| 15 |
|
| 16 |
do_test() { |
| 17 |
local label="$1"; shift |
| 18 |
local code |
| 19 |
code=$(curl -sk -o /dev/null -w '%{http_code}' "$@") |
| 20 |
printf '[%s] %s\n' "$code" "$label" | tee -a "$OUT_FILE" |
| 21 |
} |
| 22 |
|
| 23 |
do_test_headers() { |
| 24 |
local label="$1"; shift |
| 25 |
local out |
| 26 |
out=$(curl -sk -D - -o /dev/null "$@") |
| 27 |
local code=$(echo "$out" | head -1 | grep -oP '\d{3}' | head -1) |
| 28 |
printf '[%s] %s\n' "$code" "$label" | tee -a "$OUT_FILE" |
| 29 |
| 30 |
echo "$out" | grep -iE '^(x-frame-options|content-security-policy|x-content-type-options|cache-control|referrer-policy|permissions-policy|cross-origin|vary):' | sed 's/^/ /' | tee -a "$OUT_FILE" |
| 31 |
} |
| 32 |
|
| 33 |
| 34 |
echo "--- Clickjacking / Framing Controls ---" | tee -a "$OUT_FILE" |
| 35 |
do_test_headers "Main page framing headers" -A "$BROWSER_UA" "$TARGET/" |
| 36 |
do_test_headers "Encrypt page framing headers" -A "$BROWSER_UA" "$TARGET/crypt.php" |
| 37 |
do_test_headers "Admin page framing headers" -A "$BROWSER_UA" "$TARGET/admin/" |
| 38 |
echo "" | tee -a "$OUT_FILE" |
| 39 |
|
| 40 |
| 41 |
echo "--- Content-Type Sniffing ---" | tee -a "$OUT_FILE" |
| 42 |
do_test_headers "Static JS asset headers" -A "$BROWSER_UA" "$TARGET/dist/assets/index.js" 2>/dev/null || \ |
| 43 |
do_test_headers "CSS asset headers" -A "$BROWSER_UA" "$TARGET/css/homepage.css" |
| 44 |
do_test_headers "API JSON response headers" -A "$BROWSER_UA" \ |
| 45 |
-H 'Content-Type: application/json' \ |
| 46 |
-d '{"test":"x"}' \ |
| 47 |
"$API_TARGET/status" |
| 48 |
echo "" | tee -a "$OUT_FILE" |
| 49 |
|
| 50 |
| 51 |
echo "--- MIME Confusion / Content Sniffing Attack ---" | tee -a "$OUT_FILE" |
| 52 |
do_test "Script with Accept: text/plain (MIME sniff probe)" \ |
| 53 |
-A "$BROWSER_UA" -H "Accept: text/plain" "$TARGET/js/crypt.js" 2>/dev/null || \ |
| 54 |
do_test "CSS with Accept: text/plain" \ |
| 55 |
-A "$BROWSER_UA" -H "Accept: text/plain" "$TARGET/css/homepage.css" |
| 56 |
echo "" | tee -a "$OUT_FILE" |
| 57 |
|
| 58 |
| 59 |
echo "--- DOM XSS / Reflected Parameter Vectors ---" | tee -a "$OUT_FILE" |
| 60 |
XSS_FRAG=$(W "<script>alert(1)</script>") |
| 61 |
XSS_IMG=$(W "<img src=x onerror=alert(1)>") |
| 62 |
XSS_SVG=$(W "<svg/onload=alert(1)>") |
| 63 |
|
| 64 |
do_test "XSS via ?q= param" -A "$BROWSER_UA" "$TARGET/?q=$XSS_FRAG" |
| 65 |
do_test "XSS via ?redirect= param" -A "$BROWSER_UA" "$TARGET/?redirect=$XSS_FRAG" |
| 66 |
do_test "XSS via ?msg= param" -A "$BROWSER_UA" "$TARGET/?msg=$XSS_IMG" |
| 67 |
do_test "XSS via ?error= param" -A "$BROWSER_UA" "$TARGET/?error=$XSS_SVG" |
| 68 |
do_test "XSS via ?page= param" -A "$BROWSER_UA" "$TARGET/?page=$XSS_FRAG" |
| 69 |
do_test "XSS via ?url= param (possible open redirect)" -A "$BROWSER_UA" "$TARGET/?url=$(W 'javascript:alert(1)')" |
| 70 |
echo "" | tee -a "$OUT_FILE" |
| 71 |
|
| 72 |
| 73 |
echo "--- Open Redirect ---" | tee -a "$OUT_FILE" |
| 74 |
do_test "Open redirect ?next=//evil.com" -A "$BROWSER_UA" "$TARGET/?next=$(W '//evil.com')" |
| 75 |
do_test "Open redirect ?redirect=https://evil.com" -A "$BROWSER_UA" "$TARGET/?redirect=$(W 'https://evil.com')" |
| 76 |
do_test "Open redirect ?return_url=//evil.com" -A "$BROWSER_UA" "$TARGET/?return_url=$(W '//evil.com')" |
| 77 |
echo "" | tee -a "$OUT_FILE" |
| 78 |
|
| 79 |
| 80 |
echo "--- Prototype Pollution (API JSON body) ---" | tee -a "$OUT_FILE" |
| 81 |
do_test "Prototype pollution __proto__" \ |
| 82 |
-A "$BROWSER_UA" -X POST -H 'Content-Type: application/json' \ |
| 83 |
-d '{"__proto__":{"isAdmin":true},"data":"test"}' \ |
| 84 |
"$API_TARGET/encrypt" |
| 85 |
do_test "Prototype pollution constructor.prototype" \ |
| 86 |
-A "$BROWSER_UA" -X POST -H 'Content-Type: application/json' \ |
| 87 |
-d '{"constructor":{"prototype":{"isAdmin":true}},"data":"test"}' \ |
| 88 |
"$API_TARGET/encrypt" |
| 89 |
do_test "Prototype pollution via nested __proto__" \ |
| 90 |
-A "$BROWSER_UA" -X POST -H 'Content-Type: application/json' \ |
| 91 |
-d '{"a":{"__proto__":{"x":1}},"data":"test"}' \ |
| 92 |
"$API_TARGET/encrypt" |
| 93 |
echo "" | tee -a "$OUT_FILE" |
| 94 |
|
| 95 |
| 96 |
echo "--- CSRF / Cookie Security ---" | tee -a "$OUT_FILE" |
| 97 |
CSRF_HEADERS=$(curl -sk -D - -o /dev/null -A "$BROWSER_UA" "$TARGET/") |
| 98 |
echo "Cookie flags on main page:" | tee -a "$OUT_FILE" |
| 99 |
echo "$CSRF_HEADERS" | grep -i 'set-cookie:' | sed 's/^/ /' | tee -a "$OUT_FILE" |
| 100 |
| 101 |
if echo "$CSRF_HEADERS" | grep -qi 'set-cookie:'; then |
| 102 |
echo "$CSRF_HEADERS" | grep -i 'set-cookie:' | grep -viE '(httponly|secure|samesite)' | \ |
| 103 |
sed 's/^/ [WARN] Missing security flag: /' | tee -a "$OUT_FILE" |
| 104 |
else |
| 105 |
echo " [OK] No Set-Cookie on main page (stateless/API)" | tee -a "$OUT_FILE" |
| 106 |
fi |
| 107 |
|
| 108 |
| 109 |
do_test "Cross-origin POST (no Origin header - CSRF simulation)" \ |
| 110 |
-X POST -H 'Content-Type: application/x-www-form-urlencoded' \ |
| 111 |
-d 'action=test&data=csrf_test' \ |
| 112 |
"$TARGET/" |
| 113 |
do_test "Cross-origin POST with foreign Origin header" \ |
| 114 |
-X POST -H 'Origin: https://evil.com' \ |
| 115 |
-H 'Content-Type: application/x-www-form-urlencoded' \ |
| 116 |
-d 'action=test&data=csrf_test' \ |
| 117 |
"$TARGET/" |
| 118 |
echo "" | tee -a "$OUT_FILE" |
| 119 |
|
| 120 |
| 121 |
echo "--- Service Worker / PWA Paths ---" | tee -a "$OUT_FILE" |
| 122 |
do_test "Service worker path /sw.js" -A "$BROWSER_UA" "$TARGET/sw.js" |
| 123 |
do_test "Service worker path /service-worker.js" -A "$BROWSER_UA" "$TARGET/service-worker.js" |
| 124 |
do_test "PWA manifest /manifest.json" -A "$BROWSER_UA" "$TARGET/manifest.json" |
| 125 |
do_test "PWA manifest /manifest.webmanifest" -A "$BROWSER_UA" "$TARGET/manifest.webmanifest" |
| 126 |
do_test "Worker /worker.js" -A "$BROWSER_UA" "$TARGET/worker.js" |
| 127 |
echo "" | tee -a "$OUT_FILE" |
| 128 |
|
| 129 |
| 130 |
echo "--- Subresource Integrity (SRI) ---" | tee -a "$OUT_FILE" |
| 131 |
PAGE_HTML=$(curl -sk -A "$BROWSER_UA" "$TARGET/") |
| 132 |
SCRIPT_COUNT=$(echo "$PAGE_HTML" | grep -c '<script') |
| 133 |
SRI_COUNT=$(echo "$PAGE_HTML" | grep -c 'integrity=') |
| 134 |
EXTERNAL_SCRIPTS=$(echo "$PAGE_HTML" | grep -oP '<script[^>]+src="https?://(?!${TARGET_HOST_ESCAPED})[^"]+' | wc -l) |
| 135 |
printf ' Script tags: %d | With SRI integrity attr: %d | External (non-${TARGET_HOST}): %d\n' \ |
| 136 |
"$SCRIPT_COUNT" "$SRI_COUNT" "$EXTERNAL_SCRIPTS" | tee -a "$OUT_FILE" |
| 137 |
if [ "$EXTERNAL_SCRIPTS" -gt 0 ]; then |
| 138 |
echo " [WARN] External scripts found without SRI:" | tee -a "$OUT_FILE" |
| 139 |
echo "$PAGE_HTML" | grep -oP '<script[^>]+src="https?://(?!${TARGET_HOST_ESCAPED})[^"]+"[^>]*>' | sed 's/^/ /' | tee -a "$OUT_FILE" |
| 140 |
else |
| 141 |
echo " [OK] No cross-origin scripts detected" | tee -a "$OUT_FILE" |
| 142 |
fi |
| 143 |
echo "" | tee -a "$OUT_FILE" |
| 144 |
|
| 145 |
| 146 |
echo "--- Referrer-Policy / Permissions-Policy ---" | tee -a "$OUT_FILE" |
| 147 |
MAIN_HEADERS=$(curl -sk -D - -o /dev/null -A "$BROWSER_UA" "$TARGET/") |
| 148 |
for HDR in Referrer-Policy Permissions-Policy Feature-Policy Cross-Origin-Opener-Policy Cross-Origin-Embedder-Policy; do |
| 149 |
VAL=$(echo "$MAIN_HEADERS" | grep -i "^$HDR:" | head -1) |
| 150 |
if [ -n "$VAL" ]; then |
| 151 |
echo " [OK] $VAL" | tee -a "$OUT_FILE" |
| 152 |
else |
| 153 |
echo " [MISS] $HDR not set" | tee -a "$OUT_FILE" |
| 154 |
fi |
| 155 |
done |
| 156 |
echo "" | tee -a "$OUT_FILE" |
| 157 |
|
| 158 |
| 159 |
echo "--- Source Map Exposure ---" | tee -a "$OUT_FILE" |
| 160 |
| 161 |
JS_ASSETS=$(echo "$PAGE_HTML" | grep -oP 'src="(/[^"]+\.js)"' | grep -oP '"/[^"]+' | tr -d '"' | head -5) |
| 162 |
for JS in $JS_ASSETS; do |
| 163 |
MAP_URL="${TARGET}${JS}.map" |
| 164 |
MAP_CODE=$(curl -sk -o /dev/null -w '%{http_code}' -A "$BROWSER_UA" "$MAP_URL") |
| 165 |
printf ' [%s] %s\n' "$MAP_CODE" "${JS}.map" | tee -a "$OUT_FILE" |
| 166 |
done |
| 167 |
do_test "Direct .map probe /js/crypt.js.map" -A "$BROWSER_UA" "$TARGET/js/crypt.js.map" |
| 168 |
echo "" | tee -a "$OUT_FILE" |
| 169 |
|
| 170 |
echo "=== 23. Client-Side JS Security COMPLETE ===" | tee -a "$OUT_FILE" |
| 171 |
|