Skip to content

Weak Session Identifiers

Most of this cluster assumes you have to steal a session identifier. This page is the case where you don’t need to: when the identifier is generated poorly - too short, sequential, or a reversible encoding of predictable data - you can derive or guess a valid one and hijack without ever touching the victim. Framework defaults (PHP, JSP, ASP.NET, Rails) produce strong IDs; this is a vulnerability of custom session management, so the first job is recognising when you’re looking at one.

# 1. Collect many identifiers (log in repeatedly / many fresh requests)
# 2. Decode and look for structure
echo 'dХNlcj0xMDE7dHM9MTc ...' | base64 -d → "user=101;ts=17..." ← reversible!
# 3. If structured → forge another user's token. If genuinely low-entropy → brute-force.
# 4. CONFIRM: the forged/guessed token is accepted as the target account, then ride it.

Decoding a token is a hypothesis, not a win. Readable fields don’t prove forgeability (a signature, nonce, or server-side lookup can still reject a tampered value), and a short-looking token isn’t necessarily guessable (effective entropy, not character count, decides that). Treat every finding here as “test this,” and the real success indicator is the proof: a session identifier you constructed or guessed - never issued to you for the target account - is accepted by the server as that account’s authenticated session.

Strong, framework-issued IDs are not predictable - don’t waste time on them. Reach for this only when you see signals of a weak or custom scheme:

Signal Why it matters
Low effective entropy - few random positions over a small alphabet The guessable keyspace, not the character count, is what makes brute force feasible (15 random base64 chars is ~90 bits - infeasible despite looking short)
Sequential / incrementing across logins Next/other users’ IDs are adjacent - trivially predictable regardless of length
Base64/hex that decodes to readable data Token may embed user=, email, a counter, a timestamp - a forgery hypothesis to test, not a guarantee
Looks like md5(username) / sha1(email) Potentially derivable from data you know - same class as a weak CSRF token
Changes only slightly between issuances A static prefix/suffix shrinks the effective entropy to the part that actually varies
Custom cookie name (not PHPSESSID, JSESSIONID, ASP.NET_SessionId, connect.sid) Suggests a hand-rolled scheme to scrutinise

You need many identifiers to judge randomness.

  1. Gather identifiers: repeat the login (or whatever issues a session) and record each token, or capture a burst of fresh IDs.
  2. In Burp, send a request that sets a new session cookie to Sequencer and let it collect 5,000–20,000 tokens.
  3. Keep the raw values - you’ll both measure their randomness and try to decode their structure.

Two independent questions: is it structured (reversible), and is it random enough?

  • Structure: decode as base64, base64url, hex, URL-encoding. If the result contains a username, email, numeric user id, counter, or timestamp, forging is plausible - but readability alone doesn’t prove it. A verified signature/HMAC, an unpredictable nonce in the blob, or an exact server-side lookup can still reject a token you rebuild. Treat the decode as a hypothesis and confirm it in Step 3 by getting a modified token accepted.
  • Randomness: Burp Sequencer estimates effective entropy (bits) and runs FIPS-style randomness tests. Low entropy, or obvious patterns (shared prefix, monotonic region, time-correlated bytes), means a reduced keyspace you can brute-force or narrow.
# Quick manual decode checks
echo -n '<token>' | base64 -d 2>/dev/null | xxd | head
echo -n '<token>' | xxd -r -p 2>/dev/null # if it's hex

If structured (forge):

# Observed: your own cookie decodes to user=101;role=user
# base64('user=101;role=user') → your token
# Forge the admin's by changing the fields and re-encoding:
echo -n 'user=1;role=admin' | base64 → the forged token

Set the forged value and confirm forgeability: the modified token must be accepted by the server as the other account (test with a second controlled account before claiming it). Only then ride it - see Hijacking. If the rebuilt token is rejected, there is a control you can’t reproduce - an HMAC/signature (a signing-key problem → JWT), a nonce, or a server-side lookup - and this is not a forging bug.

If low-entropy (brute-force): first decide whether it is actually feasible. Estimate the effective keyspace from the alphabet and the number of genuinely random positions (a static prefix doesn’t count), then weigh it against the number of currently valid sessions (more live sessions = more hits per guess), the request rate you’re allowed, and the session lifetime (the window a guess stays valid). Only if that math is tractable, enumerate - sequential neighbours of known IDs, or the full space when it’s genuinely small - against an endpoint that distinguishes authenticated from anonymous.

Step 4 - Check validity scope and lifetime

Section titled “Step 4 - Check validity scope and lifetime”

Weak generation often travels with weak lifecycle, which widens the attack:

  • Scope: does a pre-login identifier stay valid after authentication? If so, you also have session fixation.
  • Reuse: does logout actually invalidate the ID server-side, or can a captured/old ID keep working?
  • Expiry: does the ID ever expire? A token with no server-side timeout means a single leaked or predicted value is good indefinitely.

Report any identifier that is managed with no server interference, is predictable, or never expires as weak in its own right - even before you demonstrate full forging.

  • High entropy, no structure. It’s a framework-grade random ID; prediction is a dead end - go back to stealing it (obtaining tokens, XSS).
  • Decodes to data + a signature. You can read it but can’t forge without the key - pivot to JWT / key-recovery, not brute force.
  • Brute force stalls on lockout / rate limits. Expected; prediction beats brute force whenever structure exists. Reassess the keyspace before grinding.
  • Forged token rejected despite correct structure. There’s a server-side binding you missed (an HMAC, a bound IP/User-Agent, a DB lookup) - capture more samples to find it.
Step Action
Spot Low effective entropy / sequential / decodable / hash(known) / custom cookie name
Collect Many tokens; Burp Sequencer (5k–20k)
Analyse Decode for structure; measure effective entropy
Forge Rebuild another user’s token, re-encode, confirm it’s accepted as that account
Brute Only if effective keyspace is tractable and in scope (loud)
Lifecycle Check scope reuse (fixation), logout, expiry
Ride Hijacking
  • Token decodes to readable fields → rebuild the target’s, confirm the server accepts it as that account, then ride via Hijacking
  • Rebuilt token rejected, or decodes to data + signature → JWT / cookie tampering (signing-key problem, not forging)
  • Genuinely low effective entropy, in scope → brute-force the keyspace (loud); confirm with an authenticated-only endpoint
  • Pre-login ID still valid after login → Session fixation
  • Strong random ID → abandon prediction; steal it via obtaining tokens or XSS to session
  • Hardening this → Defense