CVE-2026-85706: Detecting GitLab's Unauthenticated File Read in Your Logs
Background
GitLab has patched a critical vulnerability in the repository commits API of its Community and Enterprise Editions. Tracked as CVE-2026-85706 and rated CVSS 10.0, the flaw lets an unauthenticated remote attacker read files from a self-managed GitLab server. The primary issues in the flaw are that the endpoint takes a file path from the request without keeping it inside the intended repository directory, and it does not check whether the caller is authenticated.
If you’re running a self-managed GitLab server this should matter to you. A GitLab host concentrates sensitive material. These servers hold database credentials, SSH keys, deploy tokens, CI/CD variables, and the instance secrets themselves. Reading arbitrary files exposes the credentials and keys that guard the rest, which an attacker can then turn on systems that trust the GitLab server, including cloud accounts, container registries, and production deployment paths.
CISA added the vulnerability to its Known Exploited Vulnerabilities catalog on September 11, 2026 after confirming exploitation, and gave federal civilian agencies until September 14 to remediate. It was also flagged under Binding Operational Directive 26-04 as requiring forensic triage. That is, upgrading alone does not close the matter and you should treat previously exposed servers as potentially compromised.
Affected Products
Self-managed GitLab Community Edition and Enterprise Edition:
- 18.7 through 19.1.7, fixed in 19.1.8
- 19.2.0 through 19.2.5, fixed in 19.2.6
- 19.3.0 through 19.3.1, fixed in 19.3.2
GitLab.com and GitLab Dedicated were updated by the vendor and require no customer action.
Patching and Mitigation
Upgrade to the fixed release matching the branch already in use. The fixes were published on September 10, 2026, and one upgrade closes the issue. This patch carries database migrations, so single-node instances will take downtime while they complete, and multi-node deployments can avoid that by following GitLab's zero-downtime procedure. Of the three releases, only 19.3.2 carries post-deployment migrations. Verify the version actually running rather than assuming an update applied.
Where an upgrade cannot happen immediately:
- Restrict network access to the instance. Remove it from the public internet or limit it to trusted address ranges.
- Review which projects are set to public visibility. Exploitation needs a public project to supply a usable project ID, so an instance hosting none is considerably harder to attack.
- Treat any internet-facing instance left unpatched after September 10 as potentially compromised, and rotate the credentials that exposure puts at risk. That includes database credentials, SSH keys, deploy tokens, personal access tokens, and CI/CD variables. Follow GitLab's documented rotation process rather than regenerating the instance secrets file by hand, which can leave encrypted values in the database unreadable.
How Exploitation Works
The flaw resides in how GitLab handles multipart file uploads, which the repository commits API exposes to unauthenticated callers. GitLab Workhorse buffers an uploaded request body to disk and hands Rails a set of generated parameters pointing at what it wrote: file.path for the uploaded body, and metadata.path for a generated metadata file. Rails opens whatever those parameters name.
Both parameters are reachable by the caller. Supplying one directly, pointed at a path on the host rather than at Workhorse's temporary file, makes Rails open that path and return its contents. The endpoint does not confine the path to the repository directory, and it does not enforce authentication, so an anonymous caller can do this. Neither failure on its own would allow it.
The primary route is the repository commits API, at a path of the form /api/v4/projects/{id}/repository/commits, and the repository files API is affected the same way. The project ID has to correspond to a real project, which is why a single public project is the precondition for exploitation. No account, token, or user interaction is required, and the request does not need to be chained with any other flaw. Each request retrieves one file, so an attacker working through a list of target files repeats it.
Public exploit code we’ve analyzed reaches straight for instance configuration, log files, database settings, and private keys, naming each target by its full path on the server. It also works around the sanitization in front of the endpoint in three ways, each of which leaves a mark:
- A trailing slash on the route
- A percent-encoded letter in the route such as
%63ommitsforcommits, - A percent-encoded parameter name such as
file%2epath.
According to public reporting, probing of internet-facing servers began within a day of the patch release, and several fully working exploit scripts are now circulating publicly. Reproducing the attack takes no particular skill, so both targeted and opportunistic attempts should be expected.
Where the Evidence Lands
Detection depends on which part of the request carries the parameter, and the two cases are visible in different places.
When the parameter travels in the request body, as a multipart upload, it usually doesn’t appear in a web server, proxy, CDN, or load balancer log, because those tend to record the request line only. It is recorded by GitLab itself in the params array of api_json.log. This is the more complete source, and worth noting it’s the only one that also shows whether the caller was authenticated.
When the parameter travels in the URL query string, the web tier sees it directly. Any source that records the full request URL will carry it, which covers web server access logs, reverse proxy and load balancer logs, CDN logs, and WAF logs. Sources that support the detection logic below include but are not limited to NGINX, Apache, AWS Application Load Balancer, Amazon CloudFront, AWS WAF, Cloudflare WAF, Akamai, and F5 BIG-IP.
Where neither feed is centralized into a SIEM, api_json.log can still be searched on the host.
Detection Guidance
The dependable signal in both cases is the parameter name, though the baseline differs. In GitLab's own log, genuine uploads carry a file.path as well, so the anomaly is a value pointing somewhere other than Workhorse's temporary directories. In a web tier log there is no legitimate occurrence at all since these parameters are generated internally and never travel in a client URL. Either way the parameter stays constant across attempts while the target file varies, which keeps the logic effective as tooling changes. Note that covering file.path alone is not enough, since an attacker can move one parameter over to metadata.path and reach the same primitive.
In GitLab's API log, for the request body
ALL of:
source is GitLab api_json.log
method is POST
params contains any of:
"file.path" "file%2epath" "metadata.path" "metadata%2epath"
meta.client_id starts with "ip/" // caller is unauthenticated
params does NOT contain "/uploads/tmp/" // legitimate upload sandbox
params does NOT contain "/tmp/gitlab/" // legitimate Workhorse scratch
Note the temp directory filters. Genuine uploads carry a file.path, so the anomaly is a value that resolves outside those directories. Anchor both prefixes to the absolute paths your instance uses. GitLab actually ships these as two rules, one per parameter, with the Workhorse scratch directory allowed for metadata.path specifically. Count the enumeration separately as well. Five or more distinct out-of-sandbox values from one source address within an hour indicates a filesystem sweep.
(By the way we ❤️ vendors that provide detections)
In web tier logs, for the request URL
ALL of:
event.category is one of (web, network, firewall, intrusion_detection)
url.path contains "/api/v4/projects/"
url.path contains "/repository/"
ANY of:
url.query or url.original contains "file.path="
url.query or url.original contains "metadata.path="
url.query or url.original contains "file%2epath="
url.query or url.original contains "metadata%2epath="
As mentioned before, no sandbox filter is needed here because the Workhorse parameters are generated internally and never appear in a client's URL. Also, look for encoded route forms as they could be considered higher-confidence indicators of a malicious request. A percent-encoded letter in the path like %63ommits for commits is something no legitimate client produces.
Confirming a read
A 400 is the expected rejection for this request shape. A 2xx from the same anonymous caller on the same route means the file may have been served, which is a confirmed read and an immediate escalation. Other statuses still confirm the parameter is reachable and being probed.
Weighing a hit
Repeated requests from one address suggest scanning. So does traffic from hosting provider ranges that have no reason to reach the server. The file being asked for carries the most weight. Requests naming gitlab.yml, secrets.yml, database.yml, or gitlab-secrets.json are after instance secrets and credentials. Requests naming /etc/passwd or /proc/ are host reconnaissance, and credential paths usually follow.
After a confirmed read, attention shifts to the credentials that were exposed. Watch for personal access tokens and deploy keys that were created or modified, unexpected pipeline runs, new runner registrations, SSH authentication from sources nobody recognizes, and logins to cloud or registry accounts whose credentials lived on the GitLab host. Cover the full window the instance was exposed. We can’t stress enough - patching closes the file read, but it doesn’t invalidate anything an attacker already collected.
Abstract Detections
Abstract has released detection content for CVE-2026-85706 for our customers to deploy immediately. Exploitation attempts are caught in-stream with findings raised in real time. Abstract customers should look for the following managed detections in the Web Analytics content pack:
- Web - GitLab CVE-2026-85706 Confirmed File Read
- Web - GitLab CVE-2026-85706 Repository API Arbitrary File Read
- Web - GitLab CVE-2026-85706 Workhorse Route Encoding Bypass
Acknowledgments
GitLab's Signals Engineering team published three detections for this vulnerability as open source under their TLDR framework. They document the vulnerable request shape, the legitimate baseline each parameter has, sample log events, response procedures, and safe atomic tests. They are the authoritative reference for detecting this issue, and much of the guidance above follows their work.
- GitLab LFI file path attempt
- GitLab LFI via metadata.path parameter
- GitLab LFI attempt reading gitlab.yml
Conclusion
CVE-2026-85706 takes little effort to exploit and exposes credentials that reach well beyond the GitLab server itself. Patch self-managed instances to 19.1.8, 19.2.6, or 19.3.2, confirm the version actually running, and review both the API log and web tier logs for the patterns above across the full period the instance was exposed. Abstract customers are covered with OOTB detections for the attacks described.
ABSTRACTED
We would love you to be a part of the journey, lets grab a coffee, have a chat, and set up a demo!
Your friends at Abstract AKA one of the most fun teams in cyber ;)
.avif)
Your submission has been received.




