Conditional Competition at Portswigger Training Ground
Conditional competition for temporary intermediate states of objects
Lab: Partial construction race conditions
Necessary knowledge points before experiment
Some frameworks try to prevent accidental data corruption by using some form of request locking. For example, PHP’s native session handler module handles only one request per session at a time.
Many applications create objects in multiple steps, which can introduce temporary intermediate states that can be exploited.
For example, when registering a new user, an application might create the user in the database and set its API
key using two separate SQL
statements. This leaves a user with an existing door
but whose API
key is uninitialized.
Frameworks often allow you to pass in arrays and other non-string data structures using non-standard syntax. For example, in PHP:
param[]=foo
is equivalent toparam = ['foo']
param[]=foo & amp;param[]=bar
is equivalent toparam = ['foo', 'bar']
param[]
is equivalent toparam = []
If an account is registered, authentication is issued before the SQL
statement is assigned a value.
Experimental requirements
This experiment includes a user registration mechanism. The race condition allows you to bypass email verification and register with an arbitrary email address that does not belong to you. To resolve the lab issue, create an account exploiting this race condition, then log in and delete user carlos.
Infiltration begins
- Visit the corresponding shooting range interface
https://portswigger.net/web-security/race-conditions/lab-race-conditions-partial-construction
- start shooting range
1. Site Analysis
This is a SHOP
type of website
You can view article information, purchase, log in, register and other function points. According to the prompts, we come to the registration function point.
You need a username, email and password to register. I found that you can only use the recommended email to register.
Tried to register account 1 and found that the registration was successful.
I registered the account of 1 again and found that I could not register.
I tried to register account 2 and found that the registration was successful using the same email address, indicating that the same email address can be registered and reused.
However, you cannot log in directly with your account and password, and you need to activate by email.
Got into trouble without knowing my email address
2. Find suspicious function points (view Burp history for analysis)
The normal process is
Front-end registration → The back-end sends an email and pre-registers the user’s account/password information in the database → User activation email → The back-end assigns permissions to the user → The user can access normally
Register to send packets
The account obtained through competition under these conditions also has no authority and is useless
While browsing, I found a suspicious js
const createRegistrationForm = () => {<!-- --> const form = document.getElementById('user-registration'); const usernameLabel = document.createElement('label'); usernameLabel.textContent = 'Username'; const usernameInput = document.createElement('input'); usernameInput.required = true; usernameInput.type = 'text'; usernameInput.name = 'username'; const emailLabel = document.createElement('label'); emailLabel.textContent = 'Email'; const emailInput = document.createElement('input'); emailInput.required = true; emailInput.type = 'email'; emailInput.name = 'email'; const passwordLabel = document.createElement('label'); passwordLabel.textContent = 'Password'; const passwordInput = document.createElement('input'); passwordInput.required = true; passwordInput.type = 'password'; passwordInput.name = 'password'; const button = document.createElement('button'); button.className = 'button'; button.type = 'submit'; button.textContent = 'Register'; form.appendChild(usernameLabel); form.appendChild(usernameInput); form.appendChild(emailLabel); form.appendChild(emailInput); form.appendChild(passwordLabel); form.appendChild(passwordInput); form.appendChild(button); } const confirmEmail = () => {<!-- --> const container = document.getElementsByClassName('confirmation')[0]; const parts = window.location.href.split("?"); const query = parts.length == 2 ? parts[1] : ""; const action = query.includes('token') ? query : ""; const form = document.createElement('form'); form.method = 'POST'; form.action = '/confirm?' + action; const button = document.createElement('button'); button.className = 'button'; button.type = 'submit'; button.textContent = 'Confirm'; form.appendChild(button); container.appendChild(form); }
3. Js analysis | Benchmarking behavior
Get a rough idea of naming from json
- Function
createRegistrationForm
creates a registration form by operating DOM - The function
confirmEmail
is used to create a confirmation email function.
In the confirmation email function, the POST
request’s /confirm
endpoint is accessed, and the spliced parameter is token
The created form will obtain token
and confirm submission
4./confirm endpoint analysis | Benchmark behavior
token
is forbidden if it is empty
token
is 1, which proves that the token is verified to be 0, which is incorrect.
If it is a newly registered user, it is usually empty. You can find that it is banned. The try array is not banned.
5. Complete the experiment | Prove concept
When I register an account, if there are two SQL
statements, can I give the account an empty token
before the application assigns the token
? This way you can bypass the email verification mechanism for user registration.
Construct an enumeration to create users, and at the same time, the conditional competition mechanism of concurrent token
verification is empty and the user is successfully acquired.
User informationa4016:111111
Login account
Open the management panel and delete the user to complete the experiment.