2026年11月2日星期一
漏洞

GitLab CVSS 10 正则RCE列入CISA KEV,Alpine遭攻击

CISA将一个隐藏在Ruby正则引擎中的CVSS 10 GitLab远程代码执行漏洞列入KEV,Alpine的险情表明仅升级GitLab未必足够。

机械计数器溢出并碎裂的插图,象征正则引擎中的整数溢出
越过上限回绕的整数,就是引发雪崩的那颗石子,这里它位于Ruby的正则引擎内部。

CISA已将CVE-2026-97229加入其已知被利用漏洞目录(KEV):这是GitLab社区版和企业版中的一个CVSS 10.0整数溢出漏洞,未经身份验证的攻击者只需在CI/CD配置中放入一段精心构造的正则表达式,即可在服务器上执行任意代码。联邦机构须在11月1日前完成修复。在有人把它归入“未经证实的理论风险”之前,请注意:这并非纸上谈兵。10月间,有人花了将近一周时间试图针对Alpine Linux项目自己的GitLab服务器利用这一漏洞,险些得手,最终迫使Alpine将该实例整体推倒重建。

针对gitlab.alpinelinux.org的攻击,是目前关于此漏洞最具启发性的公开事后分析,Alpine公开发布时保留了未知项,值得称赞。简而言之:该漏洞利用已真实到足以针对特定目标构建进行调校;升级GitLab版本未必能解决问题;而被攻陷的GitLab实例,其影响范围可能是你的整个软件供应链。下面逐一分析这三点。

漏洞:距离代码执行只差一条正则表达式

这一原语是披着现代外衣的经典手法。GitLab使用Ruby编译CI配置中的正则表达式,而Ruby的正则引擎(Onigmo,一个内嵌于解释器中的C语言库)存在整数溢出或回绕问题。输入一个构造好的模式后,编译过程中的运算会越过边界回绕,在一个正在解析攻击者可控输入的进程中破坏内存。CISA的KEV条目对结果描述得很直白:正则表达式校验不足,导致GitLab服务器上未经身份验证的任意代码执行。这与我们在Brotli解码器RCE一文中剖析的漏洞同属一类:C语言数据处理库中一个溢出的整数,整个进程随之落入攻击者之手。

攻击链短得令人尴尬,这也正是CVSS 10.0名副其实而非夸大的原因。在允许注册的GitLab实例上注册一个账户;推送一个包含恶意正则的.gitlab-ci.yml仓库。GitLab的工作进程为了检查和运行流水线而编译该配置,Onigmo随即出错,内存被破坏,攻击者便在服务器本身的工作进程上下文中获得代码执行。无需凭据、无需会话、无需社会工程学,CI解析器本身就是攻击面。如果你的实例允许陌生人注册并触发流水线,那么你的未认证暴露面就是注册表单。

第一项暴露面检查不花一分钱,却能回答真正关键的问题:究竟是哪个Ruby在负责编译:

$ ruby -v
ruby 3.3.12        # vulnerable — you need 3.3.13, 3.4.12, 4.0.9 or later
# On Alpine hosts, the distro package already carries the fix:
$ apk upgrade        # 3.21 ships Ruby 3.3.13; 3.22/3.23/3.24/edge ship 3.4.12

八次崩溃、四次险些得手,一台被重建的服务器

据Alpine Linux的事件报告,GitLab于10月12日发布了修复,CVE于10月13日公开,而当天Alpine原本打算升级,却误装了19.3.4而非已修复的19.3.5,这一失误Alpine在时间线中已坦承。一周后,即10月20日,一个新注册的账户开始探测仍存在漏洞的服务器。在10月20日和21日期间,攻击者推送的构造CI配置使GitLab工作进程崩溃了八次;21日的主要攻击还引发了502错误并终止了CI任务。Alpine表示,其中有四次崩溃中,攻击者已完成部分利用流程,但在下一步失败。

最值得每位防御者警觉的细节是:攻击者在行动期间下载了Alpine自家的GitLab容器镜像。这不是广撒网式扫描,而是有人在搭建目标的本地复刻,以校准偏移量和载荷行为。Alpine对此的判断,经The Hacker News引述如下:

“这表明他们正在针对我们的确切构建版本调校漏洞利用,”Alpine在谈及攻击者于攻击途中下载其GitLab容器镜像的举动时表示。

Alpine于10月23日封禁了该账户,同一天上游Ruby在3.3.13、3.4.12和4.0.9中发布了修复,但攻击者于10月25日和26日卷土重来,试图重新进入,均未成功。10月26日,Alpine有了一项发现,彻底改变了事件的性质:尽管服务器已于10月22日升级到GitLab 19.3.6,它仍然存在漏洞。Alpine于27日将GitLab的访问范围收窄至仅开发人员和构建服务器,随即启动全面重建,并于28日完成,期间所有密码均被重置,GitLab的所有密钥也全部更换。Alpine未发现攻击者的代码曾经运行的证据,但表示无法排除这一可能,并仅凭此就将该实例视为已被攻陷。这是正确的判断,也是大多数组织会说服自己放弃的判断。

没打上的补丁:你的GitLab版本在骗你

陷阱就在这里,也是本次事件中影响面最广的部分。GitLab 10月12日的修复并非对GitLab自身代码的修改,而是对GitLab打包进官方Linux软件包和容器镜像中的Ruby所做的补丁。如果你是从这些官方制品安装并升级的GitLab,那么你已经受到保护。但如果你的GitLab运行在自行管理的Ruby之上(无论是从源码编译,还是像Alpine那样嵌入自定义容器镜像),那么升级GitLab毫无作用。存在漏洞的组件位于你刚刚打好补丁的应用之下。Alpine将其升级到19.3.6,在四天里看似完成了修复,而大门其实一直敞开着。

Alpine发布修复时还有第二个更隐蔽的陷阱。其alpinelinux/gitlab:19.3.6镜像在10月27日之前发布的版本使用的是标准Ruby,存在漏洞;而同一标签在10月27日重新发布的版本则包含Ruby 3.3.13。Alpine的指示是重新拉取alpinelinux/gitlab:19.3.6,并将10月27日之前拉取的所有镜像以及所有旧标签视为存在漏洞。对安全修复复用可变标签,意味着任何按标签固定版本或使用缓存拉取的人,都可能在误以为已打补丁的情况下运行着存在漏洞的构建。应核对摘要和拉取日期,而非标签名称。再往外看一层:该漏洞位于Ruby的正则引擎中,因此任何从不可信输入编译正则表达式的程序都有暴露风险,而不仅限于GitLab。你的其他Ruby服务也值得本周就同样的版本问题进行排查。

旧密钥和令牌被熔毁、新凭据被重新锻造的插图,象征凭据轮换
当GitLab服务器可能已被攻陷时,打补丁只是小头,重置它所保存过的一切,才是真正的代价。

一台被攻陷的GitLab服务器究竟值多少

Alpine的报告中包含了事件披露中少见的内容:一份明确列出的清单,说明如果攻击得手,哪些数据会暴露。把它当作你自己实例的检查清单来读很有价值:所有用户的电子邮件地址、姓名和IP地址;密码哈希;双因素认证密钥;CI/CD变量和Runner令牌;Webhook、集成和推送镜像凭据;个人访问令牌(以哈希形式存储);私有仓库;以及机密议题和合并请求。请以攻击者的视角再读一遍这份清单。Runner令牌和CI/CD变量是你构建基础设施的钥匙;Webhook和推送镜像是通往GitLab所连接的每个系统的路径;私有仓库则是产品本身。因此,对于疑似被攻陷的GitLab,其修复成本的计算方式不是“打补丁”,而是“全部轮换”。Alpine本身也更换了GitLab的密钥并重置了所有密码,但正如The Hacker News所指出的,它尚未说明Runner令牌、CI/CD变量、访问令牌或集成凭据是否也已轮换。

有两项架构决策让Alpine免于一个糟糕得多的星期。第一,签署Alpine软件包和发布版本的密钥从未存放在GitLab服务器上,因此,那个本可能使事件升级为发行版级供应链危机的核心机密,根本无处可偷。第二,验证并不依赖于可能已被攻陷的系统:10月20日至22日期间的每一次aports提交,都是由已知开发人员通过有记录的推送提交的,并与攻击者无法触及的记录进行比对,包括开发者本地克隆副本和全部45台构建服务器;从10月20日至24日构建的每个软件包,以及10月21日发布的版本(3.21.9、3.22.7、3.23.7、3.24.3,包括官方Docker镜像所依赖的minirootfs压缩包),均可追溯到git.alpinelinux.org上公开发布的提交。Alpine将校验延长至10月27日(服务器关闭之日)。信任,但要核验,而核验的锚点位于爆炸半径之外。

检测如同掷硬币,因此要追查崩溃并修复日志

现在是令人不安的部分。Alpine之所以能察觉这次攻击,完全是因为漏洞利用一直在失败。八次工作进程崩溃、502错误和失效的CI任务,就像一个灵敏度欠佳的烟雾报警器,但成功的漏洞利用是无声的,Alpine对此直言不讳:

“成功的漏洞利用不会留下崩溃,也不会在Web日志中留下痕迹,”Alpine在解释为何尽管未发现代码执行的证据仍重建服务器时表示。

更糟的是,一些本可以厘清问题的证据已经不复存在:后台任务日志和旧容器的文件系统都已丢失,而Alpine及其托管服务商均未记录出站网络连接,因此,即便是成功的数据外泄,事后看来也是不可见的。如果你从这次事件中只能带走一个经验教训,那就是这一条。你无法对从未收集过的遥测数据进行追溯调查。

具体来说,以下是我会在本周执行的操作顺序,与Alpine在自身应对中所做的改进相呼应:

  • 确认GitLab底层的Ruby为3.3.13、3.4.12、4.0.9或更高版本,尤其是源码编译和自定义镜像的环境,单靠升级GitLab会让漏洞依然存在。
  • 排查工作进程崩溃日志、502峰值以及失败的CI任务,并与近期注册的账户和新推送的CI配置进行关联;将重复崩溃视为利用尝试,而非普通的不稳定。
  • 如果无法排除代码执行的可能,请轮换完整清单:Runner令牌、CI/CD变量、个人访问令牌、Webhook、集成和推送镜像凭据,以及用户密码。
  • 在GitLab主机上启用出站连接和数据库日志,并将保留期延长至14天以上,Alpine的缺口在于完全没有出站流量日志。
  • 永久将软件包和发布签名密钥保留在GitLab服务器之外,并对发布标签进行签名,使制品无需依赖GitLab即可独立验证。
  • 如果你运行alpinelinux/gitlab镜像,请重新拉取19.3.6;10月27日之前拉取的镜像以及所有旧标签均存在漏洞。

CISA将该漏洞列入KEV,这是继Arista VeloCloud SQL注入之后,第二个与CI/CD相关、需要立即响应的条目,并为联邦机构设定了11月1日的截止期限。Alpine事件正是其他机构也应同步加快节奏的典型例证。已确认的事实是:该漏洞是CVSS 10.0的未认证远程代码执行,修复已在GitLab官方软件包和上游Ruby中发布,并且有真实攻击者针对一个高价值开源项目发起了执着、针对目标调校的利用尝试。尚属声称的是:Alpine未发现代码执行的证据,也未发现软件包或发布版本被篡改,并且这一结论是通过被攻陷系统之外的记录验证的。仍然未知、且可能永远未知的是:那四次部分成功的尝试中,或某次更隐蔽的尝试,是否最终完成了整条攻击链。Alpine应对这一不确定性的方式,是重建一切并重置每一个凭据。如果你的GitLab保存着源代码、流水线机密和签名相关的基础设施,那么这并非偏执,而是入场的代价。

来源

本文参考了以下报道与公告: