应对 GitLab CI/CD 正则表达式导致的未认证 RCE(CVE-2026-97229):从版本核验到流水线配置审计与回滚

枫少@KillBoy
枫少@KillBoy
枫少@KillBoy
管理员
367
文章
0
粉丝
信息安全1 35字数 1989阅读6分37秒阅读模式
AI智能摘要
GitLab CI/CD 配置文件被默认视为安全隔离文本,但 CVE-2026-97229 彻底颠覆了这一假设。攻击者无需认证,仅凭恶意构造的正则表达式即可触发服务端内存溢出,直接实现远程代码执行。该高危漏洞不仅穿透容器沙箱,更直击核心进程,迫使团队从版本核验、配置审计到网络加固进行全链路排查。当常规 WAF 无法覆盖底层协议风险时,如何在生产环境实施精准的临时缓解与回归验证,以避免业务中断?
— AI 生成的文章内容摘要

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

GitLab CI 正则表达式解析触发服务端漏洞流程示意

漏洞根因与攻击面分析

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)或路径排除长串,此类正则可能长度超标但并不包含构造的溢出载荷。在审计阻断时应优先分析是否存在大量异常重复字节或不平衡语法,避免直接中断常规构建。
  • 配置影响:该脚本仅用作静态审计,不得直接挂载为不可逆的拦截脚本,建议以“只读告警模式”先行运行于自建安全门禁中。
DevSecOps 团队进行 CI 配置审计与升级验证

阶段三:临时缓解边界与安全门禁加固

如果因生产变更窗口限制无法立刻执行停机热升级,可实施以下网络层与业务策略的临时缓解措施:

  1. 关闭公网自由注册:进入 Admin Area -> Settings -> General -> Sign-up restrictions,取消勾选 Sign-up enabled。杜绝攻击者使用新注册账号推送恶意分支。
  2. 限制外部公开项目创建:将默认项目可见性下调为 Internal 或 Private,阻断未认证请求直接向外部暴露分支推送流水线。
  3. 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. 回滚方案与灾备边界

如果在升级至目标修复版本后发生应用服务无法启动或兼容性崩溃,按以下规程执行受控回滚:

  1. 保留故障现场:备份 /var/log/gitlab 下的所有崩溃转储与日志,以供后续分析。
  2. 降级与配置恢复:
   # 以 Ubuntu/Debian 环境 Omnibus 包为例降级到备份版本
   apt-get install gitlab-ee=<降级前指定版本> --allow-downgrades
  1. 恢复数据库与元数据:由于 GitLab 跨小版本(尤其是带底层 DB Schema 迁移的版本)降级不支持直接回退运行,必须从升级前创建的热备镜像或全量备份文件执行恢复:
   gitlab-backup restore BACKUP=<升级前备份时间戳>
   gitlab-ctl reconfigure
   gitlab-ctl restart
  1. 回退后的敞口处置:如果系统被迫回滚至受影响版本,必须立即将实例下线进入维护模式,或者切断公网访问,仅保留可信内网 Bastion 主机访问,直至制定新的补丁发布计划。

 
枫少@KillBoy
  • CI/CD
  • it2021
  • it2021.com
  • nginx
  • tag
  • 代码执行
  • 网络安全
  • 远程代码执行
    • 鹰隼锐锐
      鹰隼锐锐 1

      未认证就能打服务端,太狠了

    匿名

    发表评论

    匿名网友

    拖动滑块以完成验证