API09: Improper Inventory Management - Attack Vectors
Table of Contents
Understanding the Attack Surface
⚠ EDUCATIONAL PURPOSE ONLY. These techniques are described so defenders can find and close gaps before attackers do. Only test systems you are explicitly authorized to test.
Attacking improper inventory management is fundamentally an exercise in discovery. The attacker's goal is to find the asset the defender forgot: an old version, a stale host, a debug route, or a partner endpoint that never made it into anyone's threat model. Because these assets are unmonitored by definition, exploitation is often quiet and can persist for a long time.
The attacker rarely needs a novel exploit. They need the weakest reachable copy of your functionality — and inventory gaps guarantee that a weaker copy usually exists.
Core Attack Flow
1. Map the surface
|
Enumerate subdomains, hosts, versions, and routes
v
2. Find the weak variant
|
Compare auth/rate-limit behavior across versions & hosts
v
3. Exploit the gap
|
Use the version/endpoint with missing or weaker controls
v
4. Persist quietly
|
Operate through an unmonitored path to avoid detection
Attack Patterns
1. Enumerating Deprecated API Versions
The attacker takes a known current endpoint and simply decrements the version, looking for an older variant that still answers — often with weaker or absent authorization.
GET /api/v3/users/1001 -> 401 Unauthorized (token + scope required)
GET /api/v2/users/1001 -> 401 Unauthorized
GET /api/v1/users/1001 -> 200 OK (legacy: returns full record!)
The /v1 handler predates the current authorization model, so it happily returns data the newer versions protect. This is the single most common inventory-management attack.
2. Subdomain Enumeration to Find Shadow Hosts
Old staging, dev, and demo hosts frequently remain in DNS and stay internet-reachable. Certificate-transparency logs and brute-force resolvers reveal them quickly.
# Discover hosts from certificate transparency
curl -s "https://crt.sh/?q=%25.example.com&output=json" | jq -r '.[].name_value' | sort -u
# Result includes forgotten hosts:
# dev-api.example.com
# staging.example.com
# api-old.example.com
# internal-tools.example.com
GET https://dev-api.example.com/api/users -> 200 OK (debug mode, real data)
3. Non-Production Environments with Production Data
Staging often mirrors production data but ships with verbose errors, default credentials, or disabled security features.
GET https://staging.example.com/api/v3/orders
HTTP/1.1 500 Internal Server Error
{
"error": "psycopg2.OperationalError",
"stack": "at db.connect(host=staging-db.internal:5432, user=admin, password=changeme) ...",
"debug": true
}
The leaked stack trace exposes internal hostnames and credentials that pivot the attacker deeper.
4. Missing Authentication on Old Versions
When authentication is added in a later version, the older one may never have required it at all.
# Current version enforces a bearer token
GET /api/v2/account/balance
Authorization: Bearer eyJ... -> 200 OK
# Legacy version was written before auth existed
GET /api/v1/account/balance -> 200 OK (no Authorization header needed)
5. Undocumented Debug & Admin Endpoints
Diagnostic and administrative routes left in production are discovered by fuzzing common paths.
GET /api/_debug -> 200 OK (dumps environment variables)
GET /api/admin/users -> 200 OK (no auth, lists all users)
GET /internal/backup -> 200 OK (downloads a database dump)
GET /test/reset-db -> 200 OK (destructive dev utility, still live)
6. Framework Diagnostic Endpoints (Actuator, Docs, Introspection)
Framework defaults expose powerful endpoints if not explicitly disabled.
# Spring Boot Actuator left open
GET /actuator/env -> configuration incl. secrets
GET /actuator/heapdump -> full memory dump (tokens, sessions)
GET /actuator/mappings -> a map of every route in the app
# Interactive docs / schema in production
GET /openapi.json -> full machine-readable API spec
GET /swagger-ui/index.html -> live, clickable API explorer
POST /graphql {"query":"{__schema{types{name}}}"} -> introspection
These not only expose data directly, they hand the attacker a complete map of the rest of the API.
7. Interactive API Docs as a Discovery Oracle
A leaked OpenAPI/Swagger document is a gift: it enumerates every path, parameter, and expected payload, turning blind fuzzing into targeted requests.
# Pull the spec, then list every path it declares
curl -s https://api.example.com/openapi.json | jq -r '.paths | keys[]'
/api/v3/users
/api/v3/users/{id}
/api/v1/legacy/export <-- undocumented elsewhere, but present in the spec
/api/internal/impersonate <-- an admin route the spec forgot to hide
8. Differing Security Controls Across Versions
Even when every version requires auth, the quality of the controls can differ. Attackers probe for the version with the weakest rate limiting, weakest object-level checks, or most generous scopes.
# v3 rate-limits login to 5/min; v1 never got rate limiting
POST /api/v1/login (unlimited attempts -> credential stuffing / brute force)
# v3 checks object ownership; v1 trusts the id parameter (BOLA revived)
GET /api/v1/invoices/99999 -> 200 OK (another tenant's invoice)
9. Retired-But-Reachable Services
A service "turned off" in the product may still be running on its original host or behind a load balancer rule nobody removed.
GET https://legacy-payments.example.com/api/charge -> 200 OK
# The team migrated to a new processor months ago, but the old
# service still accepts requests and still holds live API keys.
10. Mobile / Client-Specific Shadow APIs
Mobile and single-page apps often talk to endpoints that the web API's protections never cover. Inspecting app traffic reveals them.
# Intercept mobile traffic, observe a private base URL:
POST https://mobile-api.example.com/v2/profile/update
# The mobile endpoint skips the WAF and the gateway's rate limits
# that guard api.example.com, and trusts a weaker client secret.
11. Parameter- and Header-Based Version Selection
Not all versioning lives in the path. Header- or query-based version routing can silently expose old code paths.
# Same path, older behavior selected by a header
GET /api/users/1001
Accept: application/vnd.example.v1+json -> legacy handler, weaker checks
# Or via query string
GET /api/users/1001?api-version=1.0 -> routes to deprecated logic
12. Third-Party Data-Flow Blind Spots
Functionality exposed to (or by) a partner is frequently outside the data owner's monitoring. An attacker who cannot breach the front door probes the integration instead.
# A partner-facing endpoint returns sensitive data with weak identifiers
POST https://partner-api.example.com/lookup
{ "name": "Jane Doe", "dob": "1990-01-01", "zip": "10001" }
-> 200 OK { "credit_score": 742, "ssn_last4": "1234" }
13. Stale Documentation / Changelogs Leaking Removed Routes
Old developer portals, cached docs, wikis, and changelogs describe endpoints that were "removed" but not actually taken offline.
# A cached changelog mentions:
# "Deprecated /api/v1/export-all in favor of /api/v3/export"
# The attacker tries the old path directly:
GET /api/v1/export-all -> 200 OK (still exports everything)
14. JavaScript Bundle & Source-Map Mining
Front-end bundles and leaked source maps reveal API base URLs, internal endpoints, and feature-flagged routes never meant for production exposure.
# Extract endpoint strings from a production bundle
curl -s https://example.com/static/app.min.js | grep -oE '/api/[a-zA-Z0-9/_-]+'
/api/v3/users
/api/beta/experimental-search <-- feature-flagged, but reachable
/api/internal/feature-flags <-- internal config endpoint
Chaining Inventory Gaps
Individually, each gap is a foothold. Chained together, they become a full compromise:
1. crt.sh reveals dev-api.example.com (stale host)
v
2. /openapi.json on that host leaks every route (docs disclosure)
v
3. /api/v1/login has no rate limit (control drift) -> credential stuffing
v
4. /actuator/env exposes a database password (framework default)
v
5. Attacker pivots to internal systems using leaked credentials
Why These Attacks Succeed and Persist
- No monitoring on forgotten assets: The endpoint that is not in the inventory is also not in the SIEM, so abuse generates no alerts.
- Controls never applied uniformly: Security improvements land on the "current" surface; old copies keep the original weaknesses.
- Discovery is cheap: CT logs, wordlists, and app traffic inspection require little skill and no access.
- Data parity, control disparity: Stale and non-prod hosts often carry real data behind weaker walls.
Key Takeaways
- Version enumeration is the flagship attack — decrement the version and look for the copy that still answers.
- Discovery, not exploitation, is the hard part for defenders — attackers automate it trivially.
- Documentation and framework defaults are discovery oracles — an exposed spec maps your whole API.
- Shadow, mobile, and partner APIs bypass central defenses by design.
- Non-production hosts are production targets when reachable.
- Gaps chain — one stale host can unravel an entire environment.
Next Steps
- Prevention Guide: Build inventory, governance, and monitoring defenses
- Code Examples: See secure, inventoried implementations
- Hands-On Lab: Practice discovering and retiring unmanaged endpoints