Events Secure.com at Black Hat USA 2026

GitLab’s 10.0 Flaw Is Being Exploited. Over 1,000 Servers Still Have No Patch

Over 1,000 GitLab servers remain unpatched against CVE-2026-97229, the CVSS 10.0 remote code execution bug CISA confirms is under active attack.

TL;DR: Three weeks after GitLab shipped the fix, more than 1,000 servers exposed to the internet are still running the vulnerable code. CISA says attackers are already using it. The exploit code is public and free.

What Happened?

GitLab patched CVE-2026-97229 on October 12 in versions 19.4.2, 19.3.5 and 19.2.8. The bug is an integer overflow in the regular expression compiler that reads CI/CD pipeline files. It scores 10.0, the highest severity possible. The request that sets it off needs no login, and it ends with an attacker running commands on the server.

On October 29, CISA added it to the Known Exploited Vulnerabilities catalog and gave federal agencies until November 1 to fix it. Over that weekend, Shadowserver still counted more than 1,000 exposed servers running unpatched code, out of roughly 39,000 GitLab instances it sees daily.

A working exploit went public on October 13. A Metasploit module landed on October 30.

Shadowserver · November 1, 2026

Patched three weeks ago. Still wide open.

GitLab shipped the fix for CVE-2026-97229 on October 12. Shadowserver scans roughly 39,000 GitLab instances every day. Here is how many had not taken it as of the weekend.

Still vulnerable to CVE-2026-97229
1,000+
CVSS 10.0 · On CISA KEV since Oct 29 · Exploited
Still vulnerable to CVE-2026-110264
11,300+
CVSS 10.0 · Same regex engine · Not yet on KEV
GitLab instances seen daily
~39,000
Total internet facing population scanned

The patch that closes CVE-2026-97229 does not close CVE-2026-110264. A server on 19.4.2, 19.3.5 or 19.2.8 is fixed for the exploited bug and still exposed to the other one.

The Patch That Was Not a Patch

Here is the part catching teams out. The overflow does not really live in GitLab. It lives in Onigmo, the regular expression engine built into Ruby.

GitLab ships a patched Ruby inside its own packages, Docker images and Helm chart. Upgrading works there. Build GitLab from source, or run a container image that uses the distribution’s own Ruby, and the GitLab upgrade fixes nothing at all. Ruby itself has to reach 3.3.13, 3.4.12 or 4.0.9.

Ruby released that fix without filing a CVE of its own. So a version scan reads the GitLab number, sees a patched release and reports clean. One Linux distribution project upgraded its server, stayed exploitable, and only found out when its workers crashed eight times across two days. Four of those crashes got partway through the exploit.

The detail most scanners miss

You upgraded GitLab. Did you upgrade Ruby?

The overflow does not live in GitLab. It lives in Onigmo, the regular expression engine built into Ruby. Where your Ruby comes from decides whether the GitLab upgrade actually fixed anything.

Same version numberGitLab 19.3.6 installed
Fixed
Official Omnibus package, official Docker image, or the Helm chart
GitLab ships the patched Ruby inside its own packages. Upgrading GitLab is enough. Check the exact version, because a near miss such as 19.3.4 still counts as vulnerable.
Still vulnerable
Built from source, or a container image using the distribution’s own Ruby
Upgrading GitLab does nothing here. Ruby itself has to reach 3.3.13, 3.4.12 or 4.0.9. One Linux distribution’s image ran standard Ruby and stayed exploitable after its GitLab upgrade.

Ruby shipped its fix without a CVE of its own, so a version scan reads the GitLab number, sees a patched release and reports clean. Check the interpreter, not just the app: gitlab-rake gitlab:env:info or ruby -v.

What’s the Impact?

A GitLab server holds source code, pipeline secrets, runner tokens and deploy keys. Code execution there reaches well past the box itself. The one project that published its incident report rebuilt the server, reset every password, replaced its secret keys and checked its released packages against outside records.

Reproduced chain · GitLab 19.4.1

Four steps from signup to code execution

The final request needs no login at all. What it needs first is a public project the attacker can push to, which open registration hands over for free.

1
Foothold
Register an account, create a public project
Any server with open signup allows this. The account used against one Linux distribution project was newly registered.
2
Plant
Commit a crafted pipeline config
A .gitlab-ci.yml puts a regular expression in rules:exists or rules:changes. An inline comment splits the repeat count, so GitLab’s own guard never sees the real number. A file path carrying unusual characters comes along with it.
3
Trigger · unauthenticated
Call the CI lint API with no credentials
GitLab evaluates the config, Ruby recompiles the pattern for UTF 8, the size calculation overflows, and memory in the web worker is corrupted. The guard already ran and passed.
4
Execute
Run commands as the GitLab user
On a hit, commands run as git, which can read database credentials and the instance secrets file. On a miss, the worker dies, the client sees a 502, and memory spikes by roughly 2 GiB.

A failure is loud and a success can be silent. Hunt for both: the crashes tell you someone tried, and only host telemetry tells you whether one of the tries landed.

How to Avoid This

Upgrade Past the First Patch

Move to 19.5.1, 19.4.3, 19.3.6 or 19.2.9 or later from GitLab’s release page. These also close a second regex bug the first patch leaves open.

Check the Interpreter, Not the App

Run gitlab-rake gitlab:env:info or ruby -v and confirm Ruby is on 3.3.13, 3.4.12 or 4.0.9.

Shrink What an Attacker Can Reach

  • Turn off open registration, or require admin approval for new accounts
  • Block anonymous calls to the CI lint routes at your reverse proxy or WAF

Indicators of Compromise: Hunt for Signs of Exploitation

Alert on web worker crashes and on any shell started by the GitLab user. The hunt pack below lists what to look for.

CVE-2026-97229 · Hunt pack · Nov 2, 2026

What to hunt for on a GitLab server

Read this first. No attacker IP addresses, user agents or payload hashes have been published from in the wild attacks on this flaw. Everything below is a behavioral signature or an artifact left by the public exploit tools. Treat each one as an investigative lead, not proof of compromise. Hunt back to October 12, 2026, and to October 1 if your logs reach that far.
Exploit artifactsStrong signal
  • Oversized file path with a non ASCII characterThe public proof of concept commits one path at the 255 byte limit: a, then 251 x characters, then /é. Request paths with a segment of 200 or more characters plus a percent encoded non ASCII byte have no normal use.
  • Comment split repeat count in a patternA repeat count broken up by an inline regex comment, for example {45(?#x)700,}, hides the real number from GitLab’s guard. Search graphql_json.log for ciLint variables matching \{[0-9]*\(\?# or \{[0-9]{4,}[,}].
  • Regex guard warningsGitlab::Ci::Build::Rules::Rule::Clause::RegexpMatcher in application_json.log with “rejected at compile time”, “match timed out” or “time budget exceeded”. Normal rules filters are short and never trip these.
Crash signaturesStrong signal
  • Workhorse 502 with no matching Rails entryThe strongest single indicator. A 502 or badgateway on ci/lint or /api/graphql whose correlation_id never appears in api_json.log. The worker died before it could write the line.
  • Request bodies near 2 MBIn Workhorse’s read_bytes field on those same routes. That is the attack carrier.
  • Native crash reports with regex frames[BUG] blocks in puma_stderr.log containing onig_compile, compile_length_tree, regexp_matcher.rb, match_at or onig_search_gpos. Also segfault on ruby or puma in kernel logs.
  • Sidekiq worker terminationA pushed hostile config kills Sidekiq instead of Puma: “A worker terminated, shutting down the cluster”.
  • Memory spike and slow requestWorker memory jumping by roughly 2 GiB, with the request running several seconds before it fails.
Setup activityCorrelate
  • Anonymous CI lint requestsCalls to the lint API with no meta.user field. Legitimate traffic comes from signed in users and tokens. Prioritize ones that ran several seconds or returned 500 or 502.
  • Raw client user agents on lint routespython-requests, python-urllib, curl, Wget, HTTPie or no user agent at all, returning 5xx. Exclude glab, go-gitlab and project bot accounts.
  • New account to public project in under an hourAn account created on or after October 12 that creates a public project and triggers CI lint shortly after.
  • Token and branch created then destroyedThe Metasploit module makes a personal access token, pushes a randomly named branch, fires the request, then revokes the token and deletes the branch within minutes. A web login followed by a call to /api/v4/version from the same address fits its version check.
  • Your own container image pulled by an unknown accountSeen in the one publicly documented incident, most likely to tune the exploit against that exact build.
Post exploitationConfirm compromise
  • Child processes of the GitLab usersh, bash, curl, wget, python, perl, nc or base64 spawned by ruby, puma, bundle or sidekiq running as git. In testing, commands ran as uid=998(git).
  • Outbound connections from the GitLab hostAnything beyond your mirrors, registries, mail relay and update servers. Port 8000 is the public proof of concept’s default listener; the Metasploit example fetches a second stage over port 80.
  • Foreign key in authorized_keysEvery legitimate line in the git user’s authorized_keys starts with command="…gitlab-shell…". A line without that prefix is an attacker key.
  • New files, hooks and cron entriesUnder /opt/gitlab/embedded/service/gitlab-rails/public, in custom_hooks folders in Gitaly storage, or in cron for the git user.
  • Credential and supply chain tamperingNew admins, unexpected access tokens, newly registered runners, added deploy keys, changed webhooks, unusual reads of CI/CD variables, and commits, tags or release artifacts published outside normal pipelines.

Sources: GitLab security advisories and TLDR detection repository (Sigma rules, released Oct 30, 2026), Rapid7 technical analysis and proof of concept, Metasploit Framework module documentation, Ruby regexp fix notice of Oct 23, 2026, CISA KEV entry of Oct 29, 2026, Shadowserver exposure reporting, and the published incident report from one affected Linux distribution project.

Patched Is a Claim. Proven Is Evidence.

Over a thousand teams think they are covered right now, and a version scan is the reason. The gap between a patched release number and a server you can prove is safe is exactly where this flaw has been living.

Secure.com AI Teammates work above the stack you already own:

  • Red Teammate validates what is genuinely exploitable on your exposed instances instead of trusting a version string
  • AppSec Teammate follows the real dependency, so a fix that lands in Ruby cannot hide behind an application version number
  • Cloud Security Teammate surfaces the instances nobody mentioned were facing the internet
  • SOC Teammate hunts the worker crashes and anonymous lint calls across your logs, then assembles the case
  • Consequential actions wait for your approval, and every step is kept as evidence

Attack. Harden. Prove. Repeat.

Frequently Asked Questions

What is GitLab CVE-2026-97229?
It is an integer overflow in the regular expression compiler GitLab uses to read CI/CD pipeline files. It scores 10.0 on CVSS, lets an attacker run commands on the server, and CISA lists it as exploited in the wild.
Which GitLab versions fix CVE-2026-97229?
Versions 19.4.2, 19.3.5 and 19.2.8 fix it. Upgrading to 19.5.1, 19.4.3, 19.3.6 or 19.2.9 also closes a second regex flaw, CVE-2026-110264, that the first patch leaves open.
Is upgrading GitLab enough to fix CVE-2026-97229?
Only on the official Omnibus packages, Docker images and Helm chart, which bundle a patched Ruby. Source builds and container images that use the distribution’s own Ruby also need Ruby 3.3.13, 3.4.12 or 4.0.9.
Is CVE-2026-97229 on the CISA KEV list?
Yes. CISA added it to the Known Exploited Vulnerabilities catalog on October 29, 2026, with a federal remediation due date of November 1, 2026.
How can I tell if my GitLab server was targeted?
Look for 502 errors on CI lint routes with no matching Rails log entry, crash reports naming Onigmo functions such as onig_compile, anonymous CI lint calls, and shells started by the git user.