---
id: 2026-07-gitlab-mcp-account-takeover-cve-cluster
title: "@zereight/mcp-gitlab — the most-used GitLab MCP server (200K+ downloads) shipped an unauthenticated file-read → PAT theft → full account takeover, plus SSRF, DNS-rebinding and path-traversal token-redirect bugs (CVE-2026-61560 et al.)"
date_disclosed: 2026-07-06
last_updated: 2026-09-16
severity: critical
status: patched
ecosystems: [npm, mcp]
tools_affected: [zereight-mcp-gitlab, gitlab-mcp, any-agent-with-the-gitlab-mcp-server-installed]
tags: [cve, mcp, credential-theft, ssrf, dns-rebinding, path-traversal, account-takeover, localhost-not-a-boundary, unauthenticated]
---

## TL;DR
**`@zereight/mcp-gitlab`** (aka `gitlab-mcp`) — the most widely installed GitLab MCP server, **200K+ downloads, ~82K/week, 1.8K+ GitHub stars** — chained two Pluto Security findings into **unauthenticated GitLab account takeover**: in the default Docker/SSE configuration the `/sse` and `/messages` endpoints have **no authentication**, and the `upload_markdown` tool reads **arbitrary files** off the host, so any network-reachable attacker reads `/proc/self/environ`, steals the `GITLAB_PERSONAL_ACCESS_TOKEN`, and owns the GitLab account ([CVE-2026-61560](https://github.com/advisories/GHSA-cv3r-c5h8-f4g5), CVSS 9.8). Three siblings redirect the credential-bearing calls elsewhere: a header-based **SSRF** ([CVE-2026-61559](https://github.com/advisories/GHSA-2h44-8472-frjj), 9.6), a **DNS-rebinding** reach into the local Streamable-HTTP transport ([CVE-2026-61568](https://github.com/advisories/GHSA-vmp7-252j-cwp7), 9.6), and a **`job_id` path traversal** that redirects API calls to any endpoint ([CVE-2026-61462](https://nvd.nist.gov/vuln/detail/CVE-2026-61462), 9.2). Fixed progressively; **upgrade to ≥ 2.1.30**, bind to `127.0.0.1`, and rotate the PAT the server used.

## What happened
Pluto Security (Gil Maman) published ["One Request to Own Every Repo"](https://pluto.security/blog/two-critical-vulnerabilities-gitlab-mcp-account-takeover/) on **2026-07-06**; the CVEs were assigned and the GitHub advisories published across **July–September 2026** (a GHSA/CVE publication date is not a disclosure date — the research is July). This is a textbook "**localhost is not a boundary**" MCP-server failure, the same class already tracked for [Atlassian `mcp-atlassian` MCPwnfluence](2026-05-mcp-stdio-systemic-rce.md), [Bifrost](2026-09-bifrost-mcp-client-registration-unauth-rce.md), and the [agent-framework MCP batch](2026-08-agent-framework-mcp-cve-batch.md).

The cluster, per the vendor advisories and NVD:

- **[CVE-2026-61560](https://github.com/advisories/GHSA-cv3r-c5h8-f4g5) (GHSA-cv3r-c5h8-f4g5, CVSS 9.8, `< 2.1.27`)** — the headliner. With **SSE transport (the default Docker config)** the `/sse` and `/messages` endpoints carry **no authentication middleware**, so any network-reachable caller invokes tools without credentials. The `upload_markdown` tool then takes an **unsanitized `file_path`** (CWE-22) and reads any file the process can — including `/proc/self/environ`, which holds `GITLAB_PERSONAL_ACCESS_TOKEN`. Read the token, and it is full GitLab API access at the operator's privilege. The container runs as **root with no network restriction** by default.
- **[CVE-2026-61559](https://github.com/advisories/GHSA-2h44-8472-frjj) (CVSS 9.6, `< 2.1.27`)** — with `ENABLE_DYNAMIC_API_URL=true`, the server trusts an attacker-supplied **`X-GitLab-API-URL` header** with only syntax validation and no allowlist, so it forwards the victim's PAT / CI job token to any host the attacker names (CWE-918 SSRF).
- **[CVE-2026-61568](https://github.com/advisories/GHSA-vmp7-252j-cwp7) (CVSS 9.6, `< 2.1.30`)** — the Streamable-HTTP MCP endpoint has **no Host/Origin validation and no DNS-rebinding protection**, so a web page the developer visits can rebind to the local listener, initialize an MCP session, and reach GitLab credentials and CI/CD variables.
- **[CVE-2026-61462](https://nvd.nist.gov/vuln/detail/CVE-2026-61462) (VulnCheck CNA, CVSS 9.2, fixed commit `e2a81a0`)** — a **path traversal in the `job_id` parameter** of `build/index.js` (`../../../user`) redirects GitLab API requests to arbitrary endpoints using the operator's PAT (CWE-73).
- **[GHSA-5648-rgj9-v224](https://github.com/zereight/gitlab-mcp/security/advisories/GHSA-5648-rgj9-v224) (CVSS 8.1, `< 2.1.30`, no CVE)** — five more control bypasses, incl. an `execute_graphql` read-only-mode / project-allowlist bypass via comment-stripping that mishandles commas, unauthenticated Streamable-HTTP transport under cookie/OAuth flags, and a syntactic-only-token-validation session-exhaustion DoS.

A GitLab MCP server exists to give a coding agent repo, MR, issue, pipeline, and wiki access — so its single stored PAT is broad, and every bug here converges on the same prize: **that token, and the account behind it**.

## Am I affected?

```bash
# Installed anywhere an agent can load it?
npm ls -g 2>/dev/null | grep -i '@zereight/mcp-gitlab\|gitlab-mcp'
grep -ri 'zereight/mcp-gitlab\|gitlab-mcp' ~/.claude*.json ~/.cursor/ ~/.codex/ ~/.vscode/ 2>/dev/null

# Docker deployment (the default SSE config is the vulnerable one)
docker ps --format '{{.Image}}' | grep -i gitlab-mcp

# Is it network-reachable rather than localhost-only?
ss -tlnp 2>/dev/null | grep -vE '127\.0\.0\.1|::1'
```

If a version **< 2.1.30** ran with SSE/Streamable-HTTP transport reachable from anything but loopback, assume the **`GITLAB_PERSONAL_ACCESS_TOKEN` was read** and the GitLab account was accessible at the token's privilege.

### IOCs

| Type | Value |
|---|---|
| Package | `@zereight/mcp-gitlab` (npm), ~82K downloads/week, 200K+ total |
| CVE / GHSA | `CVE-2026-61560` / `GHSA-cv3r-c5h8-f4g5` (9.8); `CVE-2026-61559` / `GHSA-2h44-8472-frjj` (9.6); `CVE-2026-61568` / `GHSA-vmp7-252j-cwp7` (9.6); `CVE-2026-61462` (9.2); `GHSA-5648-rgj9-v224` (8.1) |
| Affected | `< 2.1.27` (file-read, SSRF), `< 2.1.30` (DNS-rebinding, control bypasses) |
| Fixed | **`2.1.30`** or later (latest `2.1.62` as of 2026-09-15) |
| Vulnerable defaults | SSE transport unauthenticated; Docker container runs as root, no network restriction; `ENABLE_DYNAMIC_API_URL=true` trusts `X-GitLab-API-URL` |
| Prize | `GITLAB_PERSONAL_ACCESS_TOKEN` via `/proc/self/environ` → full GitLab account takeover |

## If you are affected
1. **Upgrade to `@zereight/mcp-gitlab` ≥ 2.1.30** (prefer the latest).
2. **Rotate the GitLab PAT / CI job token** the server used, and audit GitLab access logs for token misuse during the exposure window: [playbooks/if-your-github-pat-leaked.md](../playbooks/if-your-github-pat-leaked.md) (GitLab-equivalent steps apply).
3. **Bind to `127.0.0.1`** or front the server with an authenticating reverse proxy; never expose the SSE/Streamable-HTTP transport on `0.0.0.0`. If you must use `ENABLE_DYNAMIC_API_URL`, set a `GITLAB_ALLOWED_HOSTS` allowlist.
4. If a malicious MCP server or a rebinding page may have driven the agent: [playbooks/if-an-mcp-server-was-malicious.md](../playbooks/if-an-mcp-server-was-malicious.md).

## Prevention
→ [prevention/mcp-hygiene.md](../prevention/mcp-hygiene.md) — an MCP server holding a broad PAT is a credential vault; authenticate every transport, run it unprivileged, and scope its token to least privilege.
→ [prevention/credential-hygiene.md](../prevention/credential-hygiene.md) — a single broad PAT in `env` is one file-read away from account takeover; prefer short-lived, narrowly-scoped tokens.

## Sources
- [Pluto Security — One Request to Own Every Repo: How We Hijacked GitLab Through Its MCP Server](https://pluto.security/blog/two-critical-vulnerabilities-gitlab-mcp-account-takeover/) — fetched 2026-09-16; primary research (Gil Maman, published 2026-07-06): the file-read → PAT chain and the SSRF, disclosure timeline, "200K+ downloads, 1.8K+ stars," fixed 2.1.27.
- [GitHub Advisory Database — GHSA-cv3r-c5h8-f4g5 (CVE-2026-61560)](https://github.com/advisories/GHSA-cv3r-c5h8-f4g5) — fetched 2026-09-16; CVSS 9.8, `< 2.1.27`, `upload_markdown` unauthenticated file read → PAT theft.
- [GitHub Advisory Database — GHSA-2h44-8472-frjj (CVE-2026-61559)](https://github.com/advisories/GHSA-2h44-8472-frjj) — fetched 2026-09-16; CVSS 9.6, header-based SSRF via `X-GitLab-API-URL`.
- [GitHub Advisory Database — GHSA-vmp7-252j-cwp7 (CVE-2026-61568)](https://github.com/advisories/GHSA-vmp7-252j-cwp7) — fetched 2026-09-16; CVSS 9.6, DNS-rebinding into the Streamable-HTTP transport, fixed 2.1.30.
- [GitHub Advisory — GHSA-5648-rgj9-v224](https://github.com/zereight/gitlab-mcp/security/advisories/GHSA-5648-rgj9-v224) — fetched 2026-09-16; CVSS 8.1, five control bypasses, updated 2026-09-15, fixed 2.1.30.
- [NVD — CVE-2026-61462](https://nvd.nist.gov/vuln/detail/CVE-2026-61462) — fetched 2026-09-16 via the NVD API; VulnCheck CNA, CVSS 9.2, `job_id` path traversal in `build/index.js`, fix commit `e2a81a0`.
