GitLab CVE-2026-85706: Unauthenticated File Read on the Commits API, CVSS 10.0, Already in CISA KEV
Introduction
GitLab published critical patches on September 10 for CVE-2026-85706, a CVSS 10.0 issue in the repository commits API caused by improper path confinement and missing authentication enforcement. Under the conditions GitLab described, an unauthenticated caller can read arbitrary files the GitLab server process can reach. CISA added the CVE to KEV on September 11, set a September 14 federal due date, and marked forensic triage required. watchTowr reported probes against its honeypot network from 06:00 UTC on September 11. Public proof-of-concept repositories appeared within days. If your self-managed instance was on the internet while unpatched, assume secrets were readable.
What Happened
The affected surface is GitLab Community Edition and Enterprise Edition, all versions from 18.7 before 19.1.8, 19.2 before 19.2.6, and 19.3 before 19.3.2. GitLab.com was already patched. GitLab Dedicated customers do not need to act. Every self-managed install type is in scope: Omnibus, source, and Helm.
GitLab's own wording is "improper path confinement," not a classic ../ traversal. Multiple independent write-ups (watchTowr, Rapid7, Censys, Black Hills InfoSec) converge on the same operational point: an unauthenticated request against the repository commits API can return file contents the Rails process is allowed to read. Practical precondition reported by researchers: the instance needs at least one public project. Censys notes public PoCs demonstrate reading /etc/gitlab/gitlab-secrets.json — the file that holds keys GitLab uses to encrypt CI/CD variables, deploy keys, and integration tokens.
The same patch release also ships CVE-2026-87719, a CVSS 9.9 insecure-deserialization bug in GitLab EE GraphQL subscriptions that can expose Advanced Search configuration and credentials to an authenticated Duo Chat user. Only CVE-2026-85706 is confirmed exploited in the wild as of this writing.
Single-node Omnibus upgrades will take downtime because this patch includes database migrations. Multi-node can follow GitLab's zero-downtime procedure. Only 19.3.2 includes post-deploy migrations.
Why It Matters
GitLab is not "just source control." A self-managed instance is usually the secret store for the rest of the SDLC: CI variables, deploy tokens, Kubernetes agent credentials, package-registry passwords, and cloud keys used by pipelines. An unauthenticated file read against that host is a supply-chain incident waiting on the next pipeline run.
CISA putting a three-day clock and forensic-triage flag on it is the tell. Patching stops the next read. It does not tell you whether /etc/gitlab, /var/opt/gitlab, or application secrets already walked out. Rotating blindly — especially regenerating gitlab-secrets.json without GitLab's documented procedure — can make encrypted database values unreadable. Hunt first, then rotate with a plan.
Who Is Affected
- Self-managed GitLab CE/EE 18.7 through 19.1.7, 19.2.0 through 19.2.5, and 19.3.0 through 19.3.1
- Omnibus, source installs, and Helm/Kubernetes deployments of those versions
- Highest risk: internet-exposed instances with at least one public project
- GitLab.com and GitLab Dedicated — no customer action for this CVE
- Indirectly, every cloud account, registry, and cluster whose credentials lived as CI variables or in files the
git/gitlab-railsuser can read
How to Protect Yourself
1. Record the running version, then upgrade to a fixed build on your train.
sudo gitlab-rake gitlab:env:info
cat /opt/gitlab/embedded/service/gitlab-rails/VERSION
# Debian/Ubuntu Omnibus examples — pick CE or EE to match what you run
sudo apt-get update
sudo apt-get install gitlab-ee=19.3.2-ee.0
# or: sudo apt-get install gitlab-ce=19.3.2-ce.0
# 19.2 train → 19.2.6; 19.1 train → 19.1.8
sudo gitlab-ctl reconfigure
sudo gitlab-ctl status
sudo gitlab-rake gitlab:env:info
RHEL/Helm operators should follow GitLab's upgrade docs for the same version numbers. Schedule downtime on single-node boxes.
2. Hunt access, proxy, and WAF logs from 10 September onward. Researchers hunting this bug look for HTTP POST to /api/v4/projects/*/repository/commits (with or without a trailing slash), including URL-encoded variants of that path, carrying a client-supplied file.path or file.Path. Black Hills InfoSec's lab note matters for detections: the working request may use an absolute path and may not contain ../, so a "traversal string" rule can miss it. Match the parameter name and the commits API path, not a ../ regex.
# nginx / GitLab workhorse / WAF
zgrep -E 'POST .*/api/v4/projects/.*/repository/commits' /var/log/nginx/access.log* /var/log/gitlab/nginx/gitlab_access.log*
# Rails production log (path varies by install)
sudo grep -R --line-number -E 'repository/commits|file\.path|file\.Path' \
/var/log/gitlab/gitlab-rails/production_json.log* | tail -n 200
Anything from an unexpected client, especially unauthenticated, is an incident. Preserve logs before they rotate.
3. Assume readable secrets are burned if the instance was exposed while vulnerable. Do not blindly regenerate /etc/gitlab/gitlab-secrets.json. Follow GitLab's secret-rotation and incident-response docs so encrypted CI variables remain decryptable. At minimum, rotate:
- Personal access tokens, project/group access tokens, and deploy tokens
- CI/CD variables that hold cloud keys, registry passwords, or webhook secrets
- Kubernetes agent tokens and cluster integration credentials
- SMTP, object-storage, and LDAP bind passwords stored in
gitlab.rb/gitlab.yml
# EE: credentials inventory is the right starting list
# CE: export tokens from the admin UI / API, then revoke and reissue
sudo gitlab-rake gitlab:check
4. Shrink the unauthenticated surface. Make projects private unless they must be public. Put GitLab admin and API behind SSO and a WAF. Do not expose the instance to the internet without an allow-list. Pin a patch cadence to GitLab's monthly critical releases — this one had public PoCs inside 48 hours.