Source: Bug Bounty Bootcamp by Vickie Li
APIs are the backbone of modern web and mobile apps. They often expose the same backend logic with fewer protections than the main web interface. Many programs explicitly include API endpoints in scope.
REST – uses HTTP verbs (GET/POST/PUT/DELETE), JSON responses, resource-based URLs (e.g., /users/123).
SOAP – XML-based, uses WSDL to define the service interface, typically POST to a single endpoint.
GraphQL – single endpoint (usually /graphql) with a query language; clients specify exactly what data they need.
/api, /api/v1, /swagger, /swagger-ui.html, /openapi.json, /docsRoute the mobile app or web app through Burp to capture all API calls. This reveals undocumented endpoints and parameter names that aren't in public docs.
REST APIs follow predictable patterns. If you find /users/123, try:
GET /users (list all users?) POST /users (create user?) PUT /users/123 (update user 123?) DELETE /users/123 (delete user 123?) GET /users/123/posts (sub-resource?)
New versions often have stricter security. Try downgrading:
/api/v2/users/123 -> /api/v1/users/123 -> /api/users/123
Use introspection to enumerate the entire schema:
POST /graphql
{
"__schema" {
types { name fields { name type { name } } }
}
}
Or list all types:
{ "__schema" { types { name } } }
Tools: InQL (Burp extension), GraphQL Voyager (visualize schema).
The most common API vulnerability. Change object IDs in requests:
GET /api/v1/users/1236/orders -> /api/v1/users/1237/orders GET /api/v1/invoices/9921 -> /api/v1/invoices/9922
Test all HTTP verbs – read access may be restricted but write access may not be:
PUT /api/v1/users/1237 {"email": "attacker@evil.com"}
DELETE /api/v1/users/1237
Admin endpoints accessible to regular users:
GET /api/v1/admin/users
POST /api/v1/users/1237/promote {"role": "admin"}
APIs often return full objects and rely on the client to filter. Check raw API responses for fields not shown in the UI:
GET /api/v1/users/1237
-> {"id": 1237, "email": "...", "ssn": "123-45-6789", "internal_role": "admin"}
Send malformed requests and observe errors:
GET /api/v1/users/abc (invalid type -> stack trace?)
POST /api/v1/users {} (missing fields -> field names revealed?)
PUT /api/v1/users/1 {"x":"y"} (unknown field -> accepted or rejected with details?)
APIs often lack rate limiting. Test by sending many rapid requests:
for i in $(seq 1 100); do
curl -s -o /dev/null -w "%{http_code}
" "https://api.example.com/users/$i"
done
No 429 responses = no rate limiting. Impact: account enumeration, brute force, mass data harvesting.
If the API accepts JSON bodies, try adding privileged fields:
POST /api/v1/users/register
{"username": "attacker", "password": "pass", "role": "admin", "verified": true}
role, admin, verified, is_admin fields to POST/PUT bodies