July 26, 2026
Single-Endpoint Race Conditions: Exploiting an Email Change Race Condition (PortSwigger Lab)
How two requests to the same endpoint let me inherit an administrator account.

By Cybernerddd
2 min read
Draft
Today I worked through one of the most interesting PortSwigger labs I've completed so far.
The lab focused on single-endpoint race conditions, and it finally made a concept from James Kettle's Smashing the State Machine research click for me.
Unlike the previous race condition labs where two different endpoints collide, this one only required racing the same endpoint against itself.
The Scenario
The application allowed users to change their email address.
There was also an important detail:
An administrator invitation was already pending for carlos@ginandjuice.shop.
Whoever successfully claimed that email address would automatically inherit administrator privileges.
That immediately suggested that the email change workflow was worth investigating.
Predict: Initial Recon
I began by studying the request responsible for changing a user's email.
POST /my-account/change-emailPOST /my-account/change-emailThe request contained:
- Session cookie
- CSRF token
- Email parameter
I experimented with several inputs to understand how the endpoint behaved.
For example, I replaced my exploit-server email with my personal Gmail address.
Nothing interesting happened.
I also removed my session cookie and confirmed that authentication was enforced, meaning the application tied the email change process to the current session.
Probe: Testing for a Race Condition
Following the ideas from Smashing the State Machine, I created two almost identical requests.
Request A:
email=wiener@exploit-server.netemail=wiener@exploit-server.netRequest B:
email=carlos@ginandjuice.shopemail=carlos@ginandjuice.shopI first sent both requests in sequence.
Everything behaved normally.
Only my own confirmation email arrived.
No race condition.
Prove: Parallel Execution
The real test came when I sent both requests simultaneously using Burp Suite Repeater's parallel send feature.
Instead of receiving a confirmation email for my own address, my inbox contained this:
To:
wiener@exploit-server.net
Body:
To confirm your email change to
carlos@ginandjuice.shopTo:
wiener@exploit-server.net
Body:
To confirm your email change to
carlos@ginandjuice.shopThat was the breakthrough.
The application had mixed two different request states together.
Understanding What Happened
The server appeared to perform several operations internally:
Receive request
↓
Save unconfirmed email
↓
Queue confirmation email
↓
Generate email body
↓
Send emailReceive request
↓
Save unconfirmed email
↓
Queue confirmation email
↓
Generate email body
↓
Send emailThe race condition occurred because two threads were updating the same account simultaneously.
One request selected my email address as the recipient.
Before the email body was generated, another request updated the database with Carlos' email.
The result was a confirmation email sent to me that actually confirmed ownership of Carlos' email address.
This behavior closely mirrors the vulnerability James Kettle discovered in GitLab.
The Impact
After clicking the confirmation link, my account email became:
carlos@ginandjuice.shopcarlos@ginandjuice.shopBecause this address already had a pending administrator invitation, my account immediately inherited administrator privileges.
The admin panel appeared.
From there I completed the lab by deleting the user Carlos.
Key Takeaways
- Not every race condition requires two different endpoints.
- Parallel requests can expose inconsistencies inside complex workflows.
- Email verification flows are excellent places to investigate state transitions.
- Reading research papers before attempting labs makes the learning experience significantly deeper.