很多安全团队在审视 DevOps 链路时,往往默认 .gitlab-ci.yml 只是单纯的文本配置,即便存在不可信的分支或 Pull Request,也应当受制于 Runner 的隔离沙箱。然而,被美国网络安全和基础设施安全局(CISA)列入已知被利用漏洞目录(KEV)的 CVE-2026-97229(CVSS 10.0)打破了这种假设:攻击者无需通过身份认证,也无需代码被合并或执行,仅凭包含恶意构造正则表达式的 CI 配置文件,就能在 GitLab 实例的服务端触发内存整数溢出,直接实现远程代码执行(RCE)。

漏洞根因与攻击面分析
CVE-2026-97229 的根本风险在于 GitLab 服务端在解析与编译 .gitlab-ci.yml 的规则匹配语句时,直接调用了底层的 Ruby 正则表达式引擎(Onigmo)。在弱校验场景下,极端恶意的正则表达式会导致编译器发生整数溢出(Integer Overflow)。
在 GitLab CI/CD 语义中,rules:if、only:variables 等指令支持使用 =~ 或 !~ 进行动态条件判断。例如,常见的流水线配置可能会尝试匹配分支名称或外部标签:
# 容易被外部不可信输入借道利用的规则定义示例
job_deploy_staging:
stage: deploy
script:
- echo "Deploying artifacts..."
rules:
# 当 pattern 来自不受控的合并请求或恶意提交时,解析发生在 GitLab 服务器进程内部
- if: '$CI_COMMIT_REF_NAME =~ /^(feat|fix|chore)/.+/'如果 GitLab 实例允许公共项目或开放了注册权限,攻击者仅需向开放仓库提交包含畸形嵌套正则模式的 .gitlab-ci.yml,服务端的解析服务(如 Puma 或 Workhorse 相关的工作进程)在预编译这组模式时便会发生内存崩塌并转入攻击代码。这意味着风险不仅停留在 Runner 容器内,而是直接穿透到 GitLab 服务端核心进程。
阶段一:受影响版本核验与修复范围
根据厂商发布的安全通告与修复基线,官方在以下主要分支发布了修复版本:
- 19.4.2(适用于 19.4.x 分支)
- 19.3.5(以及 19.3.x 相关的安全修补)
- 19.2.8(适用于 19.2.x 分支)
需要注意的是,CVE-2026-97229 的底层修复重点依赖于 GitLab 官方发行包(Omnibus / 官方容器镜像)中打包的受修补 Ruby 环境。
检查当前运行版本
在自建 GitLab 主机上执行以下命令确认运行版本与打包组件状态:
# 适用于 Omnibus 安装包
gitlab-rake gitlab:env:info | grep -E "GitLab Version|Ruby Version"
# 检查当前运行的 Ruby 具体版本
/opt/gitlab/embedded/bin/ruby -v环境依赖边界提示:如果企业使用的是自定义容器镜像或由源码编译(Source Install)构建的 GitLab,仅拉取 GitLab 应用代码更新并不足以彻底修复该漏洞。必须同时确保运行环境的 Ruby 底层正则引擎同步更新,否则服务器依然存在被溢出利用的窗口。
阶段二:CI/CD 仓库配置审计与检测
在完成实例打补丁之前或完成升级后的复核期,团队需要排查代码库中是否存在异常的 CI 配置定义。虽然该漏洞属于解析器层面的底层溢出,但绝大多数攻击载荷会表现为异常过长、深度嵌套括号或大量元字符的正则表达式。
本地与代码库扫描脚本
以下轻量级 Python 脚本用于快速遍历组织内所有 .gitlab-ci.yml 文件,识别具有可疑复杂度、超长边界或深度递归模式的正则字符串:
import os
import re
import yaml
# 设定可疑模式阈值:极深括号嵌套或单行正则超过合理上限
SUSPICIOUS_REGEX_PATTERN = re.compile(r'((?[w-=:]+){3,}|(([^)]{100,}))|(/[^n]{256,}/)')
def audit_ci_file(filepath):
try:
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
# 基础排查规则中的高危正则特征
matches = SUSPICIOUS_REGEX_PATTERN.findall(content)
if matches:
print(f"[!] 发现潜在风险正则配置: {filepath}")
except Exception as e:
print(f"[-] 解析文件失败 {filepath}: {e}")
for root, _, files in os.walk("./repos"):
for file in files:
if file in [".gitlab-ci.yml", "ci.yml"]:
audit_ci_file(os.path.join(root, file))误报判断与规则边界
- 误报场景:某些遗留流水线中包含复杂的语义化版本匹配(SemVer)或路径排除长串,此类正则可能长度超标但并不包含构造的溢出载荷。在审计阻断时应优先分析是否存在大量异常重复字节或不平衡语法,避免直接中断常规构建。
- 配置影响:该脚本仅用作静态审计,不得直接挂载为不可逆的拦截脚本,建议以“只读告警模式”先行运行于自建安全门禁中。

阶段三:临时缓解边界与安全门禁加固
如果因生产变更窗口限制无法立刻执行停机热升级,可实施以下网络层与业务策略的临时缓解措施:
- 关闭公网自由注册:进入
Admin Area -> Settings -> General -> Sign-up restrictions,取消勾选Sign-up enabled。杜绝攻击者使用新注册账号推送恶意分支。 - 限制外部公开项目创建:将默认项目可见性下调为
Internal或Private,阻断未认证请求直接向外部暴露分支推送流水线。 - WAF 规则临时防护:在 GitLab 反向代理(如 Nginx 或外置 WAF)层针对向 CI Lint 接口(
/api/v4/ci/lint)提交包含超长正则表达式字段的 HTTP Payload 进行深度检测并设限。
需要明确指出:WAF 无法完整覆盖 Git 协议(SSH / Git-over-HTTPS)推送触发的配置解析,网络过滤仅是降低外部扫描探测的临时止血手段,不能替代系统级补丁。
阶段四:验证、回归测试与回滚步骤
完成 GitLab 升级至 19.4.2、19.3.5 或 19.2.8 后,需要通过闭环流程验证业务与安全基线。
1. 功能回归验证
升级后,必须立刻验证现有正常的 CI/CD 流水线不会受到正则编译器变更的影响:
- 选取包含多分支构建、
rules:changes及常规环境变量正则匹配(如if: '$CI_COMMIT_TAG =~ /^vd+.d+.d+$/')的核心项目。 - 触发全量流水线,确认 GitLab 服务端工作进程(Puma)内存占用平稳,未出现进程反复重启(SIGSEGV / SIGABRT)的现象。
2. 系统异常排查
通过排查系统日志确认是否在升级前已被探测:
# 检查 Puma 异常崩溃或退出日志
grep -E "segfault|SIGSEGV|Worker.+died" /var/log/gitlab/puma/current
# 检查 API CI 校验端点的可疑请求
grep "/api/v4/ci/lint" /var/log/gitlab/nginx/gitlab_access.log | grep " 500 "3. 回滚方案与灾备边界
如果在升级至目标修复版本后发生应用服务无法启动或兼容性崩溃,按以下规程执行受控回滚:
- 保留故障现场:备份
/var/log/gitlab下的所有崩溃转储与日志,以供后续分析。 - 降级与配置恢复:
# 以 Ubuntu/Debian 环境 Omnibus 包为例降级到备份版本
apt-get install gitlab-ee=<降级前指定版本> --allow-downgrades- 恢复数据库与元数据:由于 GitLab 跨小版本(尤其是带底层 DB Schema 迁移的版本)降级不支持直接回退运行,必须从升级前创建的热备镜像或全量备份文件执行恢复:
gitlab-backup restore BACKUP=<升级前备份时间戳>
gitlab-ctl reconfigure
gitlab-ctl restart- 回退后的敞口处置:如果系统被迫回滚至受影响版本,必须立即将实例下线进入维护模式,或者切断公网访问,仅保留可信内网 Bastion 主机访问,直至制定新的补丁发布计划。
- CI/CD
- it2021
- it2021.com
- nginx
- tag
- 代码执行
- 网络安全
- 远程代码执行





上海 上海 1F
未认证就能打服务端,太狠了