Threat Overview
Alpine Linux disclosed on October 28, 2026 that an attacker submitted crafted GitLab CI configuration regular expressions on October 20–21, causing eight worker crashes and four partially progressed exploit attempts. The critical flaw (CVSS 10.0) is an integer-overflow issue in the Onigmo regular-expression compiler embedded in Ruby, reachable through GitLab CI configuration processing. Alpine did not find evidence of code execution but could not exclude compromise due to missing logs. The key new exposure lesson is that custom GitLab builds using standard Ruby may remain vulnerable even after the GitLab application version is updated.
References
Alpine Linux incident report, 2026-10-28
GitLab critical patch release, 2026-10-12
Impacted Systems
Self-managed GitLab Community Edition and Enterprise Edition 19.2 before 19.2.8, 19.3 before 19.3.5, and 19.4 before 19.4.2, on Linux servers/containers using vulnerable Ruby/Onigmo. GitLab official fixed packages bundle patched Ruby; source-built/custom images must also use Ruby 3.3.13, 3.4.12 or 4.0.9 (released October 23) or a verified vendor backport. Alpine alpinelinux/gitlab:19.3.6 images pulled before October 27 were vulnerable; repull the October 27 rebuilt image. GitLab.com and GitLab Dedicated were reported patched. CI lint and CI configuration processing are the relevant application surfaces; attacker authentication is not inherently required per GitLab advisory.
Why this matters
GitLab commonly stores source code, CI/CD secrets, runner tokens and deployment credentials. A self-managed build may look patched by GitLab version but retain a vulnerable Ruby runtime; crash and HTTP correlation are useful early indicators, while endpoint behavior can reveal successful exploitation.
Exploitation Status
Confirmed exploitation attempts at Alpine Linux (October 20–21), with worker crashes. Successful remote code execution at Alpine was not confirmed. GitLab rates the flaw CVSS 10.0 and describes unauthenticated remote code execution under certain conditions. Alpine rebuilt the server because it could not rule out compromise.
What this hunt looks for
The layered hunts below cover: GitLab Onigmo/Ruby worker crash indicators; GitLab CI lint and GraphQL endpoint activity; GitLab CI lint burst with blocked or errored requests; GitLab worker crash and HTTP 502 correlation by host; Ruby or GitLab spawning shells and execution utilities; GitLab Ruby process reaching rare external destinations; GitLab/Ruby process staging executable files; GitLab proxy 502 and worker errors over short windows.
Required logs
Depending on the hunt: CommonSecurityLog, DeviceFileEvents, DeviceNetworkEvents, DeviceProcessEvents, Syslog. Only ingested Sentinel tables can be searched; appliance-specific logs must be forwarded and parsed to be visible.
Hunt 01: GitLab Onigmo/Ruby worker crash indicators
Required: Syslog. First pass for known Alpine-like failed exploitation. A successful exploit may leave no crash; regexp alone is noisy.
Syslog
| where TimeGenerated >= ago(30d)
| where SyslogMessage has_any ('gitlab','ruby','puma','sidekiq','onigmo','regexp')
| where SyslogMessage has_any ('segfault','segmentation fault','core dumped','crashed','integer overflow','regexp')
| project TimeGenerated, Computer, ProcessName, SyslogMessage
| order by TimeGenerated desc
Hunt 02: GitLab CI lint and GraphQL endpoint activity
Required: CommonSecurityLog. Requires URL path logging from a reverse proxy/WAF in CommonSecurityLog; GraphQL alone is common and not exploit-specific.
CommonSecurityLog
| where TimeGenerated >= ago(30d)
| where RequestURL has_any ('/api/v4/ci/lint','/graphql')
| summarize Requests=count(), FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated), Urls=make_set(RequestURL,5), Actions=make_set(DeviceAction,5) by SourceIP, DestinationIP, bin(TimeGenerated,1h)
| where Requests >= 10
| order by Requests desc
Hunt 03: GitLab CI lint burst with blocked or errored requests
Required: CommonSecurityLog. WAF actions and HTTP response codes are vendor-specific; the query uses documented CommonSecurityLog columns only.
CommonSecurityLog
| where TimeGenerated >= ago(30d)
| where RequestURL has_any ('/api/v4/ci/lint','/graphql')
| where DeviceAction has_any ('block','deny','drop','reset') or Message has_any ('502','500','403','regexp','error')
| summarize Events=count(), Actions=make_set(DeviceAction,5), Messages=make_set(Message,3) by SourceIP, DestinationIP, bin(TimeGenerated,15m)
| where Events >= 3
| order by Events desc
Hunt 04: GitLab worker crash and HTTP 502 correlation by host
Required: Syslog. Only correlates if application and reverse-proxy errors share the same Computer value.
let crashes=Syslog
| where TimeGenerated >= ago(30d)
| where SyslogMessage has_any ('gitlab','puma','sidekiq','ruby','onigmo') and SyslogMessage has_any ('segfault','crash','core dumped')
| summarize CrashCount=count() by Computer, Window=bin(TimeGenerated,30m);
let errors=Syslog
| where TimeGenerated >= ago(30d)
| where SyslogMessage has_any ('gitlab','nginx','workhorse') and SyslogMessage has_any ('502','bad gateway')
| summarize ErrorCount=count() by Computer, Window=bin(TimeGenerated,30m);
crashes | join kind=inner errors on Computer, Window
| project Computer, Window, CrashCount, ErrorCount
| order by Window desc
Hunt 05: Ruby or GitLab spawning shells and execution utilities
Required: DeviceProcessEvents. High signal on GitLab application servers but CI jobs may legitimately spawn tools; scope by server and parent chain.
DeviceProcessEvents
| where TimeGenerated >= ago(30d)
| where InitiatingProcessFileName in~ ('ruby','ruby.exe','puma','sidekiq','gitlab-workhorse')
| where FileName in~ ('sh','bash','dash','zsh','curl','wget','python','python3','perl','nc','ncat','socat')
| project TimeGenerated, DeviceName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, AccountName
| order by TimeGenerated desc
Hunt 06: GitLab Ruby process reaching rare external destinations
Required: DeviceNetworkEvents. GitLab has legitimate integrations; a rare destination is a lead, not compromise evidence.
let baseline=DeviceNetworkEvents
| where TimeGenerated between (ago(30d)..ago(2d))
| where InitiatingProcessFileName in~ ('ruby','ruby.exe','puma','sidekiq','gitlab-workhorse')
| summarize by DeviceName, RemoteIP;
DeviceNetworkEvents
| where TimeGenerated >= ago(2d)
| where InitiatingProcessFileName in~ ('ruby','ruby.exe','puma','sidekiq','gitlab-workhorse')
| where isnotempty(RemoteIP) and ipv4_is_private(RemoteIP) == false
| join kind=leftanti baseline on DeviceName, RemoteIP
| summarize Connections=count(), Commands=make_set(InitiatingProcessCommandLine,3) by DeviceName, RemoteIP, RemotePort
| order by Connections desc
Hunt 07: GitLab/Ruby process staging executable files
Required: DeviceFileEvents. Application deploys and CI activity can write files; prioritize unexpected files outside known runner workspaces.
DeviceFileEvents
| where TimeGenerated >= ago(30d)
| where InitiatingProcessFileName in~ ('ruby','ruby.exe','puma','sidekiq','gitlab-workhorse')
| where FolderPath has_any ('/tmp/','/var/tmp/','/dev/shm/')
| where FileName endswith '.sh' or FileName endswith '.so' or FileName endswith '.elf' or FileName endswith '.py'
| project TimeGenerated, DeviceName, ActionType, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine
| order by TimeGenerated desc
Hunt 08: GitLab proxy 502 and worker errors over short windows
Required: Syslog. Bursts can be ordinary deployment failures or overload; corroborate with targeted CI lint requests and process crashes.
Syslog
| where TimeGenerated >= ago(30d)
| where SyslogMessage has_any ('gitlab','workhorse','nginx','puma','sidekiq')
| where SyslogMessage has_any ('502','Bad Gateway','segfault','core dumped','worker')
| summarize Events=count(), Samples=make_set(SyslogMessage,3) by Computer, bin(TimeGenerated,10m)
| where Events >= 5
| order by Events desc
Detection Notes
Highest signal: Ruby/Onigmo worker crashes correlated with CI lint activity and unexpected Ruby child shells. A successful exploit may not crash and can leave no clear HTTP trace. Normal GitLab builds, runner jobs and integrations can spawn shells and connect externally; scope endpoint hunts to the GitLab server process and exclude approved runner workloads. CommonSecurityLog requires actual URL-path capture, not just flow metadata. Syslog requires GitLab/Workhorse/Puma logs forwarded; Defender process/file/network hunts require endpoint telemetry ingested into Sentinel. Do not treat absence of a crash as proof of safety.
This is a detection reference, not a claim that a customer workspace was queried. Missing Sentinel telemetry and retention can conceal activity.