stable Gitlab Vulnerability · Research

GitLab Unauthenticated Arbitrary File Read Exposes Critical Secrets: Full Blast Radius and Incident Recovery Runbook

Cybersecurity dossier diagram illustrating the GitLab unauthenticated arbitrary file read vulnerability, detailing path traversal exploitation, key exfiltration from gitlab-secrets.json, and the impact of forged administrative session tokens.
AK

Threat intelligence editor · Published Sep 12, 2026 · Updated Oct 7, 2026, 8:05 PM EDT

Critical GitLab arbitrary file read flaw leaks server secrets. Learn how to hunt logs, stop admin session forgery, and execute an incident recovery runbook.

CVE-2026-85706 is a CVSS 10.0 unauthenticated arbitrary file read in the repository commits API of self-managed GitLab Community Edition (CE) and Enterprise Edition (EE), versions 18.7 through 19.3. CISA added it to the Known Exploited Vulnerabilities (KEV) catalog on 11 September 2026, a day after GitLab shipped the first fixes. GitLab.com and GitLab Dedicated are already patched. On a vulnerable self-managed server an attacker needs no account to read files the GitLab application can read, and the files that matter most are instance secrets such as gitlab.yml and the keys in gitlab-secrets.json. Stolen keys give an attacker the means for session forgery, database decryption and CI/CD pipeline hijacking, which is why upgrading alone does not close the incident. Arbitrary file-read primitives hit a different vendor's stack in Microsoft Defender's ShieldCrash zero-day exposing arbitrary file read.

Correction (7 October 2026): patch versions and flaw details

Correction (7 October 2026): the version of this article published on 12 September gave a patch table for GitLab 16.11 and 17.0–17.3 and install commands pinned to 17.3.2. None of those are the fix for CVE-2026-85706, and the article did not name the flaw as the exploited one. The fixed versions are listed below. The old table and commands have been removed, and the flaw-mechanics section has been rewritten to what GitLab has published.

  • What it is: per GitLab's patch release notice, "an unauthenticated user could have read arbitrary files from the GitLab server due to improper path confinement and missing authentication enforcement in the repository commits API." CVSS 10.0 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N).
  • Fixed versions (GitLab CE and EE): 19.3.2, 19.2.6, 19.1.8, 19.0.9 and 18.11.12. Affected range per GitLab: 18.7 through 19.3 before those releases. The notice mentions no workaround.
  • Backport dates matter: 19.3.2, 19.2.6 and 19.1.8 shipped on 10 September 2026. GitLab backported the fix to 19.0.9 and 18.11.12 on 23 September 2026, so anyone on 18.11 or 19.0 had no fixed release until then. Treat the exposure window for those lines as running to 23 September when you hunt through logs.
  • Exploitation: CISA added it to the KEV catalog on 11 September 2026 as "GitLab Community Edition and Enterprise Edition Path Traversal Vulnerability", with a federal due date of 14 September 2026. The KEV entry marks forensic triage as required ("Forensics Triage Requirements" under BOD 26-04) and lists ransomware campaign use as unknown. See also the NVD record.
  • Detections: GitLab says it released three threat detections for customers. Their names refer to an LFI attempt reading gitlab.yml, LFI via the metadata.path parameter, and a file-path LFI attempt.
  • Background: CVE-2023-2825 (May 2023) was a different path-traversal bug that affected only GitLab 16.0.0 and was fixed in 16.0.1 (NVD). GitLab scored it CVSS 10.0 (NVD: 7.5); it is the bug this article originally covered.

Standard binary package upgrades eliminate the traversal vector but do not cycle compromised keys. Secret-storage tooling itself is not immune either, as a HashiCorp Vault flaw allowing root host takeover via blank SSH certificates showed. An instance patched with the latest release remains fully vulnerable to offline session minting and database decryption if secrets were exfiltrated during the exposure window.

Unauthenticated request

Improper path confinement, no auth check

Arbitrary File Read

secret_key_base

db_key_base

ci_jwt_signing_key

Unauthenticated Attacker

GitLab web tier

Repository commits API

Exfiltrates Server Secrets

Admin Session Cookie Forgery

PostgreSQL CI/CD Decryption

Cloud OIDC Impersonation

Persistent System Takeover


Threat Context & Flaw Mechanics

GitLab's description is brief: improper path confinement and missing authentication enforcement in the repository commits API let an unauthenticated user read arbitrary files from the server. GitLab has not published the request format, so this article does not reproduce one.

What an attacker can reach depends on which files the GitLab application user can read. Which of the files below were readable on a given install is inference, not something GitLab or CISA has confirmed. Check your own file permissions and treat anything the application can read as exposed:

  • gitlab.yml and other application configuration (the target of one of GitLab's detection rules)
  • secrets.yml and database.yml under the Rails config directory
  • /etc/gitlab/gitlab-secrets.json, if the service user can read it (on Omnibus installs it is normally restricted, but containerized deployments can differ)
  • Raw Git repositories under /var/opt/gitlab/git-data/repositories

A fast scan-to-exploit window hit another Git hosting platform in the Gitea RCE flaw enabled by default settings. For GitLab, CISA's KEV listing one day after the first fixes is the sourced evidence that exploitation began quickly.


The True Blast Radius: Anatomy of Compromised Server Secrets

When an attacker reads instance secrets, they obtain cryptographic material governing identity authentication, session encryption, and data-at-rest protection. A different Git platform saw SAML itself become the forgery vector in the critical SAML signature-wrapping flaw exposing GitHub Enterprise Server.

Key / AttributeCryptographic PurposeExploitation MechanismBlast Radius Impact
secret_key_baseEncrypts and signs Rails session cookies (_gitlab_session).Mints encrypted cookies with administrative user IDs via ActiveSupport::MessageEncryptor.Full administrative web access bypassing passwords, MFA, and SAML assertions.
db_key_baseAES-256 symmetric key encrypting sensitive PostgreSQL columns via attr_encrypted.Decrypts database dumps or SQL query results offline.Direct exposure of plaintext CI/CD variables, private SSH deploy keys, and cloud credentials.
otp_key_baseEncrypts TOTP two-factor authentication seeds.Decrypts stored TOTP seeds to generate valid rolling six-digit MFA codes.Complete neutralization of account-level multi-factor protection.
ci_jwt_signing_keyRSA private key signing CI_JOB_JWT tokens for pipelines.Forges valid OIDC identity tokens without triggering a pipeline.Unauthorized role assumption in AWS IAM, GCP Workload Identity, or HashiCorp Vault.
openid_connect_signing_keySigns OpenID Connect assertions for enterprise SSO.Forges JWT claims for downstream applications federated to GitLab.Lateral movement into internal wikis, dashboards, and enterprise tooling.
gitlab_shell.secret_tokenAuthorizes gitlab-shell hook execution against the internal API.Invokes internal APIs directly, bypassing branch commit rules.Silent code tampering without standard audit trail generation.

Possession of secret_key_base allows administrative session forgery:

# Construct forged administrative session payload
key = ActiveSupport::KeyGenerator.new(secret_key_base).generate_key("encrypted cookie")
encryptor = ActiveSupport::MessageEncryptor.new(key, cipher: 'aes-256-gcm')
forged_cookie = encryptor.encrypt_and_sign({ "warden.user.user.key" => [[1], "$2a$10$forged..."], "session_id" => "recon_active" })

Forensic Log Hunting Guide

To determine whether an instance was probed or exfiltrated, security teams must inspect Nginx access logs (/var/log/gitlab/nginx/gitlab_access.log) and Workhorse logs (/var/log/gitlab/gitlab-workhorse/current).

Identifying Traversal Payloads and Successful Exfiltrations

GitLab's detection rules for this flaw refer to attempts to read gitlab.yml and to the metadata.path parameter, so search for both along with generic traversal strings. These patterns are a starting point; they are not GitLab's own signatures.

Inspect access and application logs for traversal signatures and successful HTTP 200 responses returning data:

# Hunt Nginx logs for directory traversal signatures
grep -E -i '(\.\.%2f|%2e%2e%2f|%2e%2e/|\.\.%5c|%252e%252e|gitlab-secrets|gitlab\.yml|metadata\.path|secrets\.yml|passwd)' \
 /var/log/gitlab/nginx/gitlab_access.log*

# Isolate successful data exfiltrations (HTTP 200 with response body > 500 bytes)
awk '($9 == 200 && $10 > 500 && ($7 ~ /\.\./ || $7 ~ /%2e%2e/i || $7 ~ /secrets/)) {print $1, $4, $7, $9, $10}' \
 /var/log/gitlab/nginx/gitlab_access.log

Auditing Session Forgery in Rails JSON Logs

Adversaries leveraging forged cookies execute administrative actions without preceding sign-in entries:

# Detect administrative controller access in Rails logs
grep '"controller":"Admin::' /var/log/gitlab/gitlab-rails/production_json.log | \
 jq '{time: .time, remote_ip: .remote_ip, user_id: .user_id, path: .path, status: .status}'

GitLab Secrets Rotation Runbook and Incident Recovery

Critical Warning: Modifying db_key_base in /etc/gitlab/gitlab-secrets.json breaks existing database ciphertext, causing OpenSSL::Cipher::CipherError: bad decrypt exceptions on all encrypted records. Teams must follow a staged invalidation and token reset process.

PostgreSQLRedis CacheGitLab ServerPlatform TeamPostgreSQLRedis CacheGitLab ServerPlatform Teamgitlab-ctl deploy-page up(Maintenance)1Rotate secret_key_base2Flush session cache3gitlab-ctl reconfigure &&gitlab-ctl restart4gitlab-rake gitlab:doctor:secrets5Reset and re-encrypt system tokens6gitlab-ctl deploy-page down7

Phase 1: Quarantine and Invalidate Forged Sessions

  1. Enable the maintenance page and back up configurations:
sudo gitlab-ctl deploy-page up
sudo cp /etc/gitlab/gitlab-secrets.json /etc/gitlab/gitlab-secrets.json.bak
sudo gitlab-rake gitlab:doctor:secrets VERBOSE=1
  1. Generate a 128-character hex string and replace secret_key_base in /etc/gitlab/gitlab-secrets.json:
NEW_KEY=$(openssl rand -hex 64)
# Replace "secret_key_base" in gitlab-secrets.json with $NEW_KEY
  1. Purge session keys in Redis to invalidate active adversary sessions:
sudo gitlab-rake cache:clear
sudo gitlab-redis-cli -s /var/opt/gitlab/redis/redis.socket \
EVAL "for i, name in ipairs(redis.call('KEYS', 'session:gitlab:*')) do redis.call('DEL', name) end" 0
sudo gitlab-ctl reconfigure && sudo gitlab-ctl restart

Phase 2: Mitigate Database Encryption and CI/CD Compromise

  1. Reset encrypted internal tokens across system models:
sudo gitlab-rake gitlab:doctor:reset_encrypted_tokens MODEL_NAMES="Project,Group,User,ApplicationSetting"
  1. Rotate external cloud credentials:
  • Revoke AWS IAM keys, GCP service account tokens, and database passwords stored in CI/CD variables.
  • Re-enter updated values via the GitLab web interface or REST API to write newly encrypted records to PostgreSQL.
  1. Cycle runner authentication tokens via the API:
curl --request POST --header "PRIVATE-TOKEN: <ADMIN_PAT>" \
"https://gitlab.example.com/api/v4/runners/<RUNNER_ID>/reset_authentication_token"
  1. Restore production traffic:
sudo gitlab-rake gitlab:check SANITIZE=true
sudo gitlab-ctl deploy-page down

Perimeter Defenses and Remediation Workflow

GitLab's notice offers no workaround, and upgrading is the fix. If an upgrade has to wait a short time, the generic traversal filters below may reduce exposure. They are not tested against CVE-2026-85706, may not match its requests, and do not replace upgrading. Apply perimeter filter rules at the Web Application Firewall (WAF) or reverse proxy layer. Network management-plane appliances face the same isolation imperative, covered in the Arista VeloCloud Orchestrator zero-day demanding active threat hunting.

ModSecurity / OWASP Core Rule Set

SecRule REQUEST_URI_RAW "@rx (?i)(?:\.\.(?:\/|%2f|\\|%5c)|%2e%2e(?:\/|%2f|\\|%5c)|%252e%252e)" \
 "id:1000001,phase:1,deny,status:403,log,msg:'GitLab Traversal Blocked'"

SecRule REQUEST_URI "@rx (?i)(?:gitlab-secrets\.json|secrets\.yml|database\.yml|\/etc\/passwd)" \
 "id:1000002,phase:1,deny,status:403,log,msg:'GitLab Configuration File Read Blocked'"

Fronting Nginx Proxy Filtering

if ($request_uri ~* "(\.\.%2f|%2e%2e%2f|%2e%2e/|\.\.%5c|gitlab-secrets|secrets\.yml)") {
 return 403 "Forbidden: Exploit Signature Detected";
}

Official Binary Package Upgrade

Upgrade to the latest patch release of your supported GitLab line, and no lower than 19.3.2, 19.2.6, 19.1.8, 19.0.9 or 18.11.12. Check GitLab's releases page for the current patch version, as newer releases may have shipped since this correction. Follow GitLab's upgrade path documentation for your install type (Omnibus, source or Helm).

Upgrading patches the file-read flaw, but true incident closure requires completing secrets rotation and verifying application logs for indicators of pre-patch data compromise.

Related reading

Keep reading

All latest →
  1. watchResearchOrnith-1.5-122B vs Sage-A1.5: Which Open Coding Model Fits Your 16, 24, 32 or 128 GB Machine5 min
  2. watchResearchClaude Duet Opens to US Pro and Max by Waitlist, and Its Background Work Draws on Your Limits4 min
  3. watchResearchKimi K3.1 Pro Cuts Moonshot's API Price to $2/$10 per Million Tokens, Matching Claude Sonnet 5.5 on Paper6 min
  4. stableResearchStock House Plans vs AI-Generated Plans: What a Code Check Catches9 min
  5. watchResearchRecursive Flint on Local Hardware: Which Quant Fits 16, 24 and 128 GB7 min
  6. watchResearchOpenAI's 12-Hour Prompt Cache: Break-Even Math for GPT-6 Astra, Luna and Sol7 min