flawopen.com/Incidents/GitLab CVE-2026-85706

CVE-2026-85706: how one unauthenticated request read any file on a GitLab server

Critical — CVSS 10.0 CWE-22: Path Traversal Disclosed September 2026
ELI5

A library has a counter where you hand over a slip of paper with a shelf number, and a clerk fetches that item for you. The clerk was supposed to only accept slips pointing at the public shelves — and was supposed to check your library card first. It did neither. Anyone walking in off the street could write "the manager's filing cabinet" on the slip, and the clerk would go get it.

Key terms on this page
path traversal
Supplying a filename containing ../ sequences (or an absolute path) so that a file operation escapes the directory the application intended to confine it to.
self-managed
A GitLab instance you run on your own servers, as opposed to GitLab's hosted SaaS at gitlab.com. Only self-managed instances were affected.
CISA KEV
The US Cybersecurity and Infrastructure Security Agency's Known Exploited Vulnerabilities catalog — a list of flaws confirmed to be under active attack. Listing triggers a binding remediation deadline for US federal civilian agencies.

What happened

GitLab disclosed a path traversal vulnerability in the repository commits API of GitLab Community Edition and Enterprise Edition. An unauthenticated attacker able to reach the instance over the network could send a single crafted HTTP request and read arbitrary files from the server's filesystem — anything readable by the account the GitLab application runs as.

GitLab rated the issue CVSS 10.0, its maximum score. Public reporting indicates exploitation attempts began within roughly 24 hours of the patch release, and CISA added the CVE to its Known Exploited Vulnerabilities catalog on 11 September 2026, setting a remediation deadline of 14 September 2026 for federal civilian agencies.

Timeline
Patch release
GitLab ships fixed versions 19.1.8, 19.2.6 and 19.3.2, disclosing the flaw in the accompanying security release.
Within ~24 hours
Security vendors report opportunistic scanning and exploitation attempts against internet-exposed self-managed instances.
11 September 2026
CISA adds CVE-2026-85706 to the KEV catalog.
14 September 2026
KEV remediation deadline for US federal civilian executive branch agencies.

The technical shape of the bug

The vulnerable surface is the commits endpoint under the v4 REST API:

POST /api/v4/projects/{id}/repository/commits/

The request body carries a file.path value. That value was used to locate a file without being confined to the repository directory, and the endpoint did not require authentication in the affected code path.

Two independent failures had to line up to produce a CVSS 10.0:

1. Improper path confinement

The user-supplied path was not verified to resolve inside the intended repository directory. Traversal sequences in file.path walked the resolved location up and out of the repository root and into the wider filesystem.

2. Missing authentication enforcement on the affected path

A traversal bug behind an authenticated endpoint is serious. The same bug reachable with no credentials at all is catastrophic — it converts every internet-exposed instance into a public file server. This is the factor that pushes the score to the maximum.

GitLab's advisory describes the root cause as improper path confinement together with missing authentication enforcement, but has not published code-level detail of the failed check. The illustration below is a generic demonstration of the class of mistake, not a reproduction of GitLab's source.

The pattern, illustrated

VULNERABLE PATTERN
# The supplied path is joined to the repo
# root and opened. Nothing verifies where
# it actually resolves to.
full = File.join(repo_root, params[:file][:path])
File.read(full)

# file.path = "../../../../etc/passwd"
# resolves far outside repo_root
CONFINED
# Resolve first, then prove containment
root = File.realpath(repo_root)
full = File.realpath(
  File.join(root, params[:file][:path])
)

unless full.start_with?(root + File::SEPARATOR)
  raise ArgumentError, "path escapes repository"
end
File.read(full)

The load-bearing idea is that you cannot validate a path by inspecting the string. Blocklisting ../ fails against encoded variants, absolute paths, and symlinks. The only durable check is to fully resolve the path — following every .. and every symlink — and then assert that the resolved result is still underneath the directory you intended.

Why patching alone was not enough

This is the part teams got wrong. An arbitrary file read on a GitLab server does not just expose source code — it exposes the secrets that the server keeps on disk. Depending on configuration, that can include the instance's shell secret file, SSH host keys, deploy tokens, CI/CD variables and database credentials.

Once those have potentially been read, upgrading closes the hole but leaves the attacker holding valid credentials. Any instance that was internet-reachable and unpatched during the exposure window should be treated as having leaked its secrets, and those secrets rotated.

The transferable lesson

The interesting thing about this CVE is not that GitLab shipped a path traversal — it is how ordinary the ingredients were. A parameter named path, a join against a base directory, and an endpoint whose authentication requirement was assumed rather than enforced. Each of those individually appears in a very large number of codebases. The lesson is that path handling and authentication should both be checked at the boundary, mechanically, rather than inherited from the assumption that the surrounding framework already did it.

FAQ

Was gitlab.com affected?

The advisory scopes the issue to self-managed Community and Enterprise Edition instances. GitLab's own hosted platform was addressed by the vendor directly; the urgent action applies to organisations running their own servers.

Does this allow code execution?

Not directly — the primitive is an arbitrary file read. In practice that is often enough to reach code execution indirectly, because the files it exposes include credentials and signing secrets that unlock other systems.

Why CVSS 10.0 for a read-only bug?

The score reflects that it is remotely reachable, needs no privileges, needs no user interaction, is trivial to exploit, and — because the exposed secrets grant control over other components — its impact is judged to extend beyond the vulnerable component itself.

Related reading

Sources