| Field | Detail |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | Race Conditions / Broken Cryptography |
| Difficulty | Practitioner |
| Objective | Exploit timestamp-based token generation by sending simultaneous password reset requests for wiener and carlos to obtain the same token, then use it to reset carlos's password |
Exploiting Time-Sensitive Vulnerabilities¶
I logged in as wiener:peter and requested a password reset:
The reset token 2ef6891c9ce99cc9db45d68bfe8f1f11cbefdd1c is 40 hex characters — a SHA-1 hash:
Substituting carlos for wiener in the URL with the same token returned "invalid token" — user-specific. Resending the same forgot-password POST produced a different token each time, confirming it's generated fresh per request.
If the token is derived from a timestamp with insufficient entropy, two requests fired at the same millisecond would produce the same token. I duplicated the POST and sent both in parallel, measuring response times:
One took 584ms, the other 886ms — a ~300ms gap. Truly parallel requests should have similar response times. The staggered processing is PHP session locking: PHP serializes requests sharing a session ID, so what looks like a parallel send becomes sequential at the server. When you see this, create separate sessions for each request.
I fetched a fresh session by sending a GET with no cookie, extracting the new phpsessionid and a fresh CSRF token. Now with two independent sessions, both requests responded at the same time:
And the email client showed two emails with the same token. Timestamp-based token generation is cryptographically broken: a SHA-1 of a timestamp has a deterministic output for any two requests arriving in the same millisecond. Cryptographic token security requires a CSPRNG — timestamps provide neither secrecy nor sufficient entropy.
With the collision model confirmed, I updated one request to target carlos and sent both in parallel:
One email arrived in our client:
If the requests were truly simultaneous, wiener and carlos received the same token. Substituting carlos for wiener in the reset URL while keeping the token:
Reset form loaded for carlos — the token was valid. Setting a new password:
Logged in as carlos, navigated to /admin:
Deleted carlos. The vulnerability is that the token's input (the timestamp) is predictable when requests arrive simultaneously, making the output predictable too.
Lab solved