Skip to content
Security
Skill

/hunt-shadow-api

Hunt shadow / zombie / undocumented API surface (OWASP API9 Improper Inventory Management) — enumerate the full API version history (v1/v2/beta/legacy paths, header- and subdomain-based versioning), pull and diff every reachable OpenAPI/Swagger spec (including ones only findable

From plugin
claude-bughunter
3.3k82 skills15 commands
Install
$ npx -y skills add elementalsouls/Claude-BugHunter --skill hunt-shadow-api --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/hunt-shadow-api

Context preview

The summary Claude sees to decide when to auto-load this skill.

Hunt shadow / zombie / undocumented API surface (OWASP API9 Improper Inventory Management) — enumerate the full API version history (v1/v2/beta/legacy paths, header- and subdomain-based versioning), pull and diff every reachable OpenAPI/Swagger spec (including ones only findable

SKILL.md

hunt-shadow-api.SKILL.md
name: hunt-shadow-api
description: "Hunt shadow / zombie / undocumented API surface (OWASP API9 Improper Inventory Management) — enumerate the full API version history (v1/v2/beta/legacy paths, header- and subdomain-based versioning), pull and diff every reachable OpenAPI/Swagger spec (including ones only findable via the Wayback Machine), and behaviorally diff old vs. current versions for auth/rate-limit/validation regressions rather than just response-shape differences. Distinct from hunt-api-misconfig, which owns exploitation once you have a spec or endpoint (mass assignment, JWT, OData, Swagger-chain attacks); distinct from hunt-subdomain, which owns host-level discovery. This skill owns the version-inventory and behavioral-diff workflow itself. Use when the target has versioned API paths, multiple specs, a changelog referencing deprecated endpoints, or a mobile app whose hardcoded backend calls look older than the current web app's."
sources: owasp_api_top10_2023, portswigger_research, public_research
report_count: 0

OWASP API9 — Improper Inventory Management (Shadow / Zombie APIs)

As an API evolves, old versions and internal/staging routes routinely stay reachable without receiving the same security fixes as the current version — because nobody tracks that they still exist. The bug is rarely in one endpoint; it's in the **delta** between what an old version enforces and what the current version enforces on the same operation.

When to use

Trigger when:

  • Versioned paths are visible (`/v1/`, `/v2/`, `/api/2023-01-01/`) or `Accept`/`X-API-Version`

headers are in play.

  • A changelog, release notes, or deprecation notice references removed/old API behavior.
  • A mobile APK/IPA (via `apk-redteam-pipeline` / `ios-redteam-pipeline`) hardcodes endpoints

that look like an older backend version than the current web app calls.

  • Multiple OpenAPI/Swagger specs are discoverable, or `info.version` in one spec implies others

exist.

DO NOT use for single-version APIs with no version history — there's nothing to diff; go straight to `hunt-api-misconfig` for direct exploitation of the one surface that exists.

---

Stage 1 — Enumerate the Full Version Surface

# Path-based versioning
for v in v1 v2 v3 v4 beta alpha internal legacy old 2022-01-01 2023-01-01 2024-01-01; do
  curl -s -o /dev/null -w "%{http_code} /api/$v/\n" "https://$TARGET/api/$v/"
done

# Header-based versioning
curl -s -H "X-API-Version: 1" https://$TARGET/api/users
curl -s -H "Accept: application/vnd.company.v1+json" https://$TARGET/api/users

# Subdomain-based versioning
for sub in api api-v1 api-v2 apiv1 apiv2 legacy-api old-api internal-api staging-api; do
  curl -s -o /dev/null -w "%{http_code} $sub\n" "https://$sub.$TARGET/"
done

A `200`/`401`/`403` on an old version path (anything but `404`/connection-refused) means the version is still live and worth carrying into Stage 3, even if it demands auth.

---

Stage 2 — Pull Every Reachable Spec, Not Just the Linked One

for path in openapi.json swagger.json v1/swagger.json v2/swagger.json v3/api-docs \
            api-docs.json swagger/v1/swagger.json .well-known/openapi.json; do
  curl -s -o /dev/null -w "%{http_code} /$path\n" "https://$TARGET/$path"
done

# Wayback Machine — a DEPRECATED version's spec often stays indexed after the live link is removed
curl -s "http://web.archive.org/cdx/search/cdx?url=$TARGET/*swagger*&output=json&collapse=urlkey"
curl -s "http://web.archive.org/cdx/search/cdx?url=$TARGET/*openapi*&output=json&collapse=urlkey"

When more than one spec resolves (a current one and an archived/old one), diff the endpoint inventories directly:

jq -r '.paths | keys[]' v1-swagger.json | sort > /tmp/v1_paths.txt
jq -r '.paths | keys[]' v2-swagger.json | sort > /tmp/v2_paths.txt
comm -23 /tmp/v1_paths.txt /tmp/v2_paths.txt   # in v1 only — candidates for "still live but forgotten"

For every path in that diff, confirm it's still reachable against the v1 base URL. A route documented only in the old spec that still returns something other than `404` is a zombie- endpoint candidate — carry it into Stage 3.

---

Stage 3 — Behavioral Diff Between Old and Current Version

For each operation that exists in **both** versions, compare security-relevant behavior, not response shape. Response shape differences are Informational; behavioral security regressions are the finding.

  • **Auth strength.** Does the old version accept no token, an expired token, or a lower-

privilege token that the current version rejects?

  curl -s -H "Authorization: Bearer $EXPIRED_TOKEN" https://$TARGET/api/v1/users/me -w '\n%{http_code}\n'
  curl -s -H "Authorization: Bearer $EXPIRED_TOKEN" https://$TARGET/api/v2/users/me -w '\n%{http_code}\n'
  • **Rate limiting.** Burst the same number of requests against both versions' equivalent

endpoint; a missing `429` on the old version means rate-limiting was added later and never backported.

  • **Input validation.** Send the identical injection/oversized/malformed payload to both; the

old version accepting what the new one rejects means hardening happened forward-only — chain into whichever injection class the payload targets (`hunt-sqli`, `hunt-idor`, etc.).

  • **Field exposure.** Does the old version's response body include fields — internal IDs, other

users' data, internal notes, PII — that the current version has since redacted?

---

Stage 4 — Deprecated / Internal Routes Never Referenced by the Current UI

  • Grep JS bundles for API calls no visible UI flow triggers (`/internal/`, `/admin/`, `/debug/`,

`/_internal/`, `/test/`, `/staging/`) — reuse `hunt-source-leak`'s JS-bundle grep patterns for this specifically.

  • Check `robots.txt` / `sitemap.xml` for disallowed API paths — a self-inflicted disclosure.
  • Mobile-app endpoint inventories (via `apk-redteam-pipeline` / `ios-redteam-pipeline`) very

often reference an older backend version than the curre

Read more
Ships withclaude-bughunter

A self-contained Claude skill bundle for bug hunting and external red-team work · 82 skills · 15 slash commands · 681 disclosed-report patterns across 24 core vulnerability classes · enterprise identity + infrastructure attack matrices · engagement-folder

Get the whole plugin

Other skills on claude-bughunter.