Back to Newsroom
Threat Intel

Alpine Linux Rebuilds Its GitLab After Attacker Targets CVSS 10.0 Ruby Regex Flaw

An attacker spent two days hammering Alpine Linux's self-hosted GitLab with crafted CI configurations aimed at CVE-2026-97229. The project found no proof the exploit worked, but rebuilt the server anyway and learned that upgrading GitLab alone leaves the Ruby bug in place.

Alpine Linux Rebuilds Its GitLab After Attacker Targets CVSS 10.0 Ruby Regex Flaw

Alpine Linux Rebuilds Its GitLab After Attacker Targets CVSS 10.0 Ruby Regex Flaw

Alpine Linux has rebuilt its self-hosted GitLab server from scratch and reset every user password after an attacker spent two days trying to exploit CVE-2026-97229, a maximum-severity remote code execution flaw, against the instance that hosts the distribution's package sources. The project disclosed the incident on 28 October.

What happened

CVE-2026-97229 carries a CVSS score of 10.0. The weakness is not in GitLab's own code but in Onigmo, the regular expression engine built into Ruby. GitLab compiles the regular expressions found in CI configurations with Ruby, so a crafted pattern in a .gitlab-ci.yml file can reach the vulnerable engine and, under certain conditions, run code on the server. GitLab shipped a fix on 12 October by patching the Ruby bundled in its Linux packages and official container images. Working proof-of-concept exploit code was published the next day.

Alpine's server is the one that hosts aports, the repository from which every Alpine package is built. On 13 October the project upgraded it to GitLab 19.3.4 instead of 19.3.5, which the team describes as a mistake: 19.3.4 was initially named as the fixed version for the 19.3 branch, but the advisory was later corrected to list it as affected.

On 20 October a freshly registered account began probing the server. Over 20 and 21 October it submitted crafted CI configurations that crashed GitLab worker processes eight times, triggered 502 errors and caused CI jobs to fail. In four of those crashes the attacker got partway through the exploit chain before failing at the next step. The same account also downloaded Alpine's public GitLab container image, which the project reads as an attempt to tune the exploit to its exact build.

Alpine blocked the account on 23 October. The attacker returned on 25 and 26 October and tried to get back in, without success.

Why upgrading GitLab was not enough

Alpine moved the server to GitLab 19.3.6 on 22 October and assumed the problem was solved. On 26 October it discovered the instance was still vulnerable. The reason is simple once you see it: GitLab's fix lives in the Ruby that GitLab ships inside its own packages and images. Alpine's container image installs a standard Ruby separately, so the GitLab upgrade never touched the vulnerable regex engine.

The same gap applies to every self-compiled GitLab install or custom image that brings its own Ruby. Those servers need Ruby 3.3.13, 3.4.12 or 4.0.9 (or later) in addition to a patched GitLab release. Ruby 3.2 reached end of life in April and will not receive the fix, yet GitLab's self-compiled installation guide still lists 3.2 as the minimum version, and neither of GitLab's October patch announcements tells self-compiled administrators to update Ruby.

On 27 October Alpine closed GitLab to everyone except its developers and build servers and started the rebuild. The new server was ready on 28 October with Ruby 3.3.13 or later, new GitLab secret keys and a mandatory password reset for every account. It is being tested before it reopens to the public.

What Alpine can and cannot rule out

The project found no sign that the attacker's code actually executed: timing probes showed no delays, there were no unexpected files, keys, hooks or processes, and no private data left through any web or API request Alpine can see.

That is not the same as proof of safety. A successful exploit of this flaw leaves no crash and no trace in the web logs. Some logs from the attack days are gone, including background job logs and the old container's filesystem, and neither Alpine nor its hosting provider logged outbound network connections. Because the evidence needed to confirm or exclude code execution no longer exists, Alpine chose to treat the server as compromised and rebuild it.

Had the attack succeeded, the exposed data could have included user names, email and IP addresses, password hashes, two-factor secrets, CI/CD variables, runner tokens, webhook and push-mirror credentials, hashed personal access tokens, private repositories and confidential issues and merge requests. Beyond the password reset and the new secret keys, Alpine has not yet said whether runner tokens, CI/CD variables, access tokens or integration credentials stored on the server have been rotated.

The packages look clean

The bigger worry for the wider ecosystem is supply-chain tampering, and here the news is better. Alpine verified that every aports change landed between 20 and 22 October arrived through a recorded push by a known developer, and it checked this against records the attacker could not reach: a developer's local copy of the repository and all 45 build servers. Every package built from 20 to 24 October, and the 21 October releases, including the minirootfs files that the official Alpine Docker images are built from, match those verified changes. The checks are being extended to 27 October, the day the server was closed. The keys that sign Alpine packages and releases have never been stored on the GitLab server.

Which GitLab servers are exposed

GitLab.com and GitLab Dedicated are already patched. For self-managed servers, the fixed versions are 19.4.2, 19.3.5 and 19.2.8, and the 21 October releases (19.5.1, 19.4.3, 19.3.6, 19.2.9, 19.1.9, 19.0.10 and 18.11.13) are the newest on each branch; they also close CVE-2026-110264, a separate regex flaw. Branches 19.5, 19.1, 19.0 and 18.11 are not listed as affected.

The published proof-of-concept needs write access first: the attacker commits a malicious CI file and a file path containing UTF-8 characters to a branch of a public project, after which only the final CI lint request is unauthenticated. That barrier disappears on servers where new users can register themselves and create projects. How the original, still-private report to GitLab avoids the write step is not yet known, and Alpine has not said how its attacker's CI configurations reached the server. The CVE was not in CISA's Known Exploited Vulnerabilities catalog as of 28 October.

What to do

  • Linux package or official container image: upgrade to the newest release for your branch.
  • Self-compiled install or custom image with its own Ruby: upgrade GitLab and move Ruby to 3.3.13, 3.4.12, 4.0.9 or later. Servers still on 19.3.4 should go to 19.3.6.
  • Cannot upgrade immediately: restrict anonymous access to public projects, stop untrusted users from creating or writing to public projects, or block the CI lint route. These steps only cover the known exploit route and do not fix the engine itself.
  • Hunt for exploitation: GitLab has released six Sigma detection rules covering hostile patterns in CI regexp warnings and GraphQL ciLint requests, unusually long non-ASCII file paths, and Ruby crash reports pointing to Onigmo. Also look for files owned by the git user in /tmp, unexpected child processes of Puma or Sidekiq, outbound connections, and changed crontabs or SSH keys. Any sign of execution means treating the server as compromised and rotating gitlab-secrets.json, all CI/CD variables, runner tokens and access tokens.
  • Reduce the attack surface: limit who can create accounts and personal access tokens.
  • Alpine users: set a new password via "Forgot your password?" once the server reopens, and change that password anywhere else you reused it; Alpine has seen credential-stuffing attempts using passwords leaked from other sites. Users of the alpinelinux/gitlab image should pull the 19.3.6 tag again, since only the image published on 27 October carries Ruby 3.3.13. Alpine's own Ruby packages are already fixed (3.4.12 on Alpine 3.22, 3.23, 3.24 and edge; 3.3.13 on 3.21) and apk upgrade installs them.

Ruby's maintainers note the bug only matters for programs that compile a regular expression from attacker-controlled text, and that the Regexp.timeout setting offers no protection.

The lesson Alpine learned the hard way is that an application upgrade says nothing about the runtime underneath it. A version check of the exposed GitLab alone would have passed; only a test of the actual behaviour, or an inventory that includes the Ruby build, would have caught it. That gap between "patched on paper" and "patched in practice" is exactly what our vulnerability scanning service is built to surface across your internet-facing infrastructure, before someone else's crafted CI file does it for you.

SHARE