OTP Reuse (Static Artifact): Time-Based OTP Replay and Rate Limit Bypass Techniques
Theory
Why This Matters
One-Time Password reuse vulnerabilities undermine the core security promise of OTP-based authentication: that each code is valid for exactly one use. Failures in OTP invalidation have been documented in enterprise authentication products and web platforms — the 2018 research by Arne Swinnen on SMS OTP reuse in multiple popular platforms demonstrated that codes remained valid after use for up to 60 seconds in some implementations. More critically, race condition vulnerabilities in OTP endpoints have allowed parallel exploitation of a single valid code, as demonstrated in responsible disclosure reports against financial applications where the OTP validation endpoint lacked appropriate database-level locking. For attackers who can observe a single OTP (via phishing, SIM swap, or shoulder surfing), the ability to reuse it extends their window of exploitation well beyond the intended 30-second TOTP interval.
Core Concept
A one-time password (OTP) is an authentication code designed to be valid for a single use and a limited time window. The security invariants are: the code must be computationally unpredictable, it must be invalidated immediately after successful validation (preventing reuse), and it must be tightly bound to both the specific user session and the specific authentication context.
OTP reuse after successful validation occurs when the server validates the OTP and grants access but does not record that this code has been consumed. On the next login attempt, the same code — still within its TOTP validity window — is accepted again. This converts the "one-time" property into "valid-for-the-full-30-second-window" semantics, which is particularly dangerous because phished OTPs arrive at the attacker's relay server with 20–29 seconds of validity remaining.
OTP valid across multiple sessions is a related flaw: the OTP is invalidated after use in the session that consumed it, but if the same user has multiple concurrent pending authentication sessions, the same OTP code is accepted for all of them. An attacker who initiates a separate authentication attempt for the victim's account while the victim is completing their own legitimate login can consume the same OTP in their own session.
TOTP clock skew window too wide is a configuration flaw: TOTP implementations accept codes from a window around the current time (typically ±1 step, i.e., the previous and next 30-second intervals). Some implementations accept ±5 or more steps (±150 seconds) to accommodate client clock drift, drastically increasing the exploitable code space and the time available to use a phished code.
Race condition on OTP validation is an implementation flaw where the OTP is not atomically marked as used before the authentication response is sent. Two concurrent requests submitting the same OTP can both pass the uniqueness check (both read "not used" from the database) before either one writes "used", a classic TOCTOU (Time Of Check, Time Of Use) vulnerability.
Technical Deep-Dive
Testing OTP reuse with Burp Repeater:
-- Step 1: Complete a valid 2FA login with OTP code 123456
POST /login/verify-otp HTTP/1.1
Host: target.example.com
Cookie: session=abc123partial
Content-Type: application/json
{"otp": "123456"}
-- Step 2: Immediately repeat the same request (within 30-second TOTP window)
POST /login/verify-otp HTTP/1.1
Host: target.example.com
Cookie: session=abc123partial_new -- new pending session from a fresh login
Content-Type: application/json
{"otp": "123456"}
-- If this second request succeeds, OTP reuse is confirmed
Race condition test with Burp Suite's parallel requests (Turbo Intruder):
# Turbo Intruder script for parallel OTP submission
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=10,
requestsPerConnection=1,
pipeline=False)
# Send 10 parallel requests with the same OTP code
for i in range(10):
engine.queue(target.req, str(i))
def handleResponse(req, interesting):
if "success" in req.response or "200" in req.status:
table.add(req)
Testing clock skew window:
import pyotp, time
secret = "BASE32SECRETHERE" # known from own TOTP setup for testing
totp = pyotp.TOTP(secret)
# Generate codes for a wide time window
current = int(time.time())
for offset in range(-300, 301, 30): # ±5 minutes in 30-second steps
ts = current + offset
code = totp.at(ts)
print(f"T{offset:+d}s (step {offset//30:+d}): {code}")
# Test each code against the target — accepted range reveals the clock skew tolerance
Security Assessment Methodology
- Obtain a valid OTP — Complete a legitimate 2FA authentication using your test account and record the OTP code used and the exact timestamp of its use.
- Test immediate reuse — Within 5 seconds of successful authentication, initiate a fresh login with the same credentials. When prompted for the OTP, submit the same code again. If it is accepted, OTP reuse is confirmed.
- Test reuse from a separate session — Open a private browser window and initiate a login for the same test account. Submit the OTP code that was just used in the first browser window. Cross-session reuse means a phishing relay has a full 30-second window to reuse a captured code.
- Test the race condition — Use Burp Suite's Turbo Intruder or the Repeater Send Group feature to submit 5–10 identical OTP validation requests simultaneously. Check whether more than one succeeds.
- Test the clock skew window — Generate TOTP codes for your test account at T-90s, T-60s, T+60s, T+90s relative to the current TOTP step. Submit each to determine how many steps outside the current window the server accepts.
- Test OTP validity after password reset — If a user resets their password, any OTP generated before the reset should be invalidated. Test whether pre-reset OTP codes remain valid.
Defensive Countermeasure — Maintain a server-side used-OTP store (Redis set or database record) that persists used TOTP codes for the duration of their validity window (at minimum the current and adjacent accepted steps), checked and updated atomically using a database transaction or Redis SETNX before granting access — so that a code accepted once is structurally blocked from being accepted again in any session.
Common Assessment Errors
- Testing reuse too slowly — TOTP codes change every 30 seconds. Reuse testing must happen within the same 30-second window (or the accepted clock skew window). Prepare the second request in Burp Repeater before triggering the first login.
- Not testing cross-session reuse — Testing reuse within the same browser session misses the phishing relay scenario. Always test from a completely separate session (private window, different browser, or Burp with fresh cookies).
- Forgetting to test HOTP endpoints — HOTP (counter-based OTP) has different reuse characteristics than TOTP: a code is valid until used, not time-limited. HOTP reuse across parallel sessions is a separate test case.
- Missing the resend-OTP race condition — Some applications allow resending the OTP. Rapidly requesting multiple OTPs and then submitting a previously issued code tests whether all issued codes remain valid simultaneously.
- Not documenting the accepted clock skew window — Reporting "OTP reuse is possible" without quantifying the exploitation window (seconds or minutes) misrepresents the severity. The window determines whether real-time phishing relay attacks are feasible.
- Overlooking backup code invalidation — Backup/recovery codes have the same one-time requirement as TOTP codes but are often stored and invalidated by a completely different code path. Test backup codes independently.
NICE Framework Alignment
| Code | Knowledge/Skill/Task Statement | How This Card Develops It |
|---|---|---|
| K0007 | Knowledge of authentication, authorisation, and access control methods | Covers the security invariants of OTP-based authentication and the implementation flaws that violate them |
| K0065 | Knowledge of policy-based and role-based access controls | Explains how OTP reuse enables repeated authentication bypass within the validity window |
| S0001 | Skill in conducting vulnerability scans and recognising vulnerabilities in security systems | Develops race condition testing and clock skew analysis techniques using Burp Suite tools |
| T0028 | Conduct and/or support authorised penetration testing on enterprise network assets | Provides a complete OTP security assessment methodology addressing reuse, race conditions, and clock skew |
Further Reading
- OWASP Cheat Sheet Series: Multifactor Authentication — OWASP Foundation
- Real-Time Phishing of Two-Factor Authentication Codes — Arne Swinnen (security research)
- TOTP: Time-Based One-Time Password Algorithm — RFC 6238 (IETF)
Challenge Lab
Reinforce your learning with a hands-on generated challenge based on this card's competency.