Disclaimer
This vulnerability was reported 2 months ago (February 20th) to the mailing list in the Security.md file and as a Github Advisory in this repo. This was tested with version 2.0.1 back then, but I confirmed it still occurs with the current version (2.0.5). Affects all versions since 1.5.0.
The issue was ignored both in the email and in the GitHub Advisory. I let the developers know in the GitHub Advisory on March 30th that by April 20th, I would be posting this publicly since there seems to be no intention to fix it.
For those running it as a Docker image, the workaround is to verify you are not running the docker container image with the port binded to 0.0.0.0. If you run the image like this, you are affected:
docker run -p 6274:6274 mcpjam/mcp-inspector
Vulnerability analysis
Summary
An authorization bypass is possible through the Host header that allows an attacker to acquire a session token. This token can later be used to authenticate against /api/mcp/connect, triggering RCE.
This issue doesn't affect the HOSTED deployments.
This issue doesn't affect deployments through a Docker container whose port is bound only to localhost, since that works as expected. Bound to localhost is the current recommended approach, updated in the README now which was not when I reported this vulnerability
Details
The endpoint api/session-token is secured by only allowing access to localhost or the list of allowed hosts. The way the endpoint retrieves the information on who is reaching out is through the Host header:
|
app.get("/api/session-token", (c) => { |
|
if (HOSTED_MODE) { |
|
return strictModeResponse(c, "/api/session-token"); |
|
} |
|
|
|
const host = c.req.header("Host"); |
|
|
|
if (!isAllowedHost(host, ALLOWED_HOSTS, HOSTED_MODE)) { |
|
appLogger.warn( |
|
`[Security] Token request denied - non-allowed Host: ${host}`, |
|
); |
|
return c.json( |
|
{ error: "Token only available via localhost or allowed hosts" }, |
|
403, |
|
); |
|
} |
|
|
|
return c.json({ token: getSessionToken() }); |
|
}); |
This can be bypassed by an attacker since they can override the provided Host header. This will return a valid token to the attacker.
Similarly, there is another way to acquire the token when production is enabled (this is the case for the Docker image and the public web page app.mcpjam.com):
|
// SECURITY: Only inject token for localhost or allowed hosts (in hosted mode) |
|
// This prevents token leakage when bound to 0.0.0.0 |
|
const host = c.req.header("Host"); |
This allows an attacker to receive the token through a call to a non-existent endpoint.
With this token, they can achieve RCE through /api/mcp/connect in a similar way as in GHSA-232v-j27c-5pp6
PoC
I'm not providing the command for the RCE since is the same as in GHSA-232v-j27c-5pp6, but it can be seen in the images
curl http://IP:6274/api/session-token --header "Host: localhost"
curl http://IP:6274/testest --header "Host: localhost"
Impact
An unauthenticated remote attacker can acquire authentication privileges, giving them access to privileged action and therefore RCE through api/mcp/connect
Disclaimer
This vulnerability was reported 2 months ago (February 20th) to the mailing list in the Security.md file and as a Github Advisory in this repo. This was tested with version 2.0.1 back then, but I confirmed it still occurs with the current version (2.0.5). Affects all versions since 1.5.0.
The issue was ignored both in the email and in the GitHub Advisory. I let the developers know in the GitHub Advisory on March 30th that by April 20th, I would be posting this publicly since there seems to be no intention to fix it.
For those running it as a Docker image, the workaround is to verify you are not running the docker container image with the port binded to 0.0.0.0. If you run the image like this, you are affected:
docker run -p 6274:6274 mcpjam/mcp-inspectorVulnerability analysis
Summary
An authorization bypass is possible through the
Hostheader that allows an attacker to acquire a session token. This token can later be used to authenticate against/api/mcp/connect, triggering RCE.This issue doesn't affect the HOSTED deployments.
This issue doesn't affect deployments through a Docker container whose port is bound only to localhost, since that works as expected. Bound to localhost is the current recommended approach, updated in the README now which was not when I reported this vulnerability
Details
The endpoint
api/session-tokenis secured by only allowing access to localhost or the list of allowed hosts. The way the endpoint retrieves the information on who is reaching out is through theHostheader:inspector/mcpjam-inspector/server/index.ts
Lines 339 to 357 in cb59b71
This can be bypassed by an attacker since they can override the provided
Hostheader. This will return a valid token to the attacker.Similarly, there is another way to acquire the token when
productionis enabled (this is the case for the Docker image and the public web page app.mcpjam.com):inspector/mcpjam-inspector/server/index.ts
Lines 389 to 391 in cb59b71
This allows an attacker to receive the token through a call to a non-existent endpoint.
With this token, they can achieve RCE through
/api/mcp/connectin a similar way as in GHSA-232v-j27c-5pp6PoC
curl http://IP:6274/api/session-token --header "Host: localhost"curl http://IP:6274/testest --header "Host: localhost"Impact
An unauthenticated remote attacker can acquire authentication privileges, giving them access to privileged action and therefore RCE through
api/mcp/connect