Browse CTFs New CTF Sign in

TOCTOU

web_injection_logic Difficulty 1–5 30 min certifiable

Theory

Why This Matters

A 2020 penetration test of an enterprise HR platform found that an employee's role was checked at login and embedded in a 7-day JWT. When the employee was terminated, the JWT remained accepted by all API endpoints for the full 7-day window — the API validated the signature but never re-checked account status against the live database. The former employee retained access to payroll data and headcount forecasts for nearly a week. This is a TOCTOU flaw at the identity layer: the check occurred at T₁ (login), the use at T₂ (every subsequent request), with the state changing in between. The same structural flaw underlies cached authorisation decisions and price-check races in e-commerce.

Core Concept

Time-of-Check-Time-of-Use (TOCTOU) is a vulnerability class where an application reads state at T₁ (check), caches or embeds it, then relies on it at T₂ (use) without re-verifying. The TOCTOU window is [T₁, T₂]. Any state change within that interval invalidates the check's conclusion.

TOCTOU is the generalisation that subsumes several specific patterns. Double-spend is a TOCTOU where T₁ and T₂ are microseconds apart, exploitable via concurrent requests. Stale session TOCTOU has a window of hours or days, exploitable by simply waiting for a role change. In web applications the dominant patterns are stale JWT role claims, cached authorisation decisions, and long-lived session state that does not reflect revocation. File-system TOCTOU (symlink swap between access() and open()) is the classic Unix form but rare in web contexts.

Short token lifetime combined with server-side session validation (re-checking user status in the database on every privileged request) closes the identity TOCTOU window. Token revocation lists stored in Redis allow immediate invalidation without waiting for token expiry.

Technical Deep-Dive

# Scenario: role checked at login, embedded in 7-day JWT
# T1 (Monday 09:00): login, role = "finance_manager" encoded in JWT
POST /api/auth/login HTTP/1.1
Host: hr.example.com
Content-Type: application/json

{"email": "[email protected]", "password": "P@ssw0rd1"}

# Response: {"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}
# JWT payload: {"sub": "alice", "role": "finance_manager", "exp": 1720000000}

# T2 (Wednesday 14:00): Alice is terminated, account disabled in HR system
# DB: UPDATE users SET active = false, role = NULL WHERE email = '[email protected]'

# T3 (Wednesday 15:00): Alice's JWT is still valid (not expired, not revoked)
# Alice accesses payroll endpoint with the still-valid token
GET /api/payroll/export HTTP/1.1
Host: hr.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

# Vulnerable server: validates JWT signature only, trusts embedded role claim
# Response: 200 OK + payroll data (TOCTOU exploited — state changed between T1 and T3)

# Secure server: re-checks user status on every request
# SELECT active, role FROM users WHERE id = :sub
# active=false -> Response: 401 Unauthorized
# Detecting TOCTOU in JWT-based authorisation
# Step 1: Obtain a valid JWT for an account with elevated privilege
# Step 2: Trigger a role or status change server-side (or wait for one in a test environment)
# Step 3: Attempt to use the old JWT for privileged operations

import jwt, requests, datetime

# Decode JWT without verification to inspect claims
token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbGljZSIsInJvbGUiOiJhZG1pbiIsImV4cCI6OTk5OTk5OTk5OX0.sig"
claims = jwt.decode(token, options={"verify_signature": False})
print(f"Role in token: {claims['role']}")
print(f"Expiry: {datetime.datetime.fromtimestamp(claims['exp'])}")

# Simulate post-revocation access attempt
s = requests.Session()
s.headers.update({"Authorization": f"Bearer {token}"})

r = s.get("https://hr.example.com/api/admin/users")
if r.status_code == 200:
    print("[TOCTOU] Token accepted after role revocation — stale claim exploited")
elif r.status_code in (401, 403):
    print("[SECURE] Server re-validated role against live database")

Security Assessment Methodology

  1. Identify long-lived state that encodes authorisation — Look for JWTs with expiry > 1 hour, session cookies without server-side expiry, and cart tokens that embed price at creation time. These are all candidates for TOCTOU exploitation.
  2. Trigger a state change and test stale access — In a test environment: revoke a role, disable an account, or change a price. Then use the pre-change token/session/cart to attempt the previously authorised action.
  3. Decode JWT claims and check for role/plan fields — Use jwt.decode with verify_signature=False or jwt.io. If role, plan, or group claims are embedded, test whether those claims are re-validated against the live database on each request.
  4. Test cart price staleness — Add an item to cart, note the price. Request an admin to change the item price (or do so yourself in a test environment). Complete the checkout with the original cart. Verify whether the old price or new price is charged.
  5. Test concurrent TOCTOU (race condition) — For short-window TOCTOU (balance checks), use Turbo Intruder as described in the race-condition card. For long-window TOCTOU (role revocation), sequential testing suffices.
  6. Check for token revocation mechanisms — Issue a JWT, then test whether logging out (which should invalidate the token) actually prevents subsequent use of the old token. Many stateless JWT implementations do not maintain a revocation list.

Defensive Countermeasure — For identity TOCTOU: set JWT expiry to 15 minutes maximum for privileged operations, and maintain a server-side token revocation list (Redis SET jti:<jti_value> 1 EX <ttl>) checked on every request. Alternatively, abandon stateless JWTs for sensitive roles and use server-side sessions with database-backed session records that are invalidated immediately on role change or account deactivation. For cart price TOCTOU: re-validate all prices against the live product catalog at checkout time, not at cart creation time, and surface any price changes to the user before confirming the order. Never honour a client-supplied price embedded in a cart token.

Common Assessment Errors

  • Treating TOCTOU only as a race condition — The concurrent (short-window) variant is the most well-known, but the sequential (long-window) variant — stale JWT, stale session, stale cart — is equally impactful and requires no concurrent request tooling to exploit.
  • Not testing post-logout token validity — Logout should invalidate the token. Many applications only clear the client-side cookie without adding the JWT to a revocation list, leaving the token usable via direct API calls after logout.
  • Assuming short JWT expiry alone is sufficient — A 1-hour JWT window is still a 1-hour TOCTOU window. For account termination scenarios, even 5 minutes is unacceptable. Revocation lists are necessary for immediate invalidation.
  • Missing the cart price TOCTOU vector — Cart token price staleness is a financial TOCTOU that does not involve authentication. It is often found in e-learning, event ticketing, and flash-sale platforms where prices change frequently.
  • Overlooking cached authorisation decisions — Some applications cache the result of a database role check in a local cache (Redis, Memcached) with a long TTL. A role change in the database does not propagate to the cache until TTL expiry, creating a TOCTOU window equal to the cache TTL.
  • Confusing TOCTOU with IDOR — IDOR is accessing a resource you are not authorised to access at all. TOCTOU is accessing a resource you were previously authorised to access, but where that authorisation has since been revoked. Both result in unauthorised access, but the root cause and fix are different.

NICE Framework Alignment

Code Knowledge/Skill/Task Statement How This Card Develops It
K0007 Knowledge of authentication, authorization, and access control methods Demonstrates that TOCTOU is a failure of continuous authorisation — the check must be repeated at the point of use
K0065 Knowledge of web application security testing techniques Develops JWT claim analysis and post-revocation access testing methodology
K0070 Knowledge of system and application security threats and vulnerabilities Establishes TOCTOU as the unifying theoretical framework for race conditions, stale sessions, and stale carts
S0001 Skill in conducting vulnerability scans and recognizing vulnerabilities in security systems Trains long-window TOCTOU testing through state-change-then-access sequences
T0028 Identify and analyze vulnerabilities and risks in web applications Applies T₁/T₂ interval analysis to identify state staleness risks across JWT, session, and cart implementations
T0570 Perform technical security assessments of web applications Structures TOCTOU assessment from long-lived state identification through post-revocation access proof

Further Reading

  • Bishop, M. & Dilger, M. (1996). Checking for Race Conditions in File Accesses — Computing Systems Journal
  • RFC 7519: JSON Web Token (JWT), Section 4.1.7 (jti claim) — IETF
  • PortSwigger Web Security Academy: JWT Attacks — PortSwigger

Challenge Lab

Reinforce your learning with a hands-on generated challenge based on this card's competency.