CVE-2026-97229CVE-2026-97229: GitLab CE/EE CI/CD regex integer overflow leading to remote code execution
By Norbert Hofmann, Cyber Defense Analyst · Updated · Revision 1
Executive summary
CVE-2026-97229 is an integer overflow (CWE-190) in the CI/CD regular expression compiler of GitLab CE/EE, rooted in Onigmo, the regex engine built into Ruby. GitLab rates it CVSS 10.0 and states that, under certain conditions, an unauthenticated user could execute arbitrary code on the GitLab server via a crafted regex in a CI/CD configuration. Fixes shipped on 2026-10-12 in 19.4.2, 19.3.5 and 19.2.8.
Rapid7 reproduced code execution as the git user on an official 19.4.1 CE image and published a proof-of-concept on 2026-10-13. CISA added the flaw to KEV on 2026-10-29. Alpine Linux reported an attempted exploitation of its self-hosted GitLab on 2026-10-20/21 and renewed attempts on 2026-10-25/26; it found no evidence of successful code execution but rebuilt the instance and reset all passwords.
Key pitfall: GitLab's fix patches the Ruby bundled in its own packages and images. Installations that use their own Ruby (source installs, custom images such as Alpine's) stay vulnerable after a GitLab upgrade until Ruby itself is updated to 3.3.13, 3.4.12 or 4.0.9 or later.
Threat assessment
Rated Critical: maximum CVSS, code execution on a system that typically holds source code, CI/CD variables, runner tokens and integration credentials, a public end-to-end PoC since 2026-10-13, KEV listing and a documented real-world attempt against a high-value open-source build infrastructure. Alpine noted the attacker downloaded its GitLab container image, which it read as tuning the exploit to its exact build.
Most exposed are internet-facing self-managed GitLab instances that allow self-registration and public project creation. Rapid7 found that the demonstrated chain requires pushing a malicious .gitlab-ci.yml and a specially named path to a public project, with only the final CI lint request unauthenticated; open registration removes that barrier. GitLab.com and GitLab Dedicated were patched before disclosure.
Successful exploitation may leave little trace: Alpine states a successful exploit would leave no crash and no trace in the web logs. Failed attempts, however, produce worker crashes and HTTP 502 errors, which are the most practical hunting signal.
Timeline
Alpine Linux reports its rebuilt GitLab instance is back online. Source
CISA adds CVE-2026-97229 to the Known Exploited Vulnerabilities catalog. Source
Alpine publishes its incident report; rebuild completed with new secret keys and password resets. Source
Alpine restricts its GitLab to developers and build servers, starts a full rebuild and publishes alpinelinux/gitlab:19.3.6 with Ruby 3.3.13. Source
Attacker returns on 2026-10-25/26 and tries to regain access to Alpine's GitLab; attempts fail. Source
Upstream Ruby ships the fix (3.3.13, 3.4.12, 4.0.9); Alpine blocks the attacker account. Source
Main attack on Alpine's still-vulnerable GitLab: crafted CI configs cause worker crashes, HTTP 502s and failed CI jobs; 4 crashes got partway through the exploit. Source
A newly registered account starts probing Alpine Linux's self-hosted GitLab. Source
Rapid7 publishes a technical analysis including detection artefacts. Source
CVE published; Rapid7 releases an advisory and a public end-to-end proof-of-concept. Source
GitLab releases 19.4.2, 19.3.5 and 19.2.8 fixing CVE-2026-97229 (CVSS 10.0). Source
Affected & exposure
GitLab CE/EE 19.2 before 19.2.8, 19.3 before 19.3.5 and 19.4 before 19.4.2; all self-managed deployment types per GitLab. Fixed in 19.2.8, 19.3.5, 19.4.2 or later (released 2026-10-12).
Installations not using GitLab's bundled Ruby (source installs, distribution packages, custom container images) additionally need Ruby 3.3.13, 3.4.12, 4.0.9 or later (upstream Ruby fix released 2026-10-23 per Alpine).
alpinelinux/gitlab images pulled before 2026-10-27 and all older tags are vulnerable; re-pull alpinelinux/gitlab:19.3.6 (contains Ruby 3.3.13). Alpine package users get fixed Ruby via apk upgrade.
Alpine notes that any program compiling regular expressions from untrusted input with an unpatched Ruby/Onigmo is exposed, not only GitLab.
GitLab.com and GitLab Dedicated: already patched, no action required.
Exploitation in the wild
Reported via HackerOne by joaxcar; GitLab's release notes did not report in-the-wild exploitation at disclosure on 2026-10-12.
Rapid7 published an end-to-end proof-of-concept on 2026-10-13 and a technical analysis on 2026-10-16, executing commands as the git OS user on gitlab/gitlab-ce:19.4.1-ce.0 with ASLR enabled.
Alpine Linux: a newly registered account started probing on 2026-10-20, sent crafted CI configurations on 2026-10-20/21 causing 8 worker crashes (4 partway through the exploit), HTTP 502 errors and failed CI jobs. The account was blocked on 2026-10-23; renewed access attempts on 2026-10-25/26 failed. Alpine found no evidence that the exploit ran but could not rule it out because some logs were lost and outbound connections were not logged.
CISA added CVE-2026-97229 to KEV on 2026-10-29 (per Rapid7). BleepingComputer reports that GitLab detected exploitation attempts and released threat detections for self-managed customers. No threat actor attribution has been published.
Detection & hunting
GitLab released six threat detections for self-managed instances (Onigmo native worker crash, GraphQL CI lint hostile regexp, CI regexp oversized non-ASCII path, CI regexp guard rejection, CI lint Workhorse worker crash, CI lint scripted client error burst); deploy those first. Rapid7 and Alpine document the artefacts below: slow CI lint requests ending in a Workhorse badgateway / HTTP 502, Puma worker crashes, shell children of the worker, and suspicious CI configs from newly registered accounts. Note that a successful exploit may not crash anything.
Hunting starting point, not lab-tested. Map index, table and field names to your environment before use.
index=gitlab sourcetype=gitlab:workhorse
(uri="*/api/v4/projects/*/ci/lint*" OR uri="/api/graphql*")
(status=502 OR error="*badgateway*" OR msg="*badgateway*")
| eval duration_s=coalesce(duration_ms/1000, duration)
| stats count min(_time) AS first max(_time) AS last max(duration_s) AS max_duration values(uri) AS uris BY remote_ip
| convert ctime(first) ctime(last)
| sort - countHunting starting point, not lab-tested. Map index, table and field names to your environment before use.
index=gitlab (sourcetype=gitlab:puma OR source="*/puma/*" OR source="*/gitlab-rails/exceptions_json.log")
("worker" AND ("terminated" OR "crashed" OR "SIGSEGV" OR "Segmentation fault" OR "killed" OR "booted")) OR "regexp_matcher.rb"
| bin _time span=15m
| stats count values(source) AS sources BY _time host
| where count >= 2Hunting starting point, not lab-tested. Map index, table and field names to your environment before use.
title: Shell spawned by GitLab Puma worker (possible CVE-2026-97229 exploitation)
status: experimental
description: Hunts for interactive shells or recon tools started by the GitLab Rails/Puma Ruby process. Baseline first; GitLab legitimately spawns git and some helper processes.
references:
- https://www.rapid7.com/blog/post/ra-gitlab-ci-cd-regex-integer-overflow-technical-analysis-cve-2026-97229
logsource:
product: linux
category: process_creation
detection:
selection_parent:
ParentImage|endswith: '/ruby'
ParentCommandLine|contains: 'puma'
selection_user:
User: 'git'
selection_child:
Image|endswith:
- '/sh'
- '/bash'
- '/dash'
- '/curl'
- '/wget'
- '/nc'
- '/python3'
- '/perl'
condition: all of selection_*
falsepositives:
- Legitimate GitLab hooks or maintenance scripts
level: high
tags:
- attack.execution
- attack.t1190
- cve.2026-97229Hunting starting point, not lab-tested. Map index, table and field names to your environment before use.
DeviceProcessEvents
| where TimeGenerated > datetime(2026-10-12)
| where InitiatingProcessFileName =~ "ruby" and InitiatingProcessCommandLine has "puma"
| where AccountName =~ "git"
| where FileName in~ ("sh", "bash", "dash", "curl", "wget", "nc", "python3", "perl")
| where ProcessCommandLine !has "gitaly" and ProcessCommandLine !has "/opt/gitlab/embedded/bin/git"
| project TimeGenerated, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine
| sort by TimeGenerated descHunting starting point, not lab-tested. Map index, table and field names to your environment before use.
index=gitlab sourcetype=gitlab:production_json
(path="/users" method=POST status=302) OR (path="*/ci/lint*" OR (path="/api/graphql" graphql_operation_name="*ciLint*"))
| eval action=if(path="/users", "signup", "ci_lint")
| stats min(eval(if(action="signup", _time, null()))) AS signup_time min(eval(if(action="ci_lint", _time, null()))) AS first_lint count(eval(action="ci_lint")) AS lint_calls BY remote_ip
| where isnotnull(signup_time) AND isnotnull(first_lint) AND first_lint - signup_time < 172800
| convert ctime(signup_time) ctime(first_lint)Mitigation
- Upgrade self-managed GitLab CE/EE to 19.4.2, 19.3.5 or 19.2.8 or later immediately. Verify the actual running version afterwards; Alpine accidentally upgraded to 19.3.4 instead of 19.3.5.
- If GitLab does not run on its bundled Ruby (source install, custom image), also update Ruby to 3.3.13, 3.4.12, 4.0.9 or later; upgrading GitLab alone does not fix the flaw there. alpinelinux/gitlab users must re-pull 19.3.6 (published 2026-10-27).
- Deploy GitLab's six threat detections for self-managed instances and review Workhorse, Puma and Rails logs back to 2026-10-12 for the artefacts above.
- Interim measures until patched (Rapid7): disable self-registration, block untrusted users from creating or writing to public projects, restrict anonymous access to public projects or block the CI lint route. These only cut the demonstrated delivery path and do not fix the compiler.
- If exploitation cannot be ruled out, follow Alpine's approach: treat the instance as compromised, rebuild it, replace secret keys and reset all passwords. Alpine lists CI/CD variables, runner tokens, webhook, integration and push mirror credentials, 2FA secrets and access tokens as potentially exposed, so rotate those as well. Enable logging of outbound and database connections and keep logs longer than 14 days.
Update log
| Date | Change |
|---|---|
| 2026-11-09 | Initial publication. |
Sources
- GitLab Patch Release 19.4.2, 19.3.5, 19.2.8
- CVE record CVE-2026-97229
- Alpine Linux: GitLab security incident (CVE-2026-97229)
- Rapid7: CVE-2026-97229 - Critical unauthenticated remote code execution in GitLab
- Rapid7 Analysis: GitLab CI/CD regex integer overflow (CVE-2026-97229)
- BleepingComputer: CISA says max severity GitLab flaw now exploited in the wild
- BleepingComputer: Alpine Linux rebuilds GitLab after attempted RCE attack
- The Hacker News: Attacker tried to exploit Alpine Linux's GitLab