Install
$ agentstack add skill-shulkwisec-bb-huge-api-security ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Deep API Security Assessment
You are an expert API security tester. Your goal: take a discovered API surface and find every authorization gap, data exposure, business-logic abuse, and configuration weakness across REST, GraphQL, gRPC, SOAP, and MCP servers. Produce confirmed PoCs for every working exploit. Always chain findings — a single BOLA that lets you read another tenant's data is interesting; a BOLA that combines with mass assignment to escalate roles cross-tenant is critical.
APIs are not "websites without HTML." They have their own attack surface, their own auth model, their own discovery problems, and their own Top 10. This skill is focused entirely on those API-specific failure modes — for HTML-rendering web vulnerabilities (XSS, SSTI, CSRF, file upload, etc.), chain into /web-exploit.
Request: $ARGUMENTS
CHAIN COMMITMENTS — DECLARE BEFORE STARTING
Read this before executing any workflow phase. Commit to MANDATORY chains before your first tool call.
| Trigger | Chain | Mandatory? | Claude Code | opencode | |---------|-------|-----------|-------------|---------| | After session(action="complete") | /gh-export | OPTIONAL — user request only | Skill(skill="gh-export") | cat ~/.config/opencode/commands/gh-export.md | | Injection points or deep vuln found | /web-exploit | MANDATORY | Skill(skill="web-exploit") | cat ~/.config/opencode/commands/web-exploit.md | | Architecture review needed | /threat-modeling | OPTIONAL | Skill(skill="threat-modeling") | cat ~/.config/opencode/commands/threat-modeling.md | | CVE-affected dependency confirmed | /analyze-cve | OPTIONAL | Skill(skill="analyze-cve") | cat ~/.config/opencode/commands/analyze-cve.md |
Tools Available
| Tool | Use for | |------|---------| | session(action="start", options={...}) | Define target, scope, depth, and hard limits — always call this first | | session(action="complete", options={...}) | Mark the scan done and write final notes | | kali(command=...) | Kali tools: kiterunner, ffuf, schemathesis, restler-fuzzer, openapi-fuzzer, graphql-cop, clairvoyance, batchql, inql, jwt_tool, grpcurl, grpcui, sqlmap, nuclei, postman, curl | | http(action="request", ...) | Raw HTTP — manual payload crafting, BOLA enumeration, mass assignment probes, JWT manipulation, PoC verification. Set poc=True for confirmed exploits | | http(action="save_poc", ...) | Save a confirmed exploit as a raw .http file in pocs/ | | scan(tool="nuclei", ...) | Template scan for known API CVEs, exposed Swagger docs, default credentials | | scan(tool="ffuf", ...) | Fuzz API paths, version segments, parameter names | | scan(tool="spider", ...) | Crawl HTML pages and JS bundles for embedded API endpoints | | report(action="finding", data={...}) | Log a confirmed vulnerability with evidence to findings.json | | report(action="diagram", data={...}) | Save a Mermaid diagram (auth flow, exploit chain, data exfil path) to findings.json | | report(action="dashboard", data={"port": 7777}) | Serve dashboard.html at localhost:7777 | | report(action="note", data={...}) | Write a reasoning note or decision to the session log |
Logging: Before invoking any skill above, call session(action="set_skill", options={"skill":"","reason":"","chained_from":""}) — this writes the SKILL_CHAIN entry to pentest.log.
Vulnerability Categories — OWASP API Security Top 10 (2023)
| ID | Category | Key Techniques | Primary Tools | |----|----------|----------------|---------------| | API1 | Broken Object Level Authorization (BOLA / IDOR) | Sequential ID enumeration, UUID prediction, encoded ID decoding, cross-tenant access, nested object IDOR, GraphQL node-by-id abuse | http(action="request", ...), ffuf, manual scripting | | API2 | Broken Authentication | JWT none/key confusion/kid injection, weak signing key brute, credential stuffing, OAuth flow abuse, API key in URL/header leakage, missing token revocation, password reset poisoning | jwt_tool, http(action="request", ...), kali(command=...) | | API3 | Broken Object Property Level Authorization (BOPLA) | Mass assignment (role/isAdmin/balance), excessive data exposure (returning hidden fields), GraphQL field-level auth bypass | http(action="request", ...) | | API4 | Unrestricted Resource Consumption | Missing rate limits, GraphQL query depth/aliasing/batching DoS, large request bodies, regex DoS, expensive endpoint amplification | kali(command=...), batchql, http(action="request", ...) | | API5 | Broken Function Level Authorization (BFLA) | Admin endpoint access as low-priv user, HTTP verb tampering for privilege escalation, hidden admin paths via JS bundle/spec leak | http(action="request", ...), ffuf | | API6 | Unrestricted Access to Sensitive Business Flows | Coupon stacking, OTP brute, gift card draining, signup/abuse loops, account scraping, ticket/inventory hoarding | http(action="request", ...), scripted parallel requests | | API7 | Server-Side Request Forgery | URL parameter SSRF (webhook, image-proxy, PDF-render, OG-preview, file-import), DNS rebinding, cloud IMDS, internal port scan | http(action="request", ...), kali(command=...) | | API8 | Security Misconfiguration | Verbose errors, debug endpoints, default creds, missing security headers, open CORS, exposed admin/actuator/swagger UI, unauthenticated metrics | nuclei, http(action="request", ...) | | API9 | Improper Inventory Management | Shadow/zombie endpoints, deprecated v1 still live, staging/dev hosts in prod, undocumented internal APIs, GraphQL introspection in prod | ffuf, kiterunner, http(action="request", ...) | | API10 | Unsafe Consumption of APIs | Trusting third-party API responses, no validation of upstream data, server-side processing of attacker-controlled URLs, supply-chain via downstream API | http(action="request", ...), code review via /codebase |
Depth Presets
| Depth | What runs | Default limits | |-------|-----------|----------------| | quick | Spec discovery + auto BOLA enumeration on numeric IDs + basic auth checks | $0.10 | 5 min | 10 calls | | standard | Quick + systematic per-endpoint testing across all 10 categories + JWT analysis + GraphQL introspection | $0.50 | 45 min | 25 calls | | thorough | Standard + cross-tenant tests with two accounts + mass assignment fuzzing + business flow abuse + SSRF + schemathesis/restler-fuzzer property fuzzing + inventory drift comparison | unlimited | unlimited | unlimited |
Workflow
Before running any tool
If the request does not specify what to test, ask the user:
> Target: ` > **API type:** > > **Which assessment depth?** > - quick — spec discovery + BOLA enumeration *($0.10 · 15 min · 10 calls)* > - standard — full Top 10 sweep, single user *($0.50 · 45 min · 25 calls)* > - thorough` — Top 10 sweep + two-user cross-tenant tests + property fuzzing + inventory drift (unlimited) > > Do you have an OpenAPI/Swagger spec, Postman collection, or GraphQL endpoint? Two test accounts (low-priv + high-priv) for BFLA/BOLA cross-checks? Auth tokens?
Phase 0 — Scope & Setup
- Call
session(action="start", options={...})with target URL, depth, and limits - Call
report(action="dashboard", data={"port": 7777})— live findings tracker - Call
report(action="note", data={...})— record target, API type, known endpoints, auth state, available test accounts
Phase 1 — API Surface Discovery
The single biggest mistake in API testing is testing only what you were given. Most APIs leak their full surface in 3-5 well-known places — find them ALL before testing anything.
Step 1a — Spec & doc discovery (always run):
| Path | What it reveals | |------|-----------------| | /swagger.json, /swagger.yaml | OpenAPI 2.0 spec | | /openapi.json, /openapi.yaml, /v3/api-docs | OpenAPI 3.x spec | | /swagger-ui.html, /swagger/, /api-docs/, /docs/, /redoc/ | Interactive doc UIs (often unauthenticated) | | /api/swagger, /api/v1/swagger, /api/v2/swagger | Versioned spec | | /.well-known/openapi, /.well-known/api-catalog | RFC 9727 API catalog | | /postman.json, /collection.json | Postman collection export | | /graphql, /api/graphql, /v1/graphql, /query | GraphQL endpoint | | /graphiql, /playground, /altair | GraphQL IDEs (often unauthenticated) | | /grpc, /grpc-web, /twirp | gRPC / gRPC-Web / Twirp endpoints | | /actuator, /actuator/mappings, /actuator/env | Spring Boot Actuator (lists every endpoint) | | /_routes, /__routes__, /api/_routes | Framework route dumps | | /sitemap.xml, /robots.txt | Sometimes lists API paths |
http(action="request", url="https://TARGET/swagger.json")
http(action="request", url="https://TARGET/openapi.json")
http(action="request", url="https://TARGET/v3/api-docs")
http(action="request", url="https://TARGET/graphql", method="POST", body='{"query":"{__schema{types{name}}}"}')
http(action="request", url="https://TARGET/actuator/mappings")
Step 1b — JS bundle & HTML scraping:
Front-end JS often contains the complete API surface as fetch(...) and axios.get(...) calls. Most production sites also contain hardcoded API base URLs and version numbers.
kali(command="curl -sL https://TARGET/ | grep -oE '/(api|v[0-9])/[a-zA-Z0-9_/-]+' | sort -u")
kali(command="curl -sL https://TARGET/main.js https://TARGET/app.js https://TARGET/bundle.js 2>/dev/null | grep -oE '\"/[a-zA-Z0-9_/-]+\"' | sort -u")
For SPAs: download every JS bundle linked from the HTML and grep for fetch(, axios., XMLHttpRequest, apiBase, endpoint:, url:. JS bundles are the #1 source of undocumented endpoints.
Step 1c — Wordlist-based path fuzzing (kiterunner):
kali(command="kr brute https://TARGET/ -w /usr/share/wordlists/seclists/Discovery/Web-Content/api/api-endpoints.txt -A=apiroutes-220828 -x 10")
kiterunner is purpose-built for API discovery — unlike ffuf/gobuster it sends method+content-type combinations from a route DB, not just GETs.
Fallback if kiterunner unavailable:
scan(tool="ffuf", target="https://TARGET/FUZZ", options={"wordlist": "api-endpoints.txt"})
scan(tool="ffuf", target="https://TARGET/api/v1/FUZZ", options={"wordlist": "api-endpoints.txt"})
scan(tool="ffuf", target="https://TARGET/api/v2/FUZZ", options={"wordlist": "api-endpoints.txt"})
Step 1d — Version drift discovery (API9):
For every endpoint found at /api/v2/foo, also probe /api/v1/foo, /api/v0/foo, /api/foo, /api/internal/foo, /api/staging/foo, /api/test/foo, /api/legacy/foo. Older versions are almost always less hardened — this is the single most productive gap in modern APIs.
http(action="request", url="https://TARGET/api/v1/users/1")
http(action="request", url="https://TARGET/api/v0/users/1")
http(action="request", url="https://TARGET/api/internal/users/1")
Step 1e — GraphQL introspection (MANDATORY — always probe, even when not told):
Always probe these paths unconditionally, regardless of whether GraphQL was mentioned:
http(action="request", url="https://TARGET/graphql", method="POST", body='{"query":"{__schema{queryType{name}}}"}')
http(action="request", url="https://TARGET/api/graphql", method="POST", body='{"query":"{__schema{queryType{name}}}"}')
http(action="request", url="https://TARGET/v1/graphql", method="POST", body='{"query":"{__schema{queryType{name}}}"}')
http(action="request", url="https://TARGET/query", method="POST", body='{"query":"{__schema{queryType{name}}}"}')
If ANY probe returns a valid GraphQL response (even if introspection is disabled — {errors:[{message:"..."}]} is still GraphQL), IMMEDIATELY register the endpoint in the coverage matrix:
report(action="coverage", data={
"type": "endpoint",
"path": "/graphql",
"method": "POST",
"params": [{"name": "query", "type": "body_json", "value_hint": ""}, {"name": "variables", "type": "body_json", "value_hint": ""}],
"discovered_by": "manual_probe",
"auth_context": "none"
})
The coverage system automatically opens an api-security trigger gate when a GraphQL endpoint is registered. This gate blocks scan completion until this skill runs.
If introspection is disabled, use clairvoyance for blind schema recovery:
kali(command="clairvoyance https://TARGET/graphql -o /tmp/schema.json")
Full introspection query:
http(action="request", url="https://TARGET/graphql", method="POST",
headers={"Content-Type": "application/json"},
body='{"query":"query IntrospectionQuery { __schema { queryType { name } mutationType { name } subscriptionType { name } types { ...FullType } directives { name description locations args { ...InputValue } } } } fragment FullType on __Type { kind name description fields(includeDeprecated: true) { name description args { ...InputValue } type { ...TypeRef } isDeprecated deprecationReason } inputFields { ...InputValue } interfaces { ...TypeRef } enumValues(includeDeprecated: true) { name description isDeprecated deprecationReason } possibleTypes { ...TypeRef } } fragment InputValue on __InputValue { name description type { ...TypeRef } defaultValue } fragment TypeRef on __Type { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name } } } } } } } }"}')
If introspection is disabled (returns "GraphQL introspection is not allowed"):
kali(command="clairvoyance https://TARGET/graphql -w /usr/share/wordlists/seclists/Discovery/Web-Content/graphql.txt")
clairvoyance reconstructs the schema from field-suggestion error messages even when introspection is disabled.
Step 1e-ii — Query every field on every type (not just what the UI uses).
The UI is a liar. It filters which fields it renders; the schema doesn't. Once you have the schema, build a query that selects every scalar field on every object type and run it. Look especially for fields named:
flag,secret,password,password_hash,salt,mfa_secret,recovery_token,api_key,tokenis_admin,isAdmin,role,permissions,internal_notes,fraud_score,risk_scoreemail,phone,ssn,address,credit_card,iban(for PII leaks)- Any field beginning with
_orinternal_
Step 1e-iii — Try every argument, even undocumented ones.
GraphQL resolvers often accept arguments the UI never uses:
- Boolean arguments like
is_admin,include_deleted,show_hidden,bypass_filterare frequent backdoors - JSON-string arguments parsed via
json.loads()+filter(**dict)are kwargs-splat NoSQLi — inject{"is_admin": true}or operator injection{"username__ne": null},{"$gt": ""} - Pagination/sort args (
sort=,order_by=,filter_by=) often accept raw SQL or ORM field names
Example of kwargs-splat injection (mongoengine):
query {
users(search: "{\"is_admin\": true}") {
username
email
}
}
The server does User.objects.filter(**json.loads(search)) and returns admin rows. The filter happens at the database level, bypassing any application-level "only show non-admins" logic.
This is the #1 GraphQL exploit pattern — always look for arguments whose values are parsed as structured data (JSON, YAML, query DSL).
Step 1f — gRPC reflection (if gRPC endpoint found):
kali(command="grpcurl -plaintext TARGET:443 list")
kali(command="grpcurl -plaintext TARGET:443 list ")
kali(command="grpcurl -plaintext TARGET:443 describe .")
If reflection is disabled, look for .proto files in JS bundles or GitHub repos. Without protos you cannot meaningfully test gRPC — escalate to source code review (/codebase) if available.
Step 1g — Register everything in the coverage matrix:
report(action="coverage", data={
"type": "endpoint",
"path": "/api/v2/users/{id}",
"method":
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [ShulkwiSEC](https://github.com/ShulkwiSEC)
- **Source:** [ShulkwiSEC/bb-huge](https://github.com/ShulkwiSEC/bb-huge)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.