Sep 17, 2026 · 5 min read

Upgrading GitLab does not rotate the secrets GitLab was holding

CVE-2026-85706 let an unauthenticated read of the GitLab secrets file, which holds the key that decrypts every CI/CD variable

Article header: Upgrading GitLab does not rotate the secrets GitLab was holding

GitLab released 19.3.2, 19.2.6 and 19.1.8 on September 10, 2026, fixing CVE-2026-85706 — a path traversal in the repository commits API that lets an unauthenticated user read arbitrary files from the server. CISA added it to the Known Exploited Vulnerabilities catalog the next day, September 11, with a remediation due date of September 14. NVD published the CVE record on September 12 — a day after the federal clock had already started. GitLab is the CNA, so the record is vendor-authored: CVSS 10.0, CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N, affecting all versions from 18.7 before 19.1.8, 19.2 before 19.2.6, and 19.3 before 19.3.2.

Timeline: GitLab shipped the fixed releases 19.3.2, 19.2.6 and 19.1.8 on September 10, 2026. CISA added CVE-2026-85706 to the KEV catalog on September 11 with a remediation due date of September 14 — a three-day deadline. NVD published the CVE record on September 12, one day after the KEV clock started and two days before it expired.

The vector string already says this is not a disclosure bug

GitLab describes the defect as "improper path confinement and missing authentication enforcement in the repository commits API." The three threat detections shipped with the release name the attack more precisely than the advisory does: "GitLab LFI attempt reading gitlab.yml", "GitLab LFI via metadata.path parameter", and "GitLab LFI file path attempt."

Then read the vector. Confidentiality is High, which a file read earns. Integrity is also High and Scope is Changed, on a vulnerability whose only capability is reading. That combination is CVSS's way of saying the files in reach are keys to something else.

On a GitLab server, they are. GitLab's backup documentation names the files — /etc/gitlab/gitlab-secrets.json on Linux package installs, /home/git/gitlab/config/secrets.yml on self-compiled ones — while explaining why the backup Rake task deliberately excludes them: "the secrets file contains your database encryption key. If you lose it, then the GitLab application will not be able to decrypt any encrypted values in the database." The same page enumerates those encrypted values: two-factor authentication secrets and the CI/CD secure variables.

The CI/CD variables documentation closes the loop in one sentence: "Variable values are encrypted using aes-256-cbc and stored in the database. This data can be read and decrypted with a valid secrets file." An attacker who read that file has a valid secrets file. What it decrypts is every CI/CD variable on the instance — every cloud access key, registry password and deploy token any pipeline on that box has ever needed, across every project.

Rotation is a separate work item from the upgrade

Upgrading closes the read. It does nothing to credentials already taken, and the version number afterward answers no question about what happened before it. GitLab published detections rather than a compromise marker, so unlike a RouterOS upgrade that ships its own compromise check, the restart leaves you as uncertain as you were.

So run two tickets. One is "upgrade to 19.1.8, 19.2.6 or 19.3.2." The other is "rotate what the instance was holding," and it belongs to whoever owns the cloud accounts, not to whoever owns the GitLab server. CISA separates them too: the catalog entry for this CVE carries a forensic-triage requirement and points at BOD 26-04's forensics guidance rather than only at the vendor patch — consistent with a directive that scores your exposure rather than the CVSS.

Rotation has an awkward shape worth knowing before you plan it, because you cannot simply re-key. GitLab's maintenance documentation permits rotating the secrets file, then carves out the one secret that matters: "Do not rotate the gitlab_rails secrets because it contains the database encryption keys. If this secret is rotated, you see the same behavior as when the secrets file is lost." Re-keying makes every stored variable unreadable to GitLab as well as to the attacker. Rotation therefore means changing the credential values at their sources — new keys at the cloud provider, new registry passwords, new deploy tokens — and re-entering them.

That is the cost, and why it gets deferred: not a maintenance window but a credential inventory nobody has, touching every project whose pipelines break the moment a stale value is replaced badly. Sequence by blast radius: long-lived static cloud keys first, then deploy keys and registry credentials, then the per-project variables that only reach a staging namespace. Runner tokens belong on the list as well — current GitLab registers runners with runner authentication tokens rather than the legacy registration tokens, and rotating one means re-registering the runner.

If you cannot rotate everything, the durable fix is to stop storing the credentials at all. GitLab mints short-lived ID tokens that cloud providers accept over OIDC, which turns the variable holding a permanent access key into a token scoped to one job. That is a quarter's work rather than a week's, and it is the version where the next arbitrary-read bug on the instance is not also a cloud account compromise.

What to check this week

  • The running version of every self-managed instance, including one a product team stood up itself: sudo gitlab-rake gitlab:env:info. Fixed releases are 19.1.8, 19.2.6 and 19.3.2; anything from 18.7 up is in range.
  • Whether that instance answered from the public internet between September 10 and the day you upgraded. That window, not the CVE, decides whether rotation is optional.
  • /var/log/gitlab/gitlab-rails/api_json.log on package installs, for commits-API requests carrying .. or %2e%2e in a path parameter. The vendor detections keyed on metadata.path and on reads of gitlab.yml.
  • How many CI/CD variables that encryption key could decrypt: GET /api/v4/admin/ci/variables for the instance level, plus the group and project equivalents. The count is the size of the rotation job.
  • Which of those values are long-lived static cloud keys rather than short-lived tokens. Those rotate first, and are the ones worth replacing with OIDC.

North InfoSec runs AI-assisted penetration testing and security assessments, including the kind of credential-chain testing described above. northinfosec.com

← All articles