On 28 October 2026, the Alpine Linux project disclosed that an attacker attempted to exploit a critical remote code execution vulnerability — CVE-2026-97229 (CVSS 10.0) — against the project's own self-hosted GitLab instance, roughly one week after GitLab released the fix. Alpine reported no evidence the attack succeeded, but that outcome owed as much to the attacker's timing or technique as it did to the defenders' posture.
This is the incident pattern every SOC and vulnerability management team dreads: a maximum-severity flaw in internet-facing CI/CD infrastructure, public patch available, and exploitation attempts beginning within days. GitLab sits at the center of the software supply chain — it holds source code, CI secrets, deployment credentials, signing keys, and pipeline definitions. A successful RCE on a GitLab server is not just a server compromise; it is a potential supply-chain breach of everything that server builds and deploys. If Alpine's instance had fallen, the integrity of one of the most widely used Linux distributions in containers worldwide would have been in question.
If you run self-hosted GitLab — and especially if your instance is reachable from the internet — treat this as an active-threat scenario, not a routine patch cycle.
Technical Analysis
The Vulnerability: CVE-2026-97229
CVE-2026-97229 carries a CVSS score of 10.0, the maximum possible rating. According to the disclosure, the flaw allows an attacker to execute arbitrary code on a GitLab server by submitting a crafted regular expression embedded in a CI configuration — in practice, this means a malicious .gitlab-ci.yml or related CI configuration input.
From a defender's perspective, the attack chain looks like this:
- Attacker gains the ability to submit CI configuration. Depending on the instance's configuration, this could require nothing more than registering an account (open registration is common on community-facing instances like Alpine's) and pushing a repository or merge request containing a crafted
.gitlab-ci.yml. - GitLab processes the malicious regular expression. The flaw lives in the component that parses or evaluates regex patterns supplied through CI configuration (e.g., rules, coverage extraction, or similar regex-accepting directives). Improper handling of the attacker-controlled pattern leads to code execution in the context of the GitLab server process.
- Code execution on the server. The attacker lands code execution as the
gituser (typical for GitLab's Rails components) — from there, lateral movement into CI runners, secret stores, and the broader environment is a short step.
Why CVSS 10.0 Is Justified Here
A 10.0 score implies the vulnerability is remotely exploitable, requires low complexity, needs no privileges (or trivially obtained ones), and requires no user interaction — with full confidentiality, integrity, and availability impact. For a CI/CD platform, that impact assessment is accurate: GitLab omnibus installations typically run alongside Redis, PostgreSQL, Gitaly, and container registries, and pipeline jobs frequently carry cloud credentials, deploy keys, and registry tokens.
Exploitation Status
- Confirmed attempted exploitation in the wild: Yes — against Alpine Linux's production GitLab instance, approximately one week after the patch release.
- Evidence of successful compromise: None found by Alpine's investigation as of the 28 October disclosure. Do not let this lull you — a failed attempt against a hardened, well-monitored target tells you nothing about attempts against yours.
- Attack surface exposure: Any GitLab instance that allows untrusted users to create projects, push merge requests, or trigger CI pipelines should be assumed targeted. Internet-facing instances with open registration are the highest-risk tier.
The one-week gap between patch and attack attempt is the critical lesson: adversaries are diffing GitLab releases and weaponizing fixes faster than most organizations patch. For critical CI/CD infrastructure, your patch SLA for CVSS 10.0 flaws should be measured in hours, not weeks.
Detection & Response
Post-exploitation on a GitLab server has recognizable fingerprints. The GitLab Rails process (puma/ruby) should essentially never spawn shells or system binaries; CI runner activity should be attributable to legitimate jobs; and web logs will show the malicious project/MR activity preceding exploitation. The detections below target these behaviors.
Sigma Rules
---
title: GitLab Rails Process Spawning Shell or System Binary
description: Detects the GitLab web application process (puma/ruby/sidekiq) spawning a shell or system utility, consistent with post-exploitation activity following RCE such as CVE-2026-97229. GitLab application processes do not legitimately spawn interactive shells.
references:
- https://thehackernews.com/2026/10/attacker-tried-to-exploit-alpine-linuxs.html
- https://attack.mitre.org/techniques/T1059/
author: Security Arsenal
date: 2026/10/29
status: experimental
id: 3f8a1c52-9b4d-4e67-a812-5e6c9d0f1a2b
tags:
- attack.execution
- attack.t1059
- attack.initial_access
- attack.t1190
logsource:
category: process_creation
product: linux
detection:
selection_parent:
ParentImage|endswith:
- '/puma'
- '/ruby'
- '/sidekiq'
- '/gitaly'
selection_child:
Image|endswith:
- '/sh'
- '/bash'
- '/dash'
- '/zsh'
- '/curl'
- '/wget'
- '/nc'
- '/ncat'
- '/python'
- '/python3'
- '/perl'
- '/base64'
condition: selection_parent and selection_child
falsepositives:
- GitLab backup or maintenance rake tasks executed by administrators (rare via puma; typically invoked from shell)
- Health-check scripts misconfigured to run under the application user
level: high
---
title: GitLab CI Runner Executing Shell Outside Expected Job Context
description: Detects suspicious child processes spawned by gitlab-runner that indicate command-and-control or payload staging, such as base64-decoded payloads, reverse shells, or downloads to temporary directories. Relevant to post-exploitation after CI configuration-based RCE (CVE-2026-97229).
references:
- https://thehackernews.com/2026/10/attacker-tried-to-exploit-alpine-linuxs.html
- https://attack.mitre.org/techniques/T1059/
author: Security Arsenal
date: 2026/10/29
status: experimental
id: 8c2d4e61-7a1b-4f39-b506-2d8e5a3c7b94
tags:
- attack.execution
- attack.t1059.004
- attack.command_and_control
- attack.t1105
logsource:
category: process_creation
product: linux
detection:
selection_parent:
ParentImage|endswith:
- '/gitlab-runner'
- '/gitlab-runner-helper'
selection_suspicious:
CommandLine|contains:
- 'base64 -d'
- 'base64 --decode'
- '/dev/tcp/'
- 'curl -s '
- 'wget -q '
- 'chmod +x /tmp/'
- 'chmod +x /dev/shm/'
- 'nc -e'
- 'ncat -e'
condition: selection_parent and selection_suspicious
falsepositives:
- Legitimate CI jobs that download and execute build tooling (tune allowlists per pipeline; alert on non-allowlisted projects)
level: medium
---
title: New External User or Project Created Followed by Rapid Pipeline Activity
description: Detects behavioral precursor to CI configuration attacks: a newly created account or project immediately triggering pipeline execution, a pattern consistent with abuse of open-registration GitLab instances for exploit delivery as seen against Alpine Linux.
references:
- https://thehackernews.com/2026/10/attacker-tried-to-exploit-alpine-linuxs.html
- https://attack.mitre.org/techniques/T1190/
author: Security Arsenal
date: 2026/10/29
status: experimental
id: 5b1e7f38-2c6a-4d84-9e17-8f3a6b2c4d51
tags:
- attack.initial_access
- attack.t1190
- attack.resource_development
logsource:
product: linux
service: gitlab
detection:
selection:
- 'event': 'project_created'
- 'event': 'user_created'
- 'event': 'pipeline_created'
condition: selection
falsepositives:
- High-volume legitimate community activity on open instances; this rule is intended for correlation (user_created followed by pipeline_created from the same user within minutes), not standalone alerting
level: low
KQL (Microsoft Sentinel / Defender)
If you ingest GitLab host syslog and audit logs into Sentinel (via Syslog/CEF collectors or the GitLab audit log API), the following hunts surface the exploitation pattern — web requests creating CI pipelines from new accounts, and shell execution under the GitLab application user.
// Hunt 1: GitLab application processes spawning shells or download tools (post-RCE behavior)
Syslog
| where TimeGenerated > ago(7d)
| where ProcessName in~ ("puma", "ruby", "sidekiq", "gitaly", "gitlab-runner")
or SyslogMessage has_any ("puma", "sidekiq", "gitlab-runner")
| where SyslogMessage has_any ("/bin/sh", "/bin/bash", "base64 -d", "/dev/tcp/", "nc -e", "curl -s", "wget -q")
| project TimeGenerated, Computer, ProcessName, SyslogMessage
| order by TimeGenerated desc;
// Hunt 2: Pipeline creation events from recently created external users (exploit staging)
let GitlabEvents = Syslog
| where TimeGenerated > ago(14d)
| where SyslogMessage has_any ("user_created", "project_created", "pipeline_created", "ci_cd")
| extend Event = extract(@"(user_created|project_created|pipeline_created)", 1, SyslogMessage)
| extend Actor = extract(@"\"?(?:author_name|username|user)\"?[:=]\s*\"?([\w.@-]+)", 1, SyslogMessage)
| where isnotempty(Event) and isnotempty(Actor);
let NewUsers = GitlabEvents | where Event == "user_created" | summarize FirstSeen = min(TimeGenerated) by Actor;
GitlabEvents
| where Event == "pipeline_created"
| join kind=inner NewUsers on Actor
| extend MinutesAfterSignup = datetime_diff("minute", TimeGenerated, FirstSeen)
| where MinutesAfterSignup <= 60
| project TimeGenerated, Actor, Event, MinutesAfterSignup, Computer, SyslogMessage
| order by MinutesAfterSignup asc;
// Hunt 3: Outbound connections from GitLab server processes to rare external destinations (C2 / exfil)
CommonSecurityLog
| where TimeGenerated > ago(7d)
| where DeviceProduct has "GitLab" or SourceHostName has "gitlab"
| where DestinationIsIpv4 and not(ipv4_is_private(DestinationIP))
| summarize ConnectionCount = count(), DistinctPorts = dcount(DestinationPort) by SourceHostName, DestinationIP, DestinationPort
| where ConnectionCount < 5 and DestinationPort !in (443, 80, 22, 53)
| order by ConnectionCount asc;
Velociraptor VQL
For live-response on a suspected GitLab host, this artifact enumerates child processes of GitLab application services and hunts for dropped executables in writable temp locations — the classic footprint of an RCE landing payload.
-- Hunt: GitLab RCE post-exploitation artifacts (CVE-2026-97229 scenario)
-- Part 1: Suspicious child processes of GitLab application services
SELECT Pid, Ppid, Name, Exe, CommandLine, Username, CreateTime
FROM pslist()
WHERE CommandLine =~ '(base64 -d|/dev/tcp/|nc -e|ncat -e|curl -s|wget -q|chmod \+x /tmp|chmod \+x /dev/shm)'
OR (Name =~ '^(sh|bash|dash|python3?|perl|curl|wget|nc|ncat)$'
AND Username =~ 'git')
-- Part 2: Recently created executable files in temp/writable directories on the GitLab host
SELECT FullPath, Size, Mtime, Ctime
FROM glob(globs=['/tmp/**', '/dev/shm/**', '/var/tmp/**'], accessor='file')
WHERE Mtime > (now() - 604800)
AND NOT IsDir
AND FullPath !~ '(bundle|node_modules|cache|\.gem|ruby|prometheus|gitaly)'
ORDER BY Mtime DESC
-- Part 3: Established outbound connections owned by the git user (C2 triage)
SELECT Pid, Name, Laddr, Raddr, Status
FROM netstat()
WHERE Status =~ 'ESTABLISHED'
AND Name =~ '(ruby|puma|sidekiq|sh|bash|python|nc)'
AND Raddr.IP !~ '^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.|127\.)'
Remediation Script (Bash)
Use the following on GitLab omnibus hosts to check the installed version, pull the current package, verify the upgrade, and confirm no unexpected processes are running under the git user. Review and stage before running in production.
#!/bin/bash
# CVE-2026-97229 GitLab verification and patch script (omnibus installations)
# Run as root. Always snapshot/backup before upgrading production GitLab.
set -euo pipefail
echo "=== [1] Current GitLab version ==="
if command -v gitlab-rake >/dev/null 2>&1; then
gitlab-rake gitlab:env:info 2>/dev/null | head -20 || true
fi
dpkg -l gitlab-ee gitlab-ce 2>/dev/null | grep ^ii || \
rpm -qa | grep -i gitlab || echo "[!] GitLab package not found — is this an omnibus host?"
echo ""
echo "=== [2] Backing up GitLab configuration ==="
BACKUP_DIR="/var/opt/gitlab/backups/pre-cve-2026-97229-$(date +%Y%m%d%H%M)"
mkdir -p "$BACKUP_DIR"
cp -a /etc/gitlab/gitlab.rb "$BACKUP_DIR/" 2>/dev/null || true
cp -a /etc/gitlab/gitlab-secrets.json "$BACKUP_DIR/" 2>/dev/null || true
echo "Config backup staged at $BACKUP_DIR"
echo ""
echo "=== [3] Updating GitLab package ==="
# IMPORTANT: Pin to the fixed version per the official GitLab release notes for CVE-2026-97229.
# Check https://about.gitlab.com/releases/categories/releases/ for the exact patched version.
if command -v apt-get >/dev/null 2>&1; then
apt-get update
apt-get install -y gitlab-ce # or: gitlab-ee (use the edition you run; pin version if required)
elif command -v dnf >/dev/null 2>&1; then
dnf install -y gitlab-ce
elif command -v yum >/dev/null 2>&1; then
yum install -y gitlab-ce
fi
echo ""
echo "=== [4] Reconfigure and verify services ==="
gitlab-ctl reconfigure
gitlab-ctl status
echo ""
echo "=== [5] Post-patch compromise checks ==="
echo "--- Processes running as 'git' user (review for shells/downloaders) ---"
ps -u git -o pid,ppid,comm,args | grep -Ev '(puma|sidekiq|gitaly|gitlab-|postgres|redis|exporter|ruby /opt/gitlab)' || echo "None unexpected."
echo ""
echo "--- Recently modified files in temp dirs ---"
find /tmp /var/tmp /dev/shm -type f -mtime -7 -ls 2>/dev/null | head -30 || echo "None found."
echo ""
echo "--- Recently created GitLab users (last 14 days) ---"
gitlab-rails runner 'puts User.where("created_at > ?", 14.days.ago).pluck(:username, :created_at, :sign_in_count)' 2>/dev/null || \
echo "[!] Run manually: gitlab-rails runner 'puts User.where(\"created_at > ?\", 14.days.ago).pluck(:username, :created_at)'"
echo ""
echo "=== Done. Verify patched version against the GitLab advisory for CVE-2026-97229. ==="
gitlab-rake gitlab:env:info 2>/dev/null | head -5
Remediation
1. Patch Immediately — This Is a CVSS 10.0 Under Active Attack Attempts
- Upgrade all self-managed GitLab instances (CE and EE, omnibus, source, and Helm/chart deployments) to the fixed version identified in GitLab's official release notes for CVE-2026-97229. Pull the exact version from the official channels:
- GitLab release posts: https://about.gitlab.com/releases/categories/releases/
- GitLab security releases: https://about.gitlab.com/security-releases/
- Do not wait for a maintenance window for a CVSS 10.0 with observed exploitation attempts. GitLab omnibus upgrades are well-tested; an emergency change is justified.
2. If You Cannot Patch Immediately, Reduce the Attack Surface
The exploit path requires an attacker to get a crafted CI configuration processed. Until patched:
- Disable open registration (Admin Area → Settings → General → Sign-up restrictions). Require admin approval for new accounts.
- Restrict CI to trusted users/groups and disable pipelines on untrusted forks and external merge requests.
- Require approval for new members and audit existing external/guest accounts — Alpine's instance was targeted precisely because community instances accept outside contributors.
- Place the instance behind a VPN or IP allowlist if it does not genuinely need public exposure.
3. Hunt Retroactively — Assume You Were Scanned Too
Alpine's attackers found their target within a week of patch release; your instance was almost certainly enumerated in the same sweep.
- Review GitLab audit logs and Rails production logs (
/var/log/gitlab/gitlab-rails/production_json.log, audit_json.log) for project creation, push, and pipeline events from recently created or dormant accounts in the window between patch release and your upgrade. - Check for unexpected outbound connections from the GitLab host and runner fleet over the same window.
- Rotate credentials if you find any indicator of successful execution: CI/CD variables, deploy tokens, runner registration tokens, registry credentials, and any cloud keys exposed to pipelines. Assume a successful RCE read everything the
gituser can read — which on omnibus includesgitlab-secrets.jsonand the database.
4. Structural Hardening for the Next One
There will be a next one — GitLab publishes critical security releases regularly, and exploit developers diff them within days.
- Isolate CI runners from the GitLab host. Runners should be ephemeral, network-segmented, and unable to reach internal management interfaces or the GitLab database.
- Treat
.gitlab-ci.ymlas untrusted code input (because, per CVE-2026-97229, it literally is). Gate pipeline execution on protected branches and maintainer approval for external contributions. - Deploy EDR on GitLab hosts and alert on application processes spawning shells — this single control would have caught the exploitation attempt regardless of the specific CVE.
- Set a CVSS 10.0 patch SLA of 24–48 hours for internet-facing CI/CD and source-control infrastructure, with automated package monitoring for GitLab security releases.
5. Report and Coordinate
If you identify confirmed exploitation, preserve logs and disk images before rebuilding, and report to your national CERT. Follow CISA guidance and monitor the CISA Known Exploited Vulnerabilities catalog for CVE-2026-97229 — given confirmed in-the-wild attempts on high-value supply-chain infrastructure, KEV listing with a federal remediation deadline is a realistic near-term outcome, and private-sector organizations should hold themselves to the same timeline.
Related Resources
Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub
Is your security operations ready?
Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.