Summary
Unauthenticated clients can occupy every session slot by sending initialize requests with syntactically valid but invalid GitLab tokens, denying service to legitimate clients.
Details
At index.ts:12387, validateToken only verifies that the supplied token is at least 20 characters long and matches the expected character set. It does not validate the token against GitLab before a session is created.
parseAuthHeaders therefore returns AuthData for any syntactically valid token.
New sessions are then admitted based only on the global capacity check at :12938, with MAX_SESSIONS defaulting to 1000. The per-session rate limiter only applies after a sessionId already exists.
As a result, an unauthenticated attacker can repeatedly send initialize requests using arbitrary garbage tokens until all session slots are occupied for SESSION_TIMEOUT_SECONDS, which defaults to 3600 seconds.
Legitimate clients then receive HTTP 503 responses.
Proof of concept
In REMOTE_AUTHORIZATION mode:
for i in $(seq 1 1000); do
curl -s -o /dev/null http://127.0.0.1:3002/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'Private-Token: aaaaaaaaaaaaaaaaaaaaaaaa' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"x","version":"0"}}}'
done
A subsequent legitimate initialize request returns:
503 "Maximum 1000 concurrent sessions allowed"
Remediation
Already shipped in 2.1.30:
- Validate tokens upstream before allocating a session.
- Rate-limit new session creation per IP.
- Reduce the idle session timeout.
Summary
Unauthenticated clients can occupy every session slot by sending
initializerequests with syntactically valid but invalid GitLab tokens, denying service to legitimate clients.Details
At
index.ts:12387,validateTokenonly verifies that the supplied token is at least 20 characters long and matches the expected character set. It does not validate the token against GitLab before a session is created.parseAuthHeaderstherefore returnsAuthDatafor any syntactically valid token.New sessions are then admitted based only on the global capacity check at
:12938, withMAX_SESSIONSdefaulting to 1000. The per-session rate limiter only applies after asessionIdalready exists.As a result, an unauthenticated attacker can repeatedly send
initializerequests using arbitrary garbage tokens until all session slots are occupied forSESSION_TIMEOUT_SECONDS, which defaults to 3600 seconds.Legitimate clients then receive HTTP 503 responses.
Proof of concept
In
REMOTE_AUTHORIZATIONmode:A subsequent legitimate
initializerequest returns:503 "Maximum 1000 concurrent sessions allowed"Remediation
Already shipped in 2.1.30: