# @zereight/mcp-gitlab: Unauthenticated arbitrary file read via `upload_markdown` enables PAT exfiltration and full account takeover

Unauthenticated SSE transport plus unsanitized file_path in upload_markdown lets attackers read /proc/self/environ, steal the GitLab PAT, and take over the account.

- Payout: Research
- Severity: critical
- Program: mcp-gitlab
- Found by: gil-maman-p, hodaya-prz
- Disclosed: 2026-09-16
- CVE: CVE-2026-61560
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-cv3r-c5h8-f4g5
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-cv3r-c5h8-f4g5
- Tags: area:ai domain:ai domain:web impact:ato impact:data-leak kind:research platform:ghsa sev:critical tech:docker tech:nodejs technique:chained technique:source-review vuln:auth-bypass vuln:path-traversal vuln:secrets-leak

**Target:** MCP server SSE transport / upload_markdown tool
**Root cause:** SSE mode exposes all MCP tools with no auth middleware, and upload_markdown passes user-supplied file_path directly to fs.readFileSync with no path validation or sandboxing.
**Steps:**
1. Connect to unauthenticated SSE endpoint and capture session ID: `curl -s -N http://<HOST>:3002/sse | head -1 | grep -oP 'sessionId=\K[^&\s]+'`
2. Optionally enumerate writable projects via `tools/call` with `list_projects` and `{"owned": true}`
3. Invoke `upload_markdown` with `project_id` and `file_path: "/proc/self/environ"` via POST to `/messages?sessionId=$SESSION_ID`
4. Response returns a GitLab upload URL like `/uploads/abc123def456/environ`
5. Fetch the uploaded file from GitLab to read NUL-separated env vars including `GITLAB_PERSONAL_ACCESS_TOKEN=glpat-...`
6. Use stolen PAT: `curl -H "Private-Token: glpat-..." https://gitlab.example.com/api/v4/user`
**How found:** Code review of the SSE transport handlers found no auth middleware on /sse and /messages, and review of the upload_markdown tool found file_path passed unsanitized to fs.readFileSync; Dockerfile lacking USER and compose binding 0.0.0.0 amplified reachability.
**Exploitation:** Attacker with network reach to port 3002 opens the unauthenticated SSE stream, invokes upload_markdown pointing at /proc/self/environ, which the server reads and uploads to a GitLab project the PAT can write to; attacker then downloads the upload URL and extracts the plaintext PAT for full API access.
**Escalation:** Docker runs as root with no USER directive and binds 0.0.0.0:3002, so /etc/shadow and app source are also readable; stolen PAT grants repos, CI/CD secrets, deploy keys, and admin functions if the token owner is admin.
**Insight:** MCP servers in SSE mode often ship without auth and expose file-reading tools by default; check whether the toolset is enabled by default and whether the container runs as root, then target /proc/self/environ to lift the server's own API token rather than hunting for a separate credential leak.

