API attack hunting (REST / GraphQL / gRPC) - BOLA/IDOR, BFLA, mass assignment, excessive data exposure, auth/JWT, introspection + batching, rate-limit abuse. OWASP API Top 10. Wiki-first, FIND schema output.
npx skills add https://github.com/Encod3d-Sec/TORCH --skill hunt-api
Assumes hunt-core for the scope gate, two-account rule, confirmation gate, enumeration limits, stop conditions, wiki protocol, FIND output, and Deadends. Do not re-derive any of that here.
qmd_query "API REST GraphQL gRPC BOLA BFLA mass assignment excessive data exposure OWASP API Top 10" via wiki-search MCP
Hub: [[web-moc]] (live web index). Primary page: [[api-security]]. Payload arsenal: wiki/payloads/api.md.
Anchors: [[api-testing]], [[graphql-attacks]].
Variants: [[grpc-web-attacks]] (gRPC-Web / protobuf transcoder abuse), [[rsql-injection]] with the [[rsql]] payload (RSQL/FIQL filter-query injection, e.g. Spring Data REST), [[rate-limit-bypass]] (header/race/distributed-source throttle bypass), [[redos]] payload (catastrophic-backtracking regex DoS in an input validator), [[jwt-attacks]] (token flaws).
For object-level authorization (BOLA/IDOR) see hunt-idor.
/api/, /v1/, /graphql, /rest/, gRPC (application/grpc, HTTP/2), Swagger UI (/swagger, /api-docs, /openapi.json), mobile/SPA backends.
Rank before testing. Not all surface is equally likely to be broken:
GET /orders/123 authorized, PATCH/DELETE not.Setup: two accounts per hunt-core (A owns, B attacks, separate profiles). Get the spec if any - Swagger/OpenAPI, GraphQL introspection, .proto.
Drive it through Burp for operator visibility. Push the load-bearing requests (the BOLA cross-account swap, the mass-assignment body, the BFLA verb/route call) into Repeater via Skill(hunt-burp) / the native Burp MCP (mcp__burp__*) so the operator can replay and inspect them; brute/fuzz belongs in Intruder (send_to_intruder), not a hand-rolled loop. A quick throwaway curl per account for the writeup PoC is fine.
__schema); gRPC server reflection (grpcurl -plaintext <h> list, then list <svc> / describe). No spec? Discover endpoints with ffuf -w <api-wordlist> -u https://HOST/FUZZ and fingerprint hosts with httpx, not a hand curl loop. This is service / endpoint DISCOVERY, not object enumeration - it is bounded by the engagement RoE (no_dos, scan-rate caps), NOT by the 5-to-20 object cap. Do not clamp the wordlist to 20.no_bruteforce, per hunt-core. Two or three adjacent IDs prove the sequential pattern; cite a total/pagination count for scale, never a sweep. Full bounded-sample loop and the trusted-identifier test live in hunt-idor. -> overlaps hunt-idor.role, isAdmin, verified, balance) to JSON bodies; look for the privilege/state change to actually take effect (confirm from a fresh read, step below).alg:none, weak secret, kid injection), API-key reuse, missing auth on some routes, OAuth scope creep.grpcurl to call methods; tamper protobuf fields; run the same authz tests as REST.no_bruteforce/no_dos - do not actually brute credentials or OTPs and never run this at volume against a live auth endpoint. See [[rate-limit-bypass]].When a direct call 403s or the extra field is stripped, it is not closed. Parameter pollution (?id=A&id=B - check and fetch may read different occurrences), array/nested wrapping of the field ({"user":{"role":"admin"}}), casing and separator variants of a mass-assign key (isAdmin/is_admin/admin), alternate content-type (form vs JSON vs XML), verb-override headers (X-HTTP-Method-Override), version downgrade (/v1/ predates the middleware /v2/ has - the most reliable), and batching to skip per-item authorization.
Chain: mass assignment -> privilege escalation. A role:admin/isAdmin:true that actually takes effect turns the whole BFLA admin surface reachable - re-run step 3 as the escalated principal. For the broader workflow/state-tampering angle hand off to hunt-bizlogic.
Distill a confirmed, GENERIC pattern (product + endpoint + impact, no client host): python3 scripts/wiki-stage.py --kind technique --slug <slug> --target-page techniques/web/api-security.md
NOT confirmation: a 200 echoing your own request back; an empty or shell response; B seeing an object that is shared, public, or org-visible; a body you have not compared against A's baseline; a BFLA endpoint returning 200 without the privileged action actually performed; a mass-assignment write returning 200 with the extra field accepted but the privileged field never verified in effect; any result not re-verified in a clean session.
IS confirmation:
200).role/isAdmin/balance) verified in effect from a fresh authenticated read, not merely accepted in the request body.Append: - [ ] API on <host> <endpoint> -- BOLA/BFLA/mass-assign all enforced;
extra fields ignored; JWT validated; introspection off
Record what you tried (pollution/array/verb/version-downgrade/batch), not just that it failed.
Take encod3d-sec/hunt-api from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.