# Meta Ads MCP: Unauthenticated HTTP MCP Tool Execution Leaks Operator Meta Access Token

Meta Ads MCP's auth middleware never returns 401, so unauthenticated callers run tools as the operator and leak META_ACCESS_TOKEN via Graph API error request_url.

- Payout: Research
- Severity: critical
- Program: Meta
- Found by: 232-323
- Disclosed: 2026-06-11
- CVE: CVE-2026-48039
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-9gw6-46qc-99vr
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-9gw6-46qc-99vr
- Tags: area:ai domain:ai domain:api impact:ato impact:data-leak kind:research platform:ghsa sev:critical tech:llm tech:python technique:source-review vuln:auth-bypass vuln:info-disclosure vuln:secrets-leak

**Target:** Streamable HTTP MCP server endpoint (/mcp) with Meta Graph API backend
**Root cause:** AuthInjectionMiddleware.dispatch() calls call_next() even with no auth headers, so tool handlers fall back to the META_ACCESS_TOKEN env var; make_api_request() puts the token in the URL query string and error serialization echoes the full request_url back.
**Steps:**
1. Deploy server with `META_ACCESS_TOKEN` set, exposing port 8080.
2. Send `POST /mcp` with no auth headers and body `{"jsonrpc":"2.0","method":"tools/call","id":2,"params":{"name":"get_ad_accounts","arguments":{"limit":1}}}`.
3. Server returns HTTP 200 and executes the tool using the env token.
4. Trigger a Graph API error; response `full_response.request_url` contains `access_token=...` verbatim.
5. Extract the operator token and use it directly against graph.facebook.com.
**How found:** Code review of the auth middleware: noticed the missing-token branch only logs a warning and still calls call_next, then traced the env-var fallback into make_api_request where the token is appended to query params and reflected in error payloads.
**Exploitation:** Any network-reachable attacker POSTs a JSON-RPC tools/call to /mcp without credentials; the middleware forwards it, the handler uses the operator's env token, and a Graph API error path returns the token in request_url inside a 200 OK response. Attacker then reuses the long-lived token against the Meta Graph API directly.
**Escalation:** Unauthenticated tool execution alone allows read/write on connected ad accounts and quota consumption; the error-path leak escalates to full out-of-band Graph API access with the operator's long-lived token.
**Insight:** In MCP/LLM-tool servers, check whether the auth middleware's 'no credentials' branch actually short-circuits or merely logs; then follow the env-var fallback into any HTTP client that puts secrets in query strings, since error serializers often echo the full request URL.

## Proof of concept (copied exactly from the original report)

**Unauthenticated POST /mcp invoking get_ad_accounts**

```
POST /mcp HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/json
Accept: application/json, text/event-stream

{"jsonrpc":"2.0","method":"tools/call","id":2,"params":{"name":"get_ad_accounts","arguments":{"limit":1}}}
```

**Docker run and poc.py reproduction command**

```
docker run --rm -p 127.0.0.1:8080:8080 -e META_ACCESS_TOKEN=FAKE_TOKEN_FOR_POC_DEMO_123456789 meta-ads-mcp-vuln001 &
python3 poc.py
```

