A session is the credential that outlives the password — the cookie, bearer token, or magic link that proves "you already logged in." Every session control rests on two invariants: binding (a session belongs to exactly one authenticated user) and lifecycle (the session dies when it should — on logout, password change, revoke, or expiry). Break binding and you get session fixation; break lifecycle and a "revoked" credential keeps authenticating.
# weak-token tell: time/PRNG-seeded session, reset, or CSRF tokens are predictable
grep -REn 'uniqid|mt_rand|\brand\(|microtime' .
Group your finding into one of these, then use the matching probe/payload.
The pre-auth session id is reused after login instead of regenerated, so an id planted before login becomes an authenticated session. Any cookie-write primitive — shared machine, CRLF/cookie-injection, or a sibling-subdomain XSS that only needs to set a cookie — escalates to ATO.
# 1. Unauthenticated GET — record the assigned id
GET / HTTP/1.1
Host: TARGET
# -> Set-Cookie: app.session=PRE_AUTH_VALUE; Path=/
# 2. Log in (or enter the share password), then re-inspect.
# If app.session is STILL PRE_AUTH_VALUE -> fixation.
// write a known unauth id domain-scoped into the victim, then have them log in over it
document.cookie = '_flow_session=7b2f6c606fab4186d7be385aa66d53d9;domain=.TARGET;path=/';
// id not rotated at the OAuth/login callback -> replay it as the authenticated victim
Log out, then replay a captured cookie/token against a sensitive endpoint. Still authenticated = logout was client-only. Watch for logout calls that error silently so the server skips revocation.
# logout aborts server-side on a missing param -> token never revoked
DELETE /api/v1/logout HTTP/1.1
Host: accounts.TARGET
Authorization: Bearer atkn_**********
# -> {"error":"Missing Logout Token Hint"}
# replay after "logout" — still 200 with PII
GET /oauth/userinfo HTTP/1.1
Authorization: Bearer atkn_**********
A stateless JWT (Cognito/Auth0) has no server-side session store, so "logout" only drops it client-side — it authorizes until exp unless a denylist exists. Same shape for any derived credential (refresh token, remember-me, passwordless/magic login_token) that outlives its parent grant.
# decode exp, log out, replay a privileged call — still 200
POST /studio/invitations?tenantId=... HTTP/2
Host: api.TARGET
Authorization: Bearer eyJraWQ...<cognito-id-token>...
Content-Type: application/json
{"email":"attacker@evil.com","role":"ADMINISTRATOR"}
# web /resetmypassword clears sessions, but the CLI path bypasses the hook
airflow users reset-password --username VICTIM --password NEW # old sessions stay live
# signed-but-not-encrypted session with a known SECRET_KEY -> forge a reset token for user_id 1
from itsdangerous import URLSafeTimedSerializer
s = URLSafeTimedSerializer("SECRET_KEY")
print(s.dumps("1")) # GET /reset/<forged> -> set new password -> admin
// predictable: session token derived from the server clock, not a CSPRNG
$token = uniqid(); // microtime-based -> sample a few, brute the nearby timestamps
// browser enforces prefix rules on "__Host-"; the server URL-decodes "__%48ost-" to "__Host-"
document.cookie = "__%48ost-evil=evil; domain=.TARGET"; // spoofs a "trusted" prefixed cookie
# trailing-dot FQDN accepted where the canonical public-suffix domain is rejected -> supercookie
curl -b "" --connect-to foo.co.uk.:PORT:127.0.0.1:PORT \
--connect-to bar.co.uk.:PORT:127.0.0.1:PORT ...
# Set-Cookie: trail=1; Domain=co.uk. -> later sent to unrelated bar.co.uk.
Session bugs are rarely the whole exploit — they are the amplifier that turns a "low" primitive into full ATO.
The techniques above are the general method. Below, each disclosed HackerOne report is a catalogued example — concrete payload, outcome, and matching practice lab. 61 in this class.
Real-world example
Forge Flask/Redash session via known static SECRET_KEY -> ATO
◆ Critical
Specimen #1380121 · urbancompany · awarded · 123 votes · resolved
Program urbancompanySurface webChain known SECRET_KEY -> forged reset token/session -> log Tag account-takeover
Root cause
The Redash instance signed Flask sessions and password-reset tokens with a static/known secret. Knowing the secret lets an attacker forge signed tokens (e.g. itsdangerous URLSafeTimedSerializer) to reset any user's password or set an arbitrary session user_id, yielding admin access.
Method
- Determine the static REDASH_SECRET_KEY / REDASH_COOKIE_SECRET (default/leaked)
- Use itsdangerous with that secret to sign a password-reset token for user id 1
- Browse /reset/<forged-token> and set a new password to log in as that user (admin)
>>> from itsdangerous import URLSafeTimedSerializer
>>> s = URLSafeTimedSerializer("<SECRET_KEY>")
>>> s.dumps(str("1")) # forged reset token for user_id 1
# GET /reset/<forged> -> set new password -> admin login
Insight — When an app uses signed-but-not-encrypted client-side sessions (Flask/itsdangerous, Rails cookie store, JWT), a default or leaked secret is game over: forge sessions or reset tokens directly. Check for default framework secrets (Redash, Grafana, etc.) and test decoding/re-signing cookies offline.
Real-world example
SSO session not invalidated on logout (replay re-login)
◆ Critical
Specimen #222082 · owox · none · 17 votes · resolved
Program owoxSurface webTag account-takeover
Root cause
After logout the server-side session is not destroyed; replaying the old authenticated request, or clicking Sign in, restores access without re-entering Google/SSO credentials.
Method
- Sign in via Google SSO and browse
- Log out (also log out of Google)
- Click Sign in again / replay a prior authenticated request
- Access is restored with no credential prompt
Insight — Logout must invalidate the server session, not just clear the client; test old-request replay and re-click sign-in after logout, especially on SSO/OAuth-fronted apps.
Real-world example
Application accepts arbitrary session id to impersonate any user
◆ Critical
Specimen #1150573 · deptofdefense · none · 7 votes · resolved
Program deptofdefenseSurface webTag account-takeover
Root cause
The portal trusts a caller-supplied session identifier without binding it to the authenticated principal, so substituting another member's session id returns that member's data as if authenticated as them.
Method
- Authenticate to the portal with valid credentials and locate where the session id is supplied.
- Replace it with another member's session id.
- Observe access to that member's data as though authenticated as them.
Insight — Where a session id is passed in a parameter/field rather than only as an opaque server-bound cookie, try swapping it for another user's value. If the app resolves identity purely from the supplied id, it is session hijacking / IDOR-over-session-id, often critical.
Real-world example
Cookie __Host-/__Secure- prefix spoofing via name URL-decoding
◆ High
Specimen #1464396 · ibb · USD 2000 · 31 votes · resolved
Program ibbSurface other
Root cause
Ruby CGI::Cookie.parse applied URL-decoding to cookie NAMES. An attacker can send a percent-encoded name like __%48ost-evil that decodes to __Host-evil, injecting a value into a name that the browser/app trusts as prefix-protected (CVE-2021-41819, same class as CVE-2020-8184).
Method
- Send a cookie header with a URL-encoded prefix in the name plus a real prefixed cookie
- Server-side parser decodes the name, collapsing __%48ost- into __Host-
- The spoofed value is merged/returned for the protected cookie name
- App makes a security decision on an attacker-influenced __Host-/__Secure- cookie
Cookie: __%48ost-evil=evil;__Host-evil=abc
# CGI::Cookie.parse(...)["__Host-evil"].to_a => ["evil","abc"]
Insight — Cookie prefixes (__Host-, __Secure-) are only trustworthy if the parser does NOT decode/normalize names. When auditing cookie parsers, test percent-encoded prefix names; fix is to never URL-decode cookie names.
Real-world example
Embedded browser forwards secure cookies across origin on redirect
◆ High
Specimen #1079561 · valve · awarded · 59 votes · resolved
Program valveSurface desktopChain cross-origin redirect -> login cookie leak -> account Tag account-takeover
Root cause
An in-app browser (Steam Big Picture mode) attaches secure login cookies to a request that begins at a trusted origin but is then forwarded/redirected to a different origin, leaking the cookies to that other site.
Method
- Get the embedded browser to open a trusted-origin URL that redirects cross-origin
- Observe secure/login cookies attached to the request as it is forwarded to the new origin
- Capture the leaked cookies on the attacker origin -> potential account takeover
Insight — Embedded/game/console browsers often mishandle cookie scoping on cross-origin redirects. Test WebViews/in-app browsers: start at a trusted origin that 302s to attacker.com and check whether Secure/HttpOnly session cookies follow. (From program summary; body limited-disclosure.)
Real-world example
Cross-proxy Digest auth state leak on reused libcurl easy handle
◆ Medium
Specimen #3697719 · curl · none · 37 votes · resolved
Program curlSurface otherTag account-takeover
Root cause
libcurl preserves per-easy-handle Proxy-Digest challenge state (realm/nonce) across transfer boundaries; after the proxy is switched, the first CONNECT to proxyB is built preemptively from proxyA's Digest state instead of starting unauthenticated.
Method
- On a reused easy handle, complete a CONNECT authenticated to proxyA (learns realm/nonce).
- Change the proxy to attacker-controlled proxyB and start a new transfer.
- Observe the first CONNECT to proxyB carries Proxy-Authorization: Digest built from proxyA's realm/nonce.
- Replay that leaked header to proxyA for the same CONNECT authority -> accepted 200 (real auth value); different authority -> 407.
CONNECT victim.test:18099 HTTP/1.1
Proxy-Authorization: Digest username="proxyuser", realm="proxyArealm", nonce="proxyAnonce", uri="victim.test:18099", ...
// controls: fresh handle or curl_easy_reset() suppress the leak
Insight — When auditing HTTP client libraries, look for per-connection/credential state stored on a reusable handle that is not cleared across trust boundaries (proxy switch, host switch, transfer end). Reused handles crossing identity boundaries leak credentials.
Real-world example
Mixed-case domain bypasses PSL check to set supercookie
◆ Medium
Specimen #2212193 · curl · none · 28 votes · resolved
Program curlSurface other
Root cause
libcurl passes hostname and cookie domain to libpsl's psl_is_cookie_domain_acceptable without lowercasing them; an uppercase in the domain (e.g. domain=co.UK) evades the public-suffix rejection, while cookie matching later is case-insensitive, yielding a supercookie across the whole TLD (CVE-2023-46218).
Method
- Serve Set-Cookie with a public-suffix domain using mixed case (domain=co.UK) to a request whose Host uses matching mixed case
- curl accepts and stores the supercookie because the PSL check saw a non-normalized domain
- Request any other host under that suffix; the supercookie is sent
HTTP/1.1 200 OK
Set-Cookie: super=oops; domain=co.UK
# stored as: .co.UK TRUE / FALSE 0 super oops
# then sent to any *.co.uk host
Insight — Look for case/normalization mismatches between the validation step and the use step. If a security check (PSL, allowlist, path, host compare) normalizes differently than the later matcher, a mixed-case or encoded variant slips past the check yet still matches at use time.
Real-world example
Failed XML-RPC login still issues a usable, non-invalidated session
◆ Medium
Specimen #3783738 · revive_adserver · none · 27 votes · resolved
Program revive_adserverSurface api
Root cause
Revive Adserver's XML-RPC ox.login returned an error but the HTTP response still set a valid session-id cookie and the session was never invalidated, so the leaked session id could drive subsequent authenticated API calls, bypassing the admin-only restriction.
Method
- Call the XML-RPC ox.login method (even with non-admin/invalid conditions)
- Method returns an error but response headers contain a Set-Cookie session id
- Reuse that session id for further API calls; they succeed without restriction
POST /www/api/v2/xmlrpc/ (method: ox.login)
-> response: error body BUT Set-Cookie: <valid session id>
-> reuse session id for authenticated API calls
Insight — Inspect responses to FAILED auth for tokens/cookies. An error status doesn't mean no session was created. Always test whether a session id issued on a failed/partial login is actually usable and whether the error path invalidates it.
Real-world example
Cookie set on TLD via trailing-dot host (curl CVE-2022-27779)
◆ Medium
Specimen #1565615 · ibb · awarded · 27 votes · resolved
Program ibbSurface networkTag cors
Root cause
libcurl (7.82-7.83.0, built without libpsl) rejected cookies for TLDs but the check failed when the host was given with a trailing dot (e.g. 'com.'), allowing a super-cookie shared across all sites under that TLD.
Method
- Target a curl-based client without libpsl
- Serve a Set-Cookie with domain matching a trailing-dot TLD host
- Cookie is now sent to unrelated sites under that TLD (session fixation)
curl 'http://example.com./' with Set-Cookie: X=Y; domain=.com.
Insight — Trailing-dot / case / IDN variants of a hostname routinely bypass public-suffix and domain-scoping checks; test them against cookie, CORS and SOP logic.
Real-world example
Predictable session tokens from PHP uniqid()
◆ Medium
Specimen #1306942 · revive_adserver · none · 14 votes · resolved
Program revive_adserverSurface webChain predictable token -> session hijack -> mass account taTag account-takeover
Root cause
Session tokens were generated with PHP uniqid(), which is derived from the server clock (microtime) and is not cryptographically secure, so tokens are low-entropy and predictable -> mass account takeover.
Method
- Locate token/session/reset generation and confirm uniqid()/rand()/mt_rand()/microtime() usage
- Sample a few tokens to learn the timestamp basis
- Predict/brute nearby timestamps to reconstruct valid session tokens for other users
// vulnerable: session token = uniqid(); (time-based, not CSPRNG)
Insight — Grep source for uniqid(), rand(), mt_rand(), microtime() feeding session tokens, password-reset links, or CSRF tokens - all predictable. The correct primitive is random_bytes()/CSPRNG. Predictable tokens escalate straight to ATO.
Real-world example
Stateless JWT bearer token cannot be revoked on logout
◆ Medium
Specimen #1319892 · trycourier · none · 12 votes · resolved
Program trycourierSurface apiTag jwtTag account-takeover
Root cause
The API authenticates with a stateless Cognito-issued JWT (Authorization: Bearer). Logout only discards it client-side; the backend has no revocation list, so the token keeps authorizing privileged actions until its exp claim.
Method
- Log in and capture a privileged API request carrying the JWT (e.g. POST /studio/invitations with Authorization: Bearer <jwt>).
- Log out of the web app.
- Replay the captured request with the same JWT and confirm it still returns 200 and performs the action (invite user / create workspace).
POST /studio/invitations?tenantId=... HTTP/2
Host: api.courier.com
Authorization: Bearer eyJraWQ...<cognito-id-token>...
Content-Type: application/json
{"email":"attacker@yopmail.com","role":"ADMINISTRATOR"}
Insight — Decode the JWT (check exp) and replay it after logout. Stateless JWT auth (Cognito/Auth0) is commonly not revocable server-side, so 'logout' is meaningless until token expiry unless a denylist/short TTL + refresh rotation exists.
Real-world example
Cookie set for TLD + trailing dot leaks cross-site (curl CVE-2022-27779)
◆ Medium
Specimen #1553301 · curl · none · 5 votes · resolved
Program curlSurface other
Root cause
curl's cookie engine rejects cookies scoped to a bare TLD but not to TLD+'.' (e.g. Domain=.me.); since a trailing-dot host is treated as equivalent to the plain host, a cookie set for '.me.' is later sent to unrelated *.me sites.
Method
- Attacker site responds with Set-Cookie for Domain=<tld>. (trailing dot)
- curl stores it scoped to the trailing-dot TLD
- When curl requests any host under that TLD via trailing-dot form, the cookie is attached
- Cookie crosses to unrelated victim sites
# attacker response header:
Set-Cookie: a=b; Domain=.me.
curl -c cookies.txt http://localtest.me./index.php
# cookies.txt: .me. TRUE / FALSE 0 a b
curl -b cookies.txt http://victim.me./ # sends Cookie: a=b
Insight — Trailing-dot (FQDN root) hostnames bypass many domain/suffix checks (cookie scoping, SOP, host allowlists, Public Suffix List). Test appending '.' to hosts to defeat domain-equality logic and to smuggle cross-site cookies/routing.
Real-world example
Sessions not invalidated after MFA activation
◆ Medium
Specimen #667739 · superhuman · awarded · 176 votes · resolved
Program superhumanSurface webTag account-takeover
Root cause
Enabling 2FA/MFA does not invalidate previously established sessions on other devices; those sessions remain fully authenticated without ever passing the new second factor.
Method
- Log into the same account on devices A and B
- On A, complete 2FA activation
- On B, reload — session remains active without MFA challenge
Insight — Test session lifecycle around every security event: enabling MFA, password change, email change, logout-all. Pre-existing sessions should be revoked or re-challenged. This is a quick, high-signal check on any account-security settings page.
Real-world example
Disconnecting an SSO/social login provider does not revoke active session
◆ Medium
Specimen #1547684 · shopify · USD 1600 · 20 votes · resolved
Program shopifySurface webChain password compromise -> link attacker SSO -> disconnectTag oauthTag account-takeover
Root cause
Unlinking an external login provider (Google) from an account does not terminate sessions that were established via that provider, so an attacker who linked their Google account keeps access even after the victim disconnects it.
Method
- Attacker (having obtained the victim's password) links their own Google account to the victim's account and logs in via Google.
- Victim notices and disconnects the attacker's Google account.
- Attacker's existing session does not expire and still has access; attacker can re-link their Google account as a persistent backdoor.
Insight — Disconnecting/unlinking an SSO provider is a revocation event that many apps ignore. Test that removing a linked identity provider (Google/GitHub/SAML) kills sessions created through it; otherwise it is a backdoor that survives even password changes.
Real-world example
Web-push subscriptions persist per-account across logout/switch and leak data
◆ Medium
Specimen #347748 · twitter · USD 560 · 19 votes · resolved
Program twitterSurface webTag account-takeover
Root cause
Web push notification subscriptions are bound per-account and are not torn down on logout or account switch, so notifications (including DM contents) for a signed-out account keep arriving in the browser.
Method
- Log in to multiple accounts one by one and enable web push notifications.
- Log in to one account; have someone send a DM to a DIFFERENT (logged-out) account.
- Observe browser notifications for the logged-out account, exposing DM content; clicking sends that account's cookies alongside the other account's request.
Insight — After logout/account-switch, check whether push subscriptions, service workers, or notification tokens are revoked. Persistent push registrations are a data-leak channel that survives session teardown on multi-account clients.
Real-world example
Scope revocation doesn't invalidate the session cookie derived from the token
◆ Medium
Specimen #388515 · nextcloud · 100 · 17 votes · resolved
Program nextcloudSurface web
Root cause
Revoking an app token's filesystem-access scope does not invalidate the session cookie already minted from that token, so the live session keeps file access after the permission is revoked (CVE-2018-16466).
Method
- Obtain an app token
- Access files with it and save the resulting cookies
- Revoke filesystem access for that token
- Reuse the saved cookies - files are still accessible
Insight — After revoking a permission/scope/token, always re-test existing sessions and cookies derived from it. Revocation frequently only blocks new authentication, leaving live sessions fully privileged.
Real-world example
Long-lived sub-session (web terminal/exec) outlives parent session expiry
◆ Medium
Specimen #2123094 · ibb · USD 2540 · 9 votes · resolved
Program ibbSurface webTag account-takeover
Root cause
An interactive web-terminal/exec channel established during a valid session keeps operating (websocket to a live machine) after the parent web session has expired, because its lifetime is not tied to the session.
Method
- Log in to the application (ArgoCD).
- Open a web-terminal session that operates a backend machine.
- Wait until the main web session expires.
- Confirm the terminal/exec channel is still connected and usable.
Insight — Persistent channels (web terminals, websockets, SSE, long-poll, exec streams) often authenticate once at open time and never re-check session validity. Open such a channel, expire/revoke the parent session, and verify the channel dies.
Real-world example
Web session ID reused against admin-only XML-RPC API
◆ Medium
Specimen #3672641 · revive_adserver · none · 6 votes · resolved
Program revive_adserverSurface api
Root cause
Session IDs minted for the low-privileged web admin console were accepted by the XML-RPC API, whose authentication is normally restricted to admin users; the session was not bound to a context (web vs API), so a low-priv web session authenticated privileged API calls.
Method
- Log into the web console as a low-privileged user and capture the session ID
- Present that session ID to the XML-RPC API endpoint
- API accepts it and exposes admin-only operations/vulnerabilities
Insight — When one app exposes multiple auth surfaces (web UI + API/XML-RPC), test cross-surface session portability. Sessions should carry a context/audience so a low-trust session can't drive a high-trust interface.
Real-world example
Session revocation doesn't revoke desktop/mobile client access
◆ Medium
Specimen #165353 · nextcloud · none · 3 votes · resolved
Program nextcloudSurface webTag account-takeover
Root cause
Killing a session in the web UI only destroys the PHP web session. Desktop/mobile clients that authenticated with raw username+password silently re-create a session on the next request, and some client sessions never appear in the session list at all.
Method
- Log in on web, desktop client, and mobile client with the same account (username+password, not app-specific token).
- In web UI, revoke the desktop client's session.
- Continue syncing from the desktop client -> it keeps working with no re-auth prompt.
- Note the mobile client never shows up in the sessions tab, so it can't be revoked at all.
Insight — When testing 'log out other sessions'/'revoke device' features, verify the token is actually invalidated server-side AND that every client type is enumerable/revocable. Clients holding raw credentials (vs app-specific tokens) defeat session revocation; the fix is per-client app passwords.
Real-world example
Session not invalidated on logout; store password readable post-logout
◆ Low
Specimen #837729 · shopify · awarded · 157 votes · resolved
Program shopifySurface webTag account-takeover
Root cause
After a 'successful logout' the server session remained valid; browsing directly to the storefront-password page still returned the protected store password, showing logout did not destroy the server-side session.
Method
- Log in, then log out (confirm success message)
- Navigate directly to /accounts/passwords (or another authenticated URL)
- Sensitive data still returned -> session alive
GET https://<shop>.myshopify.com/accounts/passwords (after logout, still authorized)
Insight — Never trust the logout UI: replay an authenticated request after logout. If it still returns data, the server session/token was not invalidated.
Real-world example
Logout does not invalidate server-side token (client-only logout)
◆ Low
Specimen #1172205 · shopify · awarded · 63 votes · resolved
Program shopifySurface mobile-androidTag account-takeover
Root cause
The logout call fails server-side (here DELETE /api/v1/logout returns 'Missing Logout Token Hint') or only clears client storage, so the access/session token remains valid and any captured token keeps authenticating requests.
Method
- Capture the bearer/session token from a logged-in session (HTTP request or local storage such as shared_prefs/default_user.xml).
- Log out from the app.
- Replay an authenticated request (e.g. GET /oauth/userinfo) with the old token and confirm it still succeeds.
DELETE /api/v1/logout HTTP/1.1
Host: accounts.shopify.com
authorization: Bearer atkn_**********
-> {"error":"Missing Logout Token Hint"} (server never revokes)
GET /oauth/userinfo HTTP/1.1
authorization: Bearer atkn_********** -> 200 with user PII
Insight — After logout, replay a previously captured token/cookie against a sensitive endpoint. If it still returns data, logout is client-only. Watch for logout requests that error out silently (missing hint/param) so the server skips revocation.
Real-world example
Session invalidation covers web password change but not the CLI path
◆ Low
Specimen #3073507 · ibb · awarded · 57 votes · resolved
Program ibbSurface webTag account-takeover
Root cause
The /resetmypassword web form terminates all existing sessions, but changing the same password via the admin CLI tool bypasses that hook and leaves sessions active.
Method
- Log in as the target user and keep the session alive.
- Change that user's password using the CLI/admin tool (not the web reset form).
- Replay the still-active session and confirm continued access despite the password change.
airflow users reset-password --username <user> --password <new> # sessions not cleared
Insight — Session-invalidation-on-credential-change is often wired to ONE code path (the web form). Enumerate every entry point that mutates credentials (CLI, admin API, bulk import, SSO sync) and test each for missing session teardown.
Real-world example
Session-revoke UI does not cover the GraphQL API channel
◆ Low
Specimen #417382 · security · USD 500 · 47 votes · resolved
Program securitySurface graphqlTag graphqlTag account-takeover
Root cause
The 'revoke session' feature invalidates the web session for normal POST/GET flows but the GraphQL endpoint continues to honour the revoked session, allowing sensitive queries after revocation.
Method
- While logged in, capture a sensitive GraphQL query (e.g. User_bounty_settings_page) and send it to Repeater.
- Go to settings/sessions and Revoke the current session; you get logged out of the web UI.
- Replay the GraphQL request from Repeater and confirm it still returns sensitive data.
POST /graphql
{"query":"query User_bounty_settings_page(...){ me { ... bounties{edges{node{awarded_amount,report{title,team{handle}}}}} } }","variables":{...}}
Insight — Session revocation and logout are frequently enforced only on the primary web pipeline. Independently test every API surface (GraphQL, REST, websocket, mobile token) against a revoked/logged-out session.
Real-world example
Cookie-prefix (__Host-/__Secure-) spoofing via percent-encoded cookie name
◆ Low
Specimen #895727 · rails · none · 46 votes · resolved
Program railsSurface webTag account-takeover
Root cause
Rack unescaped cookie NAMES while parsing, so a cookie named __%48ost- decodes server-side to __Host-. The browser never enforced the __Host-/__Secure- integrity rules (Secure, HostOnly, path=/) on the encoded form, so an attacker on a subdomain could set a cookie the server parses as a trusted prefixed cookie.
Method
- From an attacker-controlled subdomain, set a cookie whose name is the percent-encoded prefix with a wildcard domain
- Browser sends __%48ost-evil to the app (no HostOnly enforcement since name != literal __Host-)
- Server-side unescape turns it into __Host-evil, which app logic trusts
document.cookie = "__%48ost-evil=evil; domain=.example.com";
// server: URI.unescape("__%48ost-evil") => "__Host-evil"
Insight — Never trust __Host-/__Secure- prefixes if any parser in the stack unescapes cookie names. Test percent-encoded prefix variants (__%48ost-, __%53ecure-) from a subdomain to forge 'trusted' cookies. Cookie name encoding is a parser-differential surface just like the value.
Real-world example
Session not invalidated on logout (cookie replay)
◆ Low
Specimen #284 · security · awarded · 35 votes · resolved
Program securitySurface webTag account-takeover
Root cause
Logout only cleared the client cookie; the server-side session was not destroyed, so a previously captured cookie remained valid for hours.
Method
- Log in and export the session cookies
- Log out of the account
- Re-import the same cookies later (e.g. after several hours)
- Observe you are logged back in
# capture Cookie: session=... then after logout replay the same header -> still authenticated
Insight — Always test cookie replay after logout, password change, and expiry. Logout must invalidate the session server-side, not just delete the cookie.
Real-world example
Stale cookiehost on reused libcurl easy handle leaks cookies cross-origin
◆ Low
Specimen #3671818 · curl · none · 24 votes · resolved
Program curlSurface otherChain cookie leak -> attacker jar poisoning -> poisoned cookTag account-takeover
Root cause
libcurl keeps data->state.aptr.cookiehost from a request that used a custom Host: header and reuses it for cookie selection/attribution on later requests that have no custom Host:, breaking cookie origin isolation across reused handles.
Method
- On one easy handle with a cookie jar, request 1 to victim.internal with a custom Host: header (seeds cookiehost).
- Request 2 to attacker.test WITHOUT the custom Host: header; victim's cookies are sent to attacker and attacker's Set-Cookie is stored under victim.internal.
- Request 3 to victim.internal replays the attacker-poisoned cookie back to the victim.
# req1: Host: victim.internal -> jar stores sid=SECRET123 for victim.internal
# req2: (no custom Host) -> attacker.test receives cookie: sid=SECRET123; injects poison=EVIL
# req3: victim.internal receives cookie: poison=EVIL; sid=SECRET123
Insight — Same class as the proxy-digest leak: stale per-handle state (cookiehost) not cleared on the non-custom-host path. Confidentiality (cookie theft) plus integrity (jar poisoning) break across reused connections.
Real-world example
Session fixation on password-protected public share (no cookie rotation after auth)
◆ Low
Specimen #237184 · nextcloud · awarded · 20 votes · resolved
Program nextcloudSurface webTag account-takeover
Root cause
Password-protected public download/share links authenticate the visitor but never replace the pre-existing session cookies, so an attacker-provisioned session id survives successful authentication.
Method
- Pre-provision the victim with attacker-chosen cookie values (session id + oc_sessionPassphrase).
- Victim opens the password-protected public share link and enters the password across the multi-request download flow.
- Observe that every request keeps the pre-provisioned cookies and the server never issues a fresh session id after the password is accepted.
- Attacker reuses the known session id to access the share as the authenticated victim.
Cookie: ocu1w9tvnra8=AAAAAAAAAAAAAAAAAAAAAAAAA1; oc_sessionPassphrase=AAAA...AAAA1
Insight — Any transition from unauthenticated to authenticated (login, password-gate on a public link, guest->member) must rotate the session id. Diff the session cookie before and after entering the password; if unchanged, it is fixation.
Real-world example
Logout leaves session dormant and rebinds it to the next user to log in
◆ Low
Specimen #250688 · gsa_bbp · awarded · 16 votes · resolved
Program gsa_bbpSurface webChain stale session cookie -> revived on next login -> crossTag oauthTag account-takeover
Root cause
Logout only invalidates the upstream OAuth token, not the local session id; the same session id is reactivated on the next login and, because it is not regenerated, can be bound to a different user who logs in on that machine.
Method
- Log in, save the session cookie (federalist.sid), then log out; replaying /v0/me now returns 403.
- Log back in with the SAME account; replay the old cookie -> success (dormant session revived).
- Log out again, then log in with a DIFFERENT account; replay the old saved cookie -> success and it now returns the second user's data.
GET /v0/me HTTP/1.1
Cookie: federalist.sid=<saved-pre-logout-value>
Insight — A session that is merely deactivated (not destroyed) on logout becomes a backdoor: it revives on next login. If the id is also not regenerated per user, an old cookie can hijack whoever authenticates next on shared machines. Test the two-account revival sequence.
Real-world example
Session cookie value is the raw user identifier
◆ Low
Specimen #178567 · bumble · awarded · 9 votes · resolved
Program bumbleSurface webTag account-takeover
Root cause
The session cookie stores the user identifier directly rather than an unguessable token, so modifying the cookie changes which account the request is treated as, enabling impersonation / forced actions on another profile.
Method
- Log in and inspect the 'session' cookie value
- Observe it equals/derives from your user id
- Change the value to another user's identifier and replay -> requests act in that user's context
Cookie: session=<other_user_id>
# also usable to force a victim onto attacker's profile via crafted link
Insight — Always decode/inspect session cookies for meaningful, predictable content (user id, email, sequential value). If the cookie IS the identity rather than an opaque random token, it is an IDOR-grade auth bypass.
Real-world example
Persistent 'remember me' token not rotated on logout/re-login
◆ Low
Specimen #7931 · security · 150 · 4 votes · resolved
Program securitySurface webTag account-takeover
Root cause
The remember_user_token cookie is not regenerated across the session lifecycle: after logout and re-login the same token value is reissued, and presenting only that cookie yields the same token back, so a captured token stays valid indefinitely.
Method
- Log in with 'remember me', capture remember_user_token.
- Log out, then log in again; observe the same token value is returned.
- Send a request carrying only remember_user_token; confirm the response reuses the same token (no rotation/invalidation).
Cookie: remember_user_token=<captured-value>
Insight — When auditing 'remember me' / persistent-login: check whether the long-lived token rotates on logout and on each use. A static, non-rotating remember token means one-time capture = lasting access even after the victim logs out.
Real-world example
Trailing-dot FQDN bypasses public-suffix cookie rejection (curl super-cookie)
◆ Low
Specimen #3733905 · curl · none · 1 votes · resolved
Program curlSurface otherChain trailing-dot PSL bypass -> super-cookie scoped to public
Root cause
PSL/public-suffix cookie checks canonicalize hostnames but not their trailing-dot FQDN form, so Domain=co.uk is rejected while Domain=co.uk. is accepted, letting a cookie be scoped to a public suffix and shared across unrelated hosts.
Method
- Reach a server via a trailing-dot hostname (foo.co.uk.)
- Server sets Set-Cookie: trail=1; Domain=co.uk.; Path=/ (public suffix + trailing dot)
- curl accepts it (the canonical Domain=co.uk is correctly rejected)
- Follow a redirect to another trailing-dot host (bar.co.uk.); curl sends Cookie: trail=1 to it
# Canonical control (correctly rejected):
Set-Cookie: canonical=1; Domain=co.uk; Path=/
# Trailing-dot bypass (accepted, then leaked cross-host):
Set-Cookie: trail=1; Domain=co.uk.; Path=/
# later request to bar.co.uk. -> Cookie: trail=1
curl -b "" \
--connect-to foo.co.uk.:PORT:127.0.0.1:PORT \
--connect-to bar.co.uk.:PORT:127.0.0.1:PORT ...
Insight — Any hostname/domain validation that normalizes the canonical form but not the trailing-dot FQDN is bypassable. When testing PSL/same-site/domain-scoping logic, retry every check with a trailing '.' appended to the host and to the Domain attribute.
Real-world example
Sessions not invalidated on security-critical change (enabling 2FA)
◆ Low
Specimen #2234736 · sidefx · USD 300 · 89 votes · resolved
Program sidefxSurface webTag account-takeover
Root cause
Enabling 2FA does not invalidate other concurrently active sessions, and those pre-2FA sessions retain full privilege including password change.
Method
- Log in to the same account in two browsers
- In browser 1, complete 2FA enrollment
- In browser 2 (older session), reload - still authenticated
- In browser 2, change the account password successfully
Insight — Test session lifecycle around security-state transitions: enabling 2FA, password change, email change, logout-all should terminate other sessions. If an old session survives a 2FA enrollment and can still perform sensitive actions, that's a reportable session-management flaw.
Real-world example
Sessions not invalidated after password change / logout
◆ Low
Specimen #634488 · omise · awarded · 44 votes · resolved
Program omiseSurface webTag account-takeover
Root cause
Changing the password (or logging out) in one browser did not destroy other active server-side sessions, so a previously-authenticated attacker session remained valid — defeating the primary account-recovery/eviction control after credential theft.
Method
- Log in to the same account from two browsers/sessions (A and B)
- In session A, change the password and/or log out
- Confirm session B remains authenticated and can still act (edit profile)
Insight — After any password change or logout, all other sessions/tokens should be revoked. Test by holding a second session and checking it survives a password reset — if it does, stolen sessions cannot be evicted, undermining the whole recovery story.
Real-world example
Password change does not invalidate other active sessions
◆ Low
Specimen #2279041 · portswigger · awarded · 40 votes · resolved
Program portswiggerSurface webChain session hijack -> victim resets password -> attacker sTag account-takeover
Root cause
Changing the administrator password (via admin console reset) did not terminate existing sessions. An attacker who hijacked a session retains access even after the legitimate owner detects the compromise and resets the password.
Method
- Authenticate and keep a session cookie in browser A.
- Change the account password through the console/reset path.
- Confirm browser A's session is still valid -> old sessions not revoked.
Insight — After any credential-change action, verify all OTHER sessions are killed. Test: log in on two clients, change password on one, confirm the other is logged out. Also applies to reset-link invalidation (see #1288898).
Real-world example
Credential change does not invalidate other active sessions
◆ Low
Specimen #2121960 · ibb · awarded · 26 votes · resolved
Program ibbSurface webTag account-takeover
Root cause
Changing/resetting a user's password (or other credentials) does not destroy that user's other active server-side sessions, so an attacker who already holds a session keeps full access after the victim rotates the password.
Method
- Log in to the same account in two browsers (or capture a live session), simulating attacker + victim.
- In browser A (or via admin Reset Password), change or reset the account password.
- In browser B replay a captured request / perform a state-changing action and confirm it still succeeds despite the password change.
Insight — After any credential change (password reset, admin reset, email change, disabling 2FA) always re-test previously captured sessions. Secure apps invalidate all sessions on password change; many keep them alive until natural expiry. Note secure-cookie/JWT backends may be impossible to force-logout without rotating the signing key.
Real-world example
Desktop app uninstall leaves valid session on disk
◆ Low
Specimen #1797661 · mattermost · none · 20 votes · resolved
Program mattermostSurface desktop
Root cause
The desktop client stores its session token in a local profile directory that the uninstaller does not remove; reinstalling reloads the persisted token and auto-authenticates without any credential entry.
Method
- Install desktop app and log in
- Uninstall the app
- Reinstall -> automatically logged back into the same account (incl. admin panel) with no login prompt
Insight — Check whether desktop/Electron apps wipe session tokens on uninstall/logout. Residual tokens in the user profile dir let a later local user regain full account access. Look for the token files (e.g. LevelDB/Cookies under app data).
Real-world example
Forgot-password keeps same session id & other sessions alive
◆ Low
Specimen #176116 · revive_adserver · none · 17 votes · resolved
Program revive_adserverSurface webTag account-takeover
Root cause
Password reset neither rotates the session id nor invalidates other active sessions, enabling session fixation (pre-set id survives the reset) and persistence of a hijacked session after the victim resets.
Method
- Note the sessionID cookie on the pre-login recovery page
- Complete reset; observe sessionID is unchanged and you're logged in
- A separate already-logged-in session (Browser A) stays valid after the reset
Insight — After any password reset/change, verify (a) the session id rotates and (b) all other sessions are killed; failure yields fixation and post-compromise persistence.
Real-world example
Session not invalidated server-side on logout
◆ Low
Specimen #288 · security · awarded · 13 votes · resolved
Program securitySurface webTag account-takeover
Root cause
Logout only clears the cookie in the browser; the session token remains valid server-side, so a previously captured cookie still authenticates after the user 'signs out' (and even after a new login is issued).
Method
- Capture the session cookie while logged in
- Click Sign-Out
- Replay the old cookie; the server still accepts it and grants account access
Insight — Always test logout as a server-side event: grab a token, log out, replay. Also test that changing password / new login invalidates old sessions. Client-only cookie clearing is a common, easily-verified flaw.
Real-world example
Enabling 2FA does not invalidate existing sessions
◆ Low
Specimen #1927360 · wordpress · none · 12 votes · resolved
Program wordpressSurface webTag account-takeover
Root cause
Turning on 2FA changes the login requirement going forward but does not terminate already-authenticated sessions, so a session established before 2FA (e.g. by an attacker who had the password) stays valid without ever satisfying 2FA.
Method
- Log the same account in on device A and device B
- On device A, enable 2FA
- On device B, reload — the pre-existing session is still authenticated with no 2FA challenge
Insight — Security-state changes (enable 2FA, change password, revoke device) must revoke other sessions. Test whether enabling 2FA / changing password invalidates a second concurrent session; if not, an attacker who already had access keeps it after the victim 'secures' the account.
Real-world example
Sessions not invalidated after password change/reset
◆ Low
Specimen #678050 · liberapay · none · 10 votes · resolved
Program liberapaySurface webTag account-takeover
Root cause
Changing or resetting the password does not terminate other active sessions, so a session captured before the change (or an attacker's session on a shared device) remains valid afterward.
Method
- Log in to the same account in two browsers/sessions
- Change the password in session A
- Refresh session B and confirm it is still authenticated
- Log in again with the new password: the old session still coexists
Insight — After password change/reset, all other sessions must be revoked. Test by holding a second session across the change; if it survives, a compromised/stolen session persists post-remediation. A core check in any ATO-recovery review.
Real-world example
SSO logout desync: consumer logout leaves IdP session, re-login without creds
◆ Low
Specimen #1971610 · weblate · none · 10 votes · resolved
Program weblateSurface webTag samlTag account-takeover
Root cause
An SSO IdP keeps its session alive when a downstream consumer logs the user out; clicking login again silently re-authenticates via the still-valid IdP session, so a user who 'logged out' can be re-logged-in without credentials.
Method
- Log in to consumer site (weblate.org) via SSO to hosted IdP (hosted.weblate.org)
- Log out on the consumer site
- Click the login icon again
- Observe you are logged straight back in with no credential prompt (IdP session persisted)
Insight — Test SSO logout completeness: after logout on a relying party, re-trigger the SSO login; if it round-trips without a credential prompt the IdP session was never invalidated. On shared machines this is a real ATO of the previous user's account.
Real-world example
Password change fails to invalidate mobile/POS sessions
◆ Low
Specimen #55530 · shopify · USD 500 · 9 votes · resolved
Program shopifySurface webTag account-takeover
Root cause
The 'change password + log out of Shopify Mobile/POS' option only terminates web sessions; the Android mobile session remains authenticated after the password change.
Method
- Log in on web and on the mobile (Android) app
- On web, change the password and tick 'Also log out of Shopify Mobile / POS'
- Observe the mobile app session is still valid
Insight — After a password change or explicit 'log out everywhere', verify EVERY session class is killed: web, mobile app tokens, API/OAuth tokens, POS. Different session stores are often missed. A stolen mobile token survives the victim's password reset.
Real-world example
Password reset does not invalidate existing sessions
◆ Low
Specimen #15852 · mavenlink · awarded · 5 votes · resolved
Program mavenlinkSurface webTag account-takeover
Root cause
After a password change via reset, pre-existing authenticated sessions are not terminated and logging in with the new password does not revoke the old ones, so a session established with the old password stays valid.
Method
- Log in as the victim in browser A (establishes session S1)
- Trigger password reset and set a new password (e.g. from browser B)
- Confirm S1 in browser A is still authenticated
- Optionally log in with the new password in browser C and confirm all three sessions coexist
Insight — After any credential change (password reset, password change, email change, deactivation), test whether ALL other active sessions are killed. Keep a parallel session open before the change and see if it survives; if it does, an attacker who captured a session survives the victim's remediation.
Real-world example
Sessions survive removal of linked SSO provider
◆ Low
Specimen #223475 · weblate · none · 4 votes · resolved
Program weblateSurface webTag oauthTag account-takeover
Root cause
Removing a linked third-party (Google) auth association did not invalidate sessions that had been established via that provider, so an attacker's active session on another device stayed authenticated after the victim unlinked/revoked access.
Method
- Link a third-party (Google) account and log in on device 2 via it
- On device 1, remove the Google link and disconnect
- Device 2's session remains valid despite the credential source being revoked
Insight — Revoking a credential/SSO link must terminate all sessions derived from it; test that unlinking a provider or changing credentials kills existing sessions everywhere.
Real-world example
Sessions not invalidated on password change
◆ Low
Specimen #10377 · c2fo · none · 1 votes · resolved
Program c2foSurface webTag account-takeover
Root cause
Changing the account password does not terminate other active sessions authenticated with the old password, so an attacker who compromised a session/password keeps access after the victim's remediation.
Method
- Log in to the same account from two browsers (attacker + victim).
- As victim, change the account password.
- Confirm the attacker's pre-existing session remains authenticated and can still act until natural expiry.
Insight — On any password-change/reset test, verify old sessions are killed server-side. Persisting sessions after a reset defeats the primary account-recovery remediation and is a standard session-management checklist item.
Real-world example
Session fixation via by-design XSS on shared cookie domain
◆ Info
Specimen #423136 · shopify · awarded · 144 votes · resolved
Program shopifySurface webChain subdomain XSS (cookie write) -> session fixation -> acTag oauthTag account-takeover
Root cause
Apps that use a plain session id and do not regenerate it on login assign auth state to whatever session id the browser already carries. Combined with a subdomain XSS to write a scoped cookie, the attacker fixes the victim's session.
Method
- As attacker, visit the target app and grab a session id it assigns to an unauthenticated visitor (e.g. _flow_session).
- Use an XSS (here N/A/by-design on *.shopifycloud.com) to write that session id into the victim's browser scoped to the parent domain: document.cookie='_flow_session=EVIL;domain=.shopifycloud.com;path=/'.
- Force the victim through the login/OAuth-callback flow (redirect them to the app).
- Because the session id is not rotated at /auth/callback, the victim's login binds to the attacker-known id; attacker replays it to authenticate as the victim.
document.cookie='_flow_session=7b2f6c606fab4186d7be385aa66d53d9;domain=.shopifycloud.com;path=/';
Insight — Test whether the session cookie value changes across the login/OAuth-callback boundary. If it does not, any cookie-write primitive (even a low-severity or by-design XSS on a sibling subdomain) escalates to full account takeover via session fixation.
Real-world example
IDOR on 'expire all sessions' endpoint force-logs-out any user
◆ Info
Specimen #56511 · shopify · 1000 · 29 votes · resolved
Program shopifySurface webTag account-takeover
Root cause
The account-scoped session-termination endpoint keyed off a user id in the URL without verifying it belonged to the authenticated session, so any id could be targeted.
Method
- As attacker, open account settings and click 'expire all sessions'; capture the request
- Replace the numeric account id in the path with the victim's id and forward
- Victim's sessions are invalidated (denial of service / session logout)
POST /admin/settings/account/expire_specific_users_sessions/{VICTIM_ID} HTTP/1.1
Host: {shop}.myshopify.com
Content-Type: application/x-www-form-urlencoded
utf8=%E2%9C%93&_method=patch&authenticity_token={TOKEN}
Insight — Session-management actions (expire sessions, revoke tokens, logout-everywhere) are frequently missing ownership checks - always A/B test them with a foreign account id.
Real-world example
Session not invalidated server-side on logout
◆ Info
Specimen #353 · security · 100 · 22 votes · resolved
Program securitySurface webTag account-takeover
Root cause
Logout clears the client cookie but the server keeps the session valid, so a previously-captured session token still authenticates after the user logs out.
Method
- Log in and capture an authenticated request (e.g. profile edit)
- Log out
- Replay the captured request -> still returns the authenticated response
# 1. capture authed request in proxy
# 2. click logout
# 3. replay captured request -> 200 with private data
Insight — Always test session termination: capture a request, log out, replay. Also test password-change and idle-timeout invalidation. Server-side session store must be destroyed on logout, not just the cookie cleared.
Real-world example
Session fixation - session cookie not rotated on login
◆ Info
Specimen #135797 · enter · awarded · 12 votes · resolved
Program enterSurface web
Root cause
The pre-authentication session identifier is reused after a successful login instead of being regenerated, so a session value planted before login becomes an authenticated session.
Method
- As attacker, visit the site (do not log in) and record the session + xsrf cookies
- Plant those same cookies in the victim's browser (shared machine, or via injection bug)
- Victim logs in with the fixed cookies
- Attacker reuses the original cookies and is now authenticated as the victim
Cookie: app.session=s%3AEHm0kA9uwWYHayOwdRQXbuZWEIRIliQZ...; XSRF-TOKEN=AAG02cId-yyza3k8uhQR7JKuB-4YOmhizkjM
Insight — Grab a session cookie pre-login, authenticate, and check whether the identifier changed; if it survives login, it's fixation. Especially impactful on shared machines or chained with a cookie-injection/CRLF bug.
Real-world example
Passwordless login token survives OAuth access-token revocation
◆ Info
Specimen #172837 · shopify · USD 500 · 11 votes · resolved
Program shopifySurface apiChain Malicious/compromised app -> stash passwordless token -&gTag oauthTag account-takeover
Root cause
A GraphQL mutation (adminPasswordlessLogin) issues a single-use passwordless login token. Revoking the app's OAuth access token does not invalidate previously-minted passwordless tokens, so a malicious app can retain a live login link after the user revokes it.
Method
- As an authorized app, call the passwordless-login mutation to obtain a login token (do not consume it)
- User revokes the app's access token in the admin
- Use the stored token: GET /admin?login_token=<token> — it still authenticates
POST /admin/api/graphql
X-Shopify-Access-Token: <app token>
Content-Type: application/graphql
mutation{adminPasswordlessLogin(input:{}){passwordlessLoginToken}}
# then:
https://<shop>.myshopify.com/admin?login_token=<passwordlessLoginToken>
Insight — Revocation must cascade to every derived credential. When testing OAuth/app integrations, mint a secondary token (magic link, passwordless token, refresh token, API key) from an app, then revoke the app and check whether the derived token still works. Orphaned tokens = persistence after 'revocation'.
Real-world example
Session/GraphQL token survives password change and logout
◆ Info
Specimen #283847 · security · none · 9 votes · resolved
Program securitySurface graphqlTag graphqlTag account-takeover
Root cause
The auth token (x-auth-token) and host session cookie used by the password-change GraphQL mutation are not invalidated when the password changes or the user signs out, so the captured request stays replayable.
Method
- Log in, open password-change form, submit and capture the GraphQL mutation (x-auth-token + __Host-session)
- Sign out / let the session appear to expire
- Replay the captured mutation with new password values -> password changes again without re-authenticating
POST /graphql
x-auth-token: <captured>
Cookie: __Host-session=<captured>
{ mutation changePassword(currentPassword, password, confirm) }
Insight — After any auth-state change (password change, logout, MFA reset), re-test whether old session cookies AND API/GraphQL bearer tokens are actually revoked. Sensitive mutations often trust a token that outlives the session.
Real-world example
Login-denial DoS by replaying an old/invalidated session token
◆ Info
Specimen #48416 · security · none · 8 votes · resolved
Program securitySurface webTag account-takeover
Root cause
The app destroys sessions on logout/password change, but replaying an old destroyed session cookie triggers logic that invalidates the victim's currently active session; looping the replay keeps kicking the victim out, denying them login.
Method
- Capture any authenticated request from the victim (an old session token)
- Send it to Burp Intruder
- Have the victim log in on another device
- Continuously replay the old-session request - each replay force-logs-out the victim's fresh session, so they can never stay logged in
(replay a captured request carrying an old/destroyed session cookie in a loop via Intruder)
Insight — Session-invalidation logic can be weaponised: if presenting a stale token invalidates the newest session, an attacker who once captured any token can perpetually deny the victim access. Test that presenting an expired token affects only that token, never the victim's active sessions.
Real-world example
Missing Secure flag enables MITM session hijack
◆ Info
Specimen #123748 · veris · none · 3 votes · resolved
Program verisSurface webTag account-takeover
Root cause
Session cookies (uid + user token) lacked the Secure attribute, so a network attacker who lures the victim to the HTTP version of the site captures the cookies and replays them to hijack the session.
Method
- Confirm auth cookies are set without Secure (sent over plain HTTP)
- On the same network, induce the victim to load an http:// URL in cookie scope
- Capture the uid/token cookies in transit
- Set the stolen cookies in your browser to assume the victim's session
# Cookie issued without Secure:
Set-Cookie: PHPSESSID=...; path=/; HttpOnly
# attacker overwrites own cookies with captured uid/token to take over session
Insight — Audit auth cookies for the Secure flag and force an http:// request in scope; if the session cookie is transmitted in clear it is a demonstrable MITM takeover, not just a header nag. Pair with any http->https redirect that still leaks the cookie first.
Real-world example
Sessions survive password change; mobile sessions not revocable
◆ Info
Specimen #194329 · nextcloud · awarded · 35 votes · resolved
Program nextcloudSurface webTag account-takeover
Root cause
Changing the account password does not invalidate existing authenticated sessions, and mobile/app sessions are not enumerated in the session-management UI, so a victim cannot terminate an attacker's foothold even after rotating credentials.
Method
- Obtain an active session (e.g. via mobile app login)
- Victim changes their password to lock the attacker out
- Attacker's existing session remains valid (no logout on password change)
- Victim's session list does not show the mobile session, so it cannot be revoked
Insight — After a password change, verify every other session is actually killed, and confirm the session-management list includes ALL session types (web + mobile + API tokens). Gaps here defeat the primary account-recovery control.
Real-world example
Pre-logout session cookie remains valid after CSRF-triggered logout
◆ Info
Specimen #737 · security · 100 · 24 votes · resolved
Program securitySurface web
Root cause
When a request arrived with an invalid authenticity_token the app logged the user out and issued a new unauthenticated cookie, but the previously issued authenticated session cookie was never server-side invalidated and stayed active.
Method
- Capture your authenticated session cookie
- Send a request with an invalid authenticity_token to trigger logout / new cookie issuance
- Replay the original captured cookie - it is still authenticated
Insight — Logout and session-reissue events should invalidate the old session server-side, not just hand the browser a new cookie. Test by replaying the pre-event cookie after logout/reset - many frameworks only rotate client-side.
Real-world example
Session not invalidated after password change
◆ Info
Specimen #1166076 · upchieve · none · 17 votes · resolved
Program upchieveSurface webTag account-takeover
Root cause
Changing the account password does not destroy other active sessions, so an attacker who already has a session (with the old password) retains access even after the victim resets the password.
Method
- Log in to the same account in two browsers.
- Change the password from browser A.
- Confirm browser B remains authenticated - the victim cannot evict the attacker by resetting the password.
Insight — Password change / reset must revoke all other sessions server-side. Test by holding a second session and changing the password from the first; a still-valid second session is a reportable session-management flaw (removes the victim's remediation path after compromise).
Real-world example
Session not invalidated on logout (cookie replay)
◆ Info
Specimen #152080 · coursera · none · 15 votes · resolved
Program courseraSurface webTag account-takeover
Root cause
Logout only clears the client cookie but does not invalidate the server-side session, so a copied session cookie re-authenticates long after logout.
Method
- Log in and capture all session cookies
- Log out and clear cookies in the browser
- Re-import the previously saved cookies
- Access is restored and account data is editable
Insight — Test session termination by saving cookies pre-logout and replaying them post-logout; a still-valid cookie proves the server keeps the session alive. Applies to both logout and password-change flows.
Real-world example
Sessions not fully invalidated after password reset (remember-me/refresh survive)
◆ Info
Specimen #15785 · security · awarded · 15 votes · resolved
Program securitySurface webTag account-takeoverTag oauth
Root cause
After a password change/reset, existing sessions are not all terminated; notably 'remember me' persistent sessions and OAuth refresh tokens keep working, so two different passwords remain concurrently valid.
Method
- Log in in two browsers (one with 'remember me' checked).
- Reset/change the password in one browser.
- Confirm the other browser's session (and any refresh token) still authenticates.
Insight — Password change must revoke ALL sessions and refresh/remember-me tokens server-side, not just the current cookie. Specifically test the persistent-login and OAuth refresh-token paths, which are the usual gaps.
Real-world example
Sessions not invalidated on password change/reset
◆ Info
Specimen #9950 · security · awarded · 11 votes · resolved
Program securitySurface webTag account-takeover
Root cause
Changing or resetting the password did not terminate existing sessions, so an attacker who hijacked a session keeps access even after the victim changes the password (the usual remediation step).
Method
- Obtain an active victim session
- Victim changes/resets their password to lock the attacker out
- Attacker's old session cookie remains valid
Insight — Always test session lifecycle: after a password change/reset from browser A, check whether an already-authenticated browser B is logged out. Persistence-after-password-change is a real ATO-recovery failure.
Real-world example
Authenticated edit page cached and served to the next user
◆ Info
Specimen #17105 · security · none · 4 votes · resolved
Program securitySurface webTag account-takeover
Root cause
After a password change forces logout, the previous user's authenticated page (their edit/profile page revealing username) was cached and rendered for the next account logging in on the same browser.
Method
- Log into account A and change its password (forces logout)
- Log into account B in the same browser
- Observe account A's edit page (leaking A's username) is displayed
Insight — Sensitive authenticated pages must send no-store/no-cache; test post-logout/post-password-change flows for cached user-identifying pages served cross-session.
Real-world example
Session revocation incomplete across clients/tokens
◆ Info
Specimen #67220 · shopify · awarded · 3 votes · resolved
Program shopifySurface mobile-iosTag account-takeover
Root cause
An explicit 'Expire User Sessions' admin action (and, in the merged case, a password change) reported success but did not invalidate every active session/token — the iOS app's session stayed authenticated.
Method
- Log in via the mobile app and the web admin concurrently
- In web admin click 'Expire User Sessions' (or change the account password)
- Confirm the mobile-app session/token still works despite the success message
Insight — After ANY forced-logout / password-change / de-provision action, replay every other live session (mobile app token, API token, second browser, remember-me cookie). 'Log out all sessions' features routinely miss non-web token stores.