ARTICLE ORIGINAL MAG212 CYBER

Inside the GitLab Flaw: From CI Configuration to Server Execution (CVE-2026-97229)

octobre 31, 2026 · El Mostafa Ouchen

Vulnerability Analysis · CVE-2026-97229

A CVSS 10.0 bug in GitLab’s regular-expression compiler is now being exploited. This guide covers who is exposed, which version to upgrade to now (it is not the one in the original advisory), how the attack works, and how to look for signs of earlier compromise.

Actively exploitedCISA KEVGitLab CE/EERemote code executionDFIR
Gravité
CVSS 10.0
Critical · AV:N/PR:N/UI:N/S:C
Weakness
CWE-190
Integer overflow in the regex compiler
Exploitation
Confirmed
Added to CISA KEV on Oct 29, 2026
Patch to
19.5.3
or 19.4.5 / 19.3.7 / 19.2.10

GitLab sits at the center of the software supply chain. It holds source code, runs builds and often keeps credentials that reach all the way into production. When a flaw in that platform lets an outsider run commands on the server, patching it is only half the job. The other half is working out whether someone got in first.

On October 29, 2026, the U.S. Cybersecurity and Infrastructure Security Agency (CISA) added CVE-2026-97229 to its Known Exploited Vulnerabilities (KEV) catalog. That confirms the flaw is being exploited in the wild.4 GitLab released the fix on October 12.1 Rapid7 published a full technical analysis and a public proof of concept days later.7,8 Any self-managed instance that is still unpatched should be treated as a priority incident.

Points clés à retenir

  • Upgrade to GitLab’s October 29 releases (19.5.3, 19.4.5, 19.3.7, 19.2.10 or the matching release for your branch). The first fixes for this CVE (19.4.2, 19.3.5, 19.2.8) are still exposed to a second CVSS 10.0 regex-compiler flaw, CVE-2026-110264, patched on October 21.2,3
  • The flaw gives code execution on the GitLab application server as the git service account, not just inside a CI job. Rapid7 reproduced this on gitlab-ce 19.4.1 with ASLR enabled.7
  • Instances that allow open sign-up and public project creation are the easiest to attack. In the route Rapid7 demonstrated, the attacker needs to push to a public project first, then sends an unauthenticated CI lint request.6,7
  • Patching is not the same as clearing an incident. If your instance was exposed before you patched, investigate for prior compromise and review every credential the server could reach.
What is and isn’t known

Active exploitation is confirmed by CISA’s KEV listing. As of October 31, the sources we reviewed do not name an attacker or a campaign. They also contain no victim timeline and no verified list of attack IP addresses or file hashes. The attack sequence below is Rapid7’s lab reproduction, and real attacks may take another route. The commands in this guide are defensive examples, not output from a real incident.

01 / EXPOSUREWho needs to act, and which version to install

GitLab rates CVE-2026-97229 Critical, CVSS 3.1 score 10.0 (vector AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H). It affects both Community and Enterprise editions. The vulnerable ranges are 19.2 before 19.2.8, 19.3 before 19.3.5, and 19.4 before 19.4.2. GitLab.com already runs the patched code. GitLab patched all GitLab Dedicated tenants before the advisory was published.1 The CVE record classifies the weakness as CWE-190, integer overflow or wraparound. The researcher joaxcar reported it through GitLab’s HackerOne bug bounty program.5

Important: do not stop at the first fix

On October 21, GitLab patched CVE-2026-110264, another CVSS 10.0 integer overflow in the regex compiler, reported by the same researcher. It affects every version from 7.0 up to the October 21 releases. That means an instance upgraded only to 19.4.2, 19.3.5 or 19.2.8 is still critically exposed.2 GitLab then shipped further patch releases on October 29.3 Those are the versions to install today.

BranchFirst fix for CVE-2026-97229
(Oct 12)
Also fixes CVE-2026-110264
(Oct 21)
Recommended target
(Oct 29)
19.5Not in affected range19.5.119.5.3
19.419.4.219.4.319.4.5
19.319.3.519.3.619.3.7
19.219.2.819.2.919.2.10
19.1Not in affected range19.1.919.1.10
19.0Not in affected range19.0.1019.0.11
18.11Not in affected range18.11.1318.11.14

Sources: GitLab patch releases of October 12, 21 and 29, 2026.1–3 The October 29 release also says self-managed GitLab Runner and AI Gateway must be updated separately. Check GitLab’s releases page for anything newer before you schedule maintenance.

The exposed population is large. Censys data reported on October 14 counted roughly 25,800 vulnerable GitLab hosts, about 31% of the GitLab hosts it observed. The largest numbers were in Germany, Russia and the United States.16

02 / ATTACK PATHHow repository content becomes server execution

The bug sits in Onigmo, the regular-expression engine built into Ruby, and in how GitLab uses it. GitLab compiles regular expressions from CI configuration, such as rules:exists et rules:changes patterns. That means text an attacker controls ends up being compiled on the server.7

From repository input to application execution

  1. Attacker prepares a public projectCommits a malicious .gitlab-ci.yml and a file path with unusual four-byte UTF-8 characters.
  2. Unauthenticated CI lint requestA request to the CI lint endpoint makes GitLab check the project’s paths against the attacker’s regex.
  3. The guard is bypassedAn inline comment hides a large repeat count from GitLab’s digits-only size check, so the pattern passes.
  4. Recompilation and overflowThe UTF-8 path forces the pattern to be compiled again, outside the timeout and size checks. The program-size calculation overflows a signed 32-bit integer.
  5. Command execution in the Puma workerThe broken compiled code redirects execution to system(), which runs commands as the git user. The worker then crashes and is replaced.
Figure 1. Simplified view of Rapid7’s demonstrated exploit route. MAG212 CYBER illustration based on Rapid7’s analysis.6,7

Two details matter for defenders. First, the attack needs write access to a project. GitLab’s PR:N rating describes the final request, which needs no login. But Rapid7 notes that its route first requires pushing a commit, and that this does not fit neatly with a “no privileges” rating. On an instance with open sign-up and public project creation, getting that access is trivial.6,7

Second, this is code execution on the application server, not in a disposable CI runner. Rapid7 achieved command execution in the Rails/Puma worker as the git account, with ASLR enabled. Its lab exploit needed around 2 GiB of extra memory and offsets tuned to the specific build. That is a real barrier, but a well-resourced attacker can get past it. The exploit does not by itself give root access.7

03 / ROOT CAUSEWhy a regex timeout is not a fix

On October 23, the Ruby core team published an advisory covering four regex-engine bugs found through GitLab’s bug bounty program: CVE-2026-89078, CVE-2026-93577, CVE-2026-97229 and CVE-2026-110264. The advisory says CVE-2026-97229 comes from an integer overflow in the compiled pattern size. It recommends Ruby 4.0.9, 3.4.12 or 3.3.13 or later, the releases that contain all four fixes. On the 4.0 branch, the fix for this specific overflow first shipped in 4.0.8.9

Ruby’s guidance is clear. Only applications that compile regular expressions from attacker-controlled strings are exposed, for example through Regexp.new, Regexp.union ou /#{input}/. Patterns written by developers as constants are not affected. Regexp.timeout does not protect against these bugs. Limiting pattern length is also unreliable, because some of them can be triggered by short patterns.9

The upstream fix, Ruby pull request #19020, makes two changes. It replaces a multiplication that could overflow (the pattern length times the repeat count) with a comparison based on division, so the dangerous product is never calculated. It also caps the compiled program’s size while it is being built, raising a RegexpError before jump offsets can wrap around.10 The general lesson: once a value has overflowed, checking it afterwards cannot prove it was safe. The check has to happen before the overflow.

Operational consequence

Upgrade GitLab through its supported distribution (the Linux package, the official container image or the Helm chart). Updating the host operating system’s ruby package does not update the Ruby that GitLab bundles, your container images or every application node. Other Ruby applications that compile user-supplied regular expressions need their own assessment against Ruby’s advisory.

04 / INVESTIGATIONBuild a timeline before drawing conclusions

Start your investigation window on the date a vulnerable version first became reachable by untrusted users, not on the date CISA announced exploitation. Rapid7’s proof of concept has been public since mid-October, so the window may be wider than you expect.8 Keep that window open until containment and patch verification are complete. Record every timestamp in UTC, along with the source time zone, the host or pod it came from, and any clock drift. Store repository commits and project IDs alongside request correlation IDs so events can be matched up later.

Correlate three independent evidence layers

Layer 1Request
  • Long-running …/ci/lint calls
  • HTTP 502 / “badgateway” EOF in Workhorse
  • Proxy and WAF logs
Layer 2Application
  • Puma worker restarts
  • regexp_matcher.rb in traces
  • New CI configuration commits with odd paths
Layer 3Host
  • Shell started by the Puma process
  • Sudden memory jump of about 2 GiB
  • New outbound connections
Strong signal = several layers line up within the same time window and correlation ID
Figure 2. An investigation model built by MAG212 CYBER. Signal examples come from Rapid7’s hunting guidance.7
SignalHow to interpret it
Long GET /api/v4/projects/<id>/ci/lint?content_ref=… near a worker crashMatches the demonstrated route, but ordinary lint traffic is common.
HTTP 502 / “badgateway: failed to receive response: EOF” plus a Puma restartA useful correlation. Not proof of compromise on its own.
regexp_matcher.rb in a saved traceSupports a regex-path investigation. Not proof on its own.
Commit with a comment-split repeat, POSIX character classes and four-byte UTF-8 path namesA strong lead when it doesn’t match the project’s normal activity.
Unexpected shell or interpreter started by a Puma workerEscalate if it can’t be explained. Rule out legitimate maintenance first.
New tokens, users, SSH keys, webhooks or pipeline changesGeneral post-compromise hunting. Not specific to this CVE.

Rapid7 says other exploitation paths may look different. Not every successful exploit has to crash a worker.7

GitLab also publishes six detections for this CVE in its open-source gitlab-security-oss/tldr repository: onigmo_native_worker_crash, graphql_cilint_hostile_regexp, ci_regexp_oversized_non_ascii_path, ci_regexp_guard_rejection, ci_lint_workhorse_worker_crash et ci_lint_scripted_client_error_burst.11 Take the rule logic directly from that repository and test it against your own log schema before relying on it.

05 / HANDS-ONDefensive command-line triage

Scope and safety

These examples are for Linux package (Omnibus) installations unless stated otherwise. They are built around documented GitLab commands and log locations. They are not a vendor-certified scanner, and they have not been run against a live incident. Work on preserved copies where you can, because inspecting a live system changes its state. Keep all output private: logs, process lists and environment data can contain secrets.

A · Identify the running environmentbash
sudo gitlab-rake gitlab:env:info
sudo gitlab-ctl status

Both are documented GitLab commands.12,13 Run them on every Rails application node. Record the GitLab version, revision and bundled Ruby version, and compare each node and container image digest with the version you intended to deploy. A healthy service status shows the service is up. It does not show the server was never compromised.

B · Inventory local logs, including rotated filesbash
sudo find /var/log/gitlab -maxdepth 2 -type f \
  -printf '%TY-%Tm-%TdT%TH:%TM:%TS  %s  %p\n' | sort

Log paths and rotation behavior follow GitLab’s log documentation.14 Uses GNU find syntax.

C · Broad search across preserved, uncompressed logsbash
# Point this at a restricted COPY of the collected logs
LOG_COPY='/mnt/evidence/gitlab-logs'
rg -n -i --text \
  'ci/lint|cilint|content_ref|regexp_matcher\.rb|onigmo|badgateway|failed to receive response|segmentation fault' \
  "$LOG_COPY"

Requires ripgrep. Expect a lot of false positives. No match does not mean the system is clean. Compressed archives need the next step.

D · Search compressed archives without altering thembash
# .gz archives (logrotate) and svlogd '@….s' archives
find "$LOG_COPY" -type f \( -name '*.gz' -o -name '@*.s' \) -print0 |
  while IFS= read -r -d '' f; do
    gzip -cdf -- "$f" | rg -n -i --label="$f" -H \
      'ci/lint|regexp_matcher\.rb|onigmo|badgateway|EOF'
  done

GitLab documents both .gz and svlogd .s archives.14 Check each archive’s format first, and leave the originals untouched.

Prioritize the gitlab-workhorse, puma et gitlab-rails logs, together with reverse-proxy, load-balancer and central SIEM logs. For JSON logs, pull out the time, path or URI, status, remote address and correlation_id, using the field names your version actually writes. Field names vary between components and versions. Interpret client IP addresses in light of your trusted-proxy configuration.14

E · Capture current host contextbash
date -u '+%Y-%m-%dT%H:%M:%SZ'
sudo ps -eo user,pid,ppid,lstart,args --forest
sudo ss -tpn
# Set the start date to the beginning of your exposure window
sudo journalctl -k --since '2026-10-01 00:00:00 UTC' --no-pager

Look for a shell, interpreter or download tool (sh, bash, curl, wget, python) running under a Puma worker as git, especially one with outbound connections. Current process and socket lists will not show activity that has already ended. Kernel logs may explain a crash or out-of-memory event, but they cannot show who caused it.

F · Review repository history in an isolated copybash
# Never run the project's pipeline or scripts during analysis
git --no-pager log --all --date=iso-strict \
  --format='%H %ad %an %s' --name-status -- .gitlab-ci.yml
git --no-pager show --no-ext-diff --no-textconv '<SUSPECT_COMMIT>:.gitlab-ci.yml'
git -c core.quotePath=true ls-tree -r --name-only '<SUSPECT_COMMIT>'

With core.quotePath=true, non-ASCII path names are shown as escaped bytes, which makes four-byte UTF-8 characters easy to spot. Also check any CI files the configuration includes and any deleted branches. A clone may not contain deleted or unreachable objects, so pull those from server-side evidence. Git author names and timestamps can be set by the attacker. Confirm them against GitLab’s server-side push and authentication records.

06 / RESPONSEContainment, recovery and prevention

A response that closes both the flaw and the incident

  1. Contain and preserveRestrict access at the network edge. Collect volatile data and export logs before retention expires.
  2. Patch the real deploymentFollow the required upgrade stops, then confirm the version on every application node and image.
  3. Investigate trust and secretsReview repositories, identity events, runners and deployment access the server could reach.
  4. Recover and validateRebuild if compromised, revoke exposed credentials, and monitor for recurrence.
Figure 3. MAG212 CYBER’s defensive workflow for CVE-2026-97229.

Contenir

If you suspect active compromise, restrict access to the instance at a controlled network boundary and bring in incident responders. Collect volatile evidence if you can do it without delaying urgent containment. Export central logs before retention expires. Don’t publish raw CI configurations, process output or secrets as “proof”.

Correctif

Upgrade to the October 29 release for your branch, or anything newer (see Section 01). Follow GitLab’s required upgrade stops (for the 19.x series these are 19.2, 19.5, 19.8 and 19.11) and let background migrations finish between stops.15 The October 12 release includes database migrations, so single-node instances should plan for downtime.1 A backup taken after a known compromise is evidence. Don’t assume it is safe to restore from.

Reduce exposure while you patch

Rapid7 recommends restricting anonymous access to public projects and stopping untrusted users from creating or writing to projects. Rapid7 is explicit that this does not fix the compiler bug and may not block other routes.6 In practice, that means turning off open sign-up (or requiring admin approval), limiting who can create projects, and setting default and restricted visibility levels. MFA protects accounts, but it does nothing to stop an unauthenticated request to the vulnerable endpoint.

Credentials

List every token, CI/CD variable, SSH key, cloud identity and deployment credential the compromised service could access. After containment, revoke anything exposed and issue replacements through a trusted control plane. Check downstream audit trails, such as your cloud provider, container registry and package registries, for any use of the old credentials. Don’t limit the search to the GitLab host.

Recovery

If compromise is confirmed, rebuild the affected application infrastructure from trusted images. Restore data only after reviewing it, check the integrity of repositories and pipelines, and re-establish trust in runners and deployment integrations. Keep the originals for the investigation. Changing passwords and restarting services will not remove every possible form of persistence.

Preventive controlPurpose
Restrict sign-up and public project creationCuts down write access for untrusted users. Keep only the access the business actually needs.
Segment the application, runners and productionLimits lateral movement. Avoid privileged runner configurations unless they’re required.
Short-lived, narrowly scoped deployment identitiesMakes stolen build credentials less valuable and shortens their lifetime.
Protect CI configuration and release approvalsMakes unauthorized pipeline changes harder to hide.
Forward logs and endpoint telemetry off-hostKeeps your visibility even if the server itself is tampered with.
Track bundled runtimes and image digestsConfirms the component you actually run got the fix, instead of assuming an OS update covered it.

07 / CLOSUREWhen can you call the incident resolved?

Reach three separate conclusions, and record each one on its own:

  • Vulnerability remediatedEvery application node and image runs a release that fixes both CVE-2026-97229 and CVE-2026-110264, and this has been checked rather than assumed.
  • Investigation completed within the available evidenceRecord which nodes, date ranges, log sources and credentials were reviewed, and which telemetry was missing.
  • Recovery validatedCredentials are rotated, integrations are trusted again, and monitoring is in place to spot a recurrence.

A clean search means no matching evidence was found in the sources you checked. It does not prove the system was never accessed. Say that plainly in your report.

MAG212 CYBER assessment

Treat GitLab as production security infrastructure, not a developer convenience. An effective response to CVE-2026-97229 has three parts. First, an application update you have verified, to a release that also fixes CVE-2026-110264. Second, evidence correlated across the request, application and host layers. Third, a disciplined review of the delivery credentials that make this platform so valuable to attackers.

REFERENCESSources primaires

  1. GitLab Critical Patch Release: 19.4.2, 19.3.5, 19.2.8 — GitLab, Oct 12, 2026
  2. GitLab Critical Patch Release: 19.5.1 (CVE-2026-110264) — GitLab, Oct 21, 2026
  3. GitLab Patch Release: 19.5.3 — GitLab, Oct 29, 2026
  4. CISA adds two Known Exploited Vulnerabilities to catalog — CISA, Oct 29, 2026
  5. CVE-2026-97229 record — CVE Program (CNA: GitLab)
  6. CVE-2026-97229: Critical unauthenticated remote code execution in GitLab — Rapid7, Oct 13, 2026 (updated Oct 30)
  7. GitLab CI/CD regex integer overflow: technical analysis — Rapid7, Oct 16, 2026
  8. CVE-2026-97229 proof of concept — Jonah Burgess / Rapid7, GitHub
  9. Regexp fixes for GitLab CVEs — Ruby core team, Oct 23, 2026
  10. Avoid overflow in the regexp compiler’s program size arithmetic (PR #19020) — ruby/ruby, GitHub
  11. GitLab Security detections (tldr) — GitLab Security, GitLab.com
  12. Maintenance Rake tasks — GitLab Docs
  13. Linux package maintenance commands — GitLab Docs
  14. Log system — GitLab Docs
  15. Plan your upgrade path — GitLab Docs
  16. Coverage of Censys exposure data for CVE-2026-97229 — Daily Security Review, Oct 14, 2026

Methodology. Sources were checked on October 31, 2026. CISA’s KEV catalog page could not be retrieved directly, so this article confirms the KEV addition from CISA’s bulletin and the CVE record. It does not state a federal remediation deadline. GitLab’s detection rules are identified by name only, because their full contents were not independently validated. Figures are original MAG212 CYBER explanatory graphics based on the cited material. They are not vendor screenshots or incident evidence. No exploit was run and no production environment was examined for this article.

EO

El Mostafa Ouchen
MAG212 CYBER. Writes practical threat research and defensive guidance for security and engineering teams.

Get the next analysis first

Practical security research and defensive guidance, free in your inbox.

Abonnez-vous gratuitement