从 TeamCity 到语义漂移 — CI/CD 平台的认证绕过模式与披露伦理

免责声明:本博客文章仅用于教育和研究目的。提供的所有技术和代码示例旨在帮助防御者理解攻击手法并提高安全态势。请勿使用此信息访问或干扰您不拥有或没有明确测试权限的系统。未经授权的使用可能违反法律和道德准则。作者对因应用所讨论概念而导致的任何误用或损害不承担任何责任。

系列说明:本文是该系列的第二篇,讨论漏洞的利用影响、披露争议,以及将 CVE-2024-27198 放入更广阔的"语义漂移"模式中进行横向比较。技术层面的代码分析、实验环境和发现方法论见第一篇:[[teamcity-cve-2024-27198-discovery]]。

目录

  1. 引言:跳出单个漏洞
  2. 漏洞可利用性与在野利用
  3. 漏洞披露与社区争议
  4. 语义漂移:同类漏洞的横向比较
  5. 五条件审计清单
  6. 结论
  7. 参考与来源

1. 引言:跳出单个漏洞

第一篇还原了 CVE-2024-27198 的发现过程——从不带答案的基线建模,到语义变异、灰盒收敛,再到定向验证。

但如果只把它看成一个孤立事件,就错过了它背后更值得关注的东西。做完这篇分析后我横向翻了翻过去几年类似的认证绕过漏洞,发现它们不是散点,而是同一根因的反复出现:请求在流经多个路径解析器时,语义发生了漂移,而鉴权决定发生在漂移之前。

这篇文章想做的事就是把这条线拉通。先还原漏洞造成的真实影响和披露过程中的争议,再用几个同行案例说明这个模式有多常见,最后给一个可以在审计时直接用的检查清单。

2. 漏洞可利用性与在野利用

2.1 影响能力边界

结合 Rapid7 在 2024-03-04 披露中给出的公开利用样例,漏洞的实际危害可以拆成一条更具体的能力链:

漏洞利用能力链 图2-1 从未认证访问到供应链外溢的能力升级路径。

  1. 未认证调用管理 REST 接口(跨越登录边界) 攻击者可构造类似 /does-not-exist?jsp=/app/rest/...;.jsp 的请求,把本应受保护的 REST 端点暴露给未登录请求。 这一步代表的是直接获得对管理 API 的未认证调用能力

  2. 直接创建管理员账号(权限立刻提升到 SYSTEM_ADMIN) Rapid7 的公开示例显示,攻击者可向 /app/rest/users 发起创建用户请求,并在请求体中赋予 SYSTEM_ADMIN 角色。 返回结果中的用户对象会包含管理员角色字段,这意味着攻击者无需先拿低权限账号,起手就是全局管理员。

  3. 生成管理员访问令牌(持久化控制) 除了创建管理员用户,攻击者还可针对用户 token 接口(如 /app/rest/users/id:1/tokens/...)生成管理员 token。 一旦 token 生成成功,即使后续密码策略变更,攻击者仍可能通过 API 持续访问,形成较强持久化。

  4. 接管 TeamCity 控制面(项目/构建/制品/代理) 在管理员权限下,影响范围不是单一页面,而是整个 TeamCity 控制面:

    • 项目与构建配置修改
    • 构建任务与构建步骤变更
    • 构建代理与制品链路控制
    • 凭据、连接配置与自动化流程滥用
  5. 供应链外溢风险(从 CI/CD 平台扩展到下游) TeamCity 是 CI/CD 中枢,一旦被管理员级接管,风险会外溢到制品发布、部署流程和下游环境,因此该漏洞的危害不止"单机被控",而是可能演化为供应链级事件

2.2 利用复杂度评估

  • 前置门槛:仅需网络可达目标 TeamCity 服务,无需登录认证等权限。
  • 交互要求:无需受害者额外交互。
  • 利用稳定性:在满足语义条件时,利用链可重复触发。

对应 CVSS 9.8(Critical)的根本原因在于:攻击路径短、权限前置低、影响面高。 其典型向量可表述为:CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H,即网络可达、低复杂度、无前置权限、无用户交互且三性高影响。

2.3 在野利用与真实影响

从公开情报看,CVE-2024-27198 并非只是"理论可利用",而是在披露后快速进入规模化利用阶段,体现出其低门槛与高收益特征。

  • 美国 CISA 将其纳入 KEV(Known Exploited Vulnerabilities)目录,并标注"已用于勒索软件":CISA 的 KEV 条目显示该漏洞 Known To Be Used in Ransomware Campaigns: Known,并要求在规定日期前完成处置(联邦机构截止 2024-03-28)。这类信号通常意味着漏洞已进入实战攻击链条且应急优先级很高。

  • 厂商一手"受害者报告":JetBrains 在后续官方文章中表示,在 Rapid7 发布完整技术细节与公开 exploit 示例之后,他们开始收到客户反馈服务器被入侵,并列举了多起代表性案例(包括勒索加密、异常管理员账号创建等)。

  • 披露后数小时至数天内出现规模化扫描与入侵迹象(第三方观测汇总):

    • Shadowserver:最早在 2024-03-04 22:00 UTC 观测到利用尝试;2024-03-05 观测到约 16 个 IP 扫描互联网寻找易受攻击实例(媒体汇总口径)。
    • GreyNoise:追踪到针对该漏洞的利用尝试,观测到数十个恶意 IP 参与活动(媒体引用口径)。
    • LeakIX:在 2024-03-06 前后报告"clear signs of rogue user creation(明显的异常用户创建迹象)";并在 2024-03-07 的媒体报道中被引用为 1,442 个实例存在此类迹象;同一报道还提到其观测到约 1,711 个仍处于易受攻击状态的实例。
  • 安全厂商遥测观测到后渗透载荷投放:Trend Micro 报告称,其遥测观测到攻击者利用 TeamCity 漏洞链在受害服务器上投放多种后渗透载荷,包括勒索软件(Jasmin)、挖矿(XMRig)、Cobalt Strike beacon、SparkRAT 等,并给出了示例网络流量与进程树证据。该类后渗透活动符合"先接管 CI/CD 中枢,再横向与变现"的现实攻击路径。

基于上述情报,可以确认,这个漏洞并不是"理论上很危险",而是"已经被快速武器化并在野利用"的高危漏洞。

3. 漏洞披露与社区争议

该漏洞除了技术层面的典型性外,披露过程本身也很有代表性:它把"公开透明"与"防守窗口"之间的矛盾放到了台面上。

漏洞披露时间线与争议 图3-1 从漏洞报告、修复发布到在野利用确认的披露时间线与争议焦点。

3.1 更完整的披露时间线

结合 JetBrains 与 Rapid7 双方公开信息,可以把链条补得更完整一些(JetBrains 时间线为 CET):

  1. 2024-02-19:Rapid7 首次联系 JetBrains,报告 TeamCity 严重安全问题。
  2. 2024-02-20:Rapid7 提交详细技术报告;JetBrains 当天复现确认。
  3. 2024-02-21 ~ 2024-02-23:双方围绕披露策略出现明显分歧。JetBrains 倾向"先发布修复与补丁、邮件通知客户、提供高层次风险说明,再延后完整技术细节";Rapid7 则坚持修复一旦公开可得,就应尽快同步完整技术披露。
  4. 2024-03-01:双方继续就 CVE 标识、版本影响范围与披露节奏沟通。
  5. 2024-03-04 15:00:JetBrains 发布 2023.11.4(含修复)及安全补丁插件。
  6. 2024-03-04 15:05:JetBrains 向 TeamCity On-Premises 客户和安全订阅用户发送告警邮件。
  7. 2024-03-04 15:59:JetBrains 发布单独安全公告,公开 CVE-2024-27198 / CVE-2024-27199、受影响范围、严重性与缓解方案,但未公开完整 root cause、利用链与复现/利用细节。
  8. 2024-03-04 19:07:JetBrains 将这两个问题加入其公开安全问题页面,并使对应 CVE 记录对外可见。
  9. 2024-03-04 20:23:Rapid7 发布完整技术披露文章与利用细节。
  10. 2024-03-07:CISA 将 CVE-2024-27198 纳入 KEV,确认已进入在野利用态势。

需要特别区分的是,15:59 的动作是"安全公告发布",而不是"完整技术披露";19:07 则更接近"CVE 记录/安全问题页面正式公开"。Rapid7 文中还特别说明,JetBrains 的版本发布日志在不同时区可能显示为 3 月 3 日或 3 月 4 日,这也让"修复版到底何时已公开可得"在传播层面更容易引发争议。

3.2 从披露到在野利用的加速链条

从 SecurityWeek(引用 Shadowserver、GreyNoise、LeakIX)和 CISA 信号看,利用出现速度很快:

  1. 2024-03-04:漏洞细节公开当天即出现利用尝试。
  2. 2024-03-05:Shadowserver 观测到互联网扫描与探测活动上升;GreyNoise 也开始看到多源攻击尝试。
  3. 2024-03-06:LeakIX 报告出现大规模异常账户创建迹象。
  4. 2024-03-07:CISA 将其加入 KEV,风险等级进一步被官方"在野利用"标签确认。

这条链路的意义在于:争议不是停留在理念层,而是直接映射为防守方在最初 24~72 小时内的实战压力。

3.3 争议焦点:不是"有没有公告",而是"公开粒度与时机"

如果只看表面,会出现一个疑问:JetBrains 既然在 2024-03-04 15:59 已经发布了安全公告,Rapid7 为什么仍然会把这一路径描述为接近其所反对的 “silent patching”?关键原因在于,双方对"公开"一词的含义并不相同。

对 Rapid7 而言,问题不在于厂商是否"完全沉默",而在于:

  1. 修复是否先于充分披露对外可见:一旦修复版已发布,具备逆向能力的攻击者就可能通过补丁比对快速理解漏洞;如果厂商此时只给高层次风险说明、而不及时公开足够的技术细节,信息优势会先落到高水平攻击者手里。
  2. 防守方是否获得了足够可操作的信息:从 Rapid7 的视角看,仅有"存在认证绕过风险、请立即升级"这类摘要,并不等于防守者已经拿到与攻击者近似对等的理解能力,因此这种"先修复、后细节"的模式会被归入其反对的 silent patching 范畴。

对 JetBrains 而言,安全公告、CVE 编号、影响范围和缓解措施已经足以构成"负责任通知";真正不应在修复刚上线时同步公开的,是完整 root cause、利用条件与 exploit 细节。因为一旦这些内容在补丁发布同时出现,会显著降低利用门槛,使尚未完成升级的客户暴露在更高风险窗口中。

因此,双方真正冲突的不是"该不该公开",而是:

  1. Rapid7 侧重点:反对"补丁先行、细节后置",认为这会让 skilled attacker 比普通防守者更早获益。
  2. JetBrains 侧重点:反对"修复与完整利用细节同步公开",认为这会把窗口期内的攻击门槛压得过低。

换句话说,Rapid7 所说的 “silent patching” 在这里并不是指 “JetBrains 完全没有发公告”,而是指其反对"先发布修复和高层次公告,再延后完整技术披露"的披露模式;而 JetBrains 则认为,这种延迟 full disclosure 的做法恰恰是为了给客户争取升级与打补丁的缓冲时间。

3.4 本文观点

我个人观点会更偏"分层披露"。说直白一点:Rapid7 反对静默修补并没有错,但这次节奏确实偏激进。 TeamCity 这种全球广泛部署的 CI/CD 枢纽软件,企业侧真实响应要走"感知风险 -> 评估影响 -> 变更审批 -> 生产上线"这整套流程;很多组织在跨时区场景下,补丁发布后最初几小时甚至还没进入有效响应状态。

所以问题不在"该不该公开",而在"什么时候公开到什么粒度"。更现实的路径是:

  1. 先同步高价值防守信息(受影响范围、风险等级、可执行缓解措施、检测线索);
  2. 给一个可落地的升级窗口;
  3. 再发布完整技术细节。

这种"分层+分时"策略,通常更有机会同时兼顾透明性与防守可操作性。

4. 语义漂移:同类漏洞的横向比较

作者注:本节内容不在原始分析草稿中,是在做完 TeamCity 复现后,横向翻阅过去几年类似漏洞时做的归纳。没有现成理论框架可以参考,属于个人观察总结。

如果把 TeamCity CVE-2024-27198 只看成一个孤立事件,就错过了它背后反复出现的一个模式。做完这篇分析后,我横向翻了翻近几年的认证绕过漏洞,发现它们共享同一套根因结构。

4.1 Shiro + Spring:同一出戏,年年返场

Apache Shiro 和 Spring MVC 的组合,可能是这个模式里最高产的一对。从 2020 年开始,几乎每年都有新变种被报出来:

  • CVE-2020-13933%3b(URL 编码的分号)→ Shiro 解码后认为 ; 是路径参数分隔符,匹配不到受限规则 → 请求 pass 给 Spring,Spring 把 %3b 当普通字符 → 命中受限 Controller
  • CVE-2020-17523// 双斜杠 → Shiro 和 Spring 的 normalize 结果不同
  • CVE-2020-1957..; 语义差异
  • CVE-2022-40664:又是 %3b,前一年修完第二年又出新绕过

核心剧情不在于"Shiro 写错了"或"Spring 写错了"——它们各自单看都是按设计干活。问题出在两个组件的路径语言是两种不同的方言:Shiro 对"什么是路径、什么是参数、什么是分隔符"有一套理解,Spring 有另一套。只要两个组件还在各说各话,组合绕过就不会停。修一次往往只堵了一个具体绕过形式而非根因。

和 TeamCity 的类比一目了然:TeamCity 是鉴权层 vs 视图路由层在同一进程内的漂移,Shiro+Spring 是安全框架 vs 业务框架的跨组件漂移。根因结构一样。

4.2 代理层的语义漂移

不光 Java 生态,反向代理和应用服务器之间的路径解析差异也是重灾区:

  • Envoy Proxy OAuth2 Filter 绕过(CVE-2023-27496):Envoy 的 OAuth2 filter 对路径有自己的 normalize 逻辑。当请求为 /public/../admin/secret-api 时,OAuth2 filter 按 /public/... 匹配 → 不触发鉴权放行 → upstream backend 再做一次 normalize → 命中 /admin/secret-api。又是同样的结构:鉴权发生在前一层,执行发生在后一层,中间路径被重新理解了。

  • Nginx + 后端规范化不一致:Nginx 的 merge_slashes 和 normalize 行为,跟 Tomcat、uWSGI、Gunicorn 等后端的路径解析各有差异。这类组合没有统一 CVE 编号(因为是跨产品组合问题而非单一产品漏洞),但在实战中非常常见。核心矛盾很简单:反向代理的路径策略是运维配置的,后端的路径解析是开发框架定的——运维和开发很可能互相不知道对方的"理解"。

  • F5 BIG-IP Auth Bypass(CVE-2023-46747):机制上是 HTTP 请求走私,但根因结构如出一辙——前端对 HTTP 请求的解析结果和后端对同一份请求的解析结果不同,鉴权在前端语义里做,执行在后端语义里做。不是同一类攻击手法,但属于同一个漏洞产生模式。

4.3 共同结构

这些案例横向拉通之后,有一个共同的产生条件,五个条件全部满足 → 极大概率存在可利用的认证绕过:

  1. 请求流经两个或以上的独立路径解析器
  2. 各解析器对同一输入产生不同的输出
  3. 鉴权决定基于较早的解析器的输出
  4. 最终路由/执行基于较晚的解析器的输出
  5. 解析差异可以通过请求构造被稳定控制

这不是某一个产品的 bug,而是现代 Web 架构的一种"默认风险"——只要请求链路里躺着两个以上的路径解析器,且没有统一的 canonical form 约束,语义漂移就是必然的。区别只在漂移窗口的大小和被利用的难易程度。

5. 五条件审计清单

把上面五个条件翻译成审计时可以逐条核对的 checklist:

#检查项怎么确认风险信号
1请求链路中是否存在 ≥2 个路径解析环节?画出请求从入口到最终 handler 的全链路,标出每个"看路径做决定"的节点链路中存在容器级路由、框架级路由、反向代理、WAF 等任意两个以上独立解析器
2这些环节对同一输入是否可能给出不同结果?对每个解析环节单独测试:给它同样的输入,看它输出的"有效路径"是什么两个环节对 //;%2f..\ 等字符的处理策略不同
3鉴权决定发生在哪个环节?确认鉴权过滤器/中间件在链路中的位置,它用的是哪个环节的路径理解鉴权在环节 N,但环节 N+1 对路径的理解不同
4最终 handler 路由发生在哪个环节?确认最终 Controller/Handler 的映射逻辑用的是哪层解析结果最终路由使用与鉴权不同的解析结果
5差异是否可被攻击者稳定构造?尝试构造特定请求,使其在鉴权层看来是 A、在执行层看来是 B能够稳定复现"鉴权放行 + 执行命中受保护端点"的组合

如果用这套清单去反查 TeamCity 漏洞:1(鉴权链 + 视图路由 + 路径规范化)→ 2(分号语义差异)→ 3(鉴权在前)→ 4(路由在规范化之后)→ 5(可通过 .jsp 后缀 + jsp 参数 + ; 分号稳定构造),全部打勾。

这套清单当然不是什么严谨的理论——没有经过大规模统计验证,也没有同行评议。它只是一个基于有限案例的观察归纳。作用不是当定理用,而是下次审计任意 Web 系统时,给你一个可以逐条对照的思考框架,尤其在常规漏洞扫描器扫不出东西的时候。

6. 结论

CVE-2024-27198 的典型性,不仅在于"未认证可达",更在于它揭示了现代 Web 系统里一个经常被低估的问题:安全边界不是由某个单点鉴权函数决定的,而是由整条请求处理链的一致性决定的。

从本文复现与分析过程看,这个漏洞并非传统意义上的"显式越权接口",而是由三层因素叠加形成的语义错位:

  1. 路径外观(.jsp)改变了请求进入的处理链;
  2. 控制器允许请求参数参与视图目标改写;
  3. 路径规范化(尤其分号语义)在不同阶段存在解释差异。

这三者叠加后,系统出现了"鉴权看到一种请求,执行命中另一种请求"的结构性问题。 也正因为如此,这类漏洞的修复重点不应停留在"补一个黑名单规则",而应转向更稳定的工程策略:在统一的 canonical form 上完成鉴权、路由、视图解析与审计,并把跨阶段语义一致性作为安全设计约束。

6.1 一次分析之后的几个感受

写完这两篇文章,心里有几个很具体的感受。

第一,知道答案和重走发现路径,完全是两回事。 读 Rapid7 报告的时候觉得"哦,就是路径解析不一致嘛"。但从零基线开始撞的时候——每一步都不知道下一步往哪走。阶段 B 看到 /does-not-exist.jsp 返回 401 而不是 404 时,我的第一反应是"这有啥?不就是后缀影响了错误处理吗"。直到灰盒阶段看到 jsp 参数直接参与 setViewName(),才意识到这不是小差异。大部分漏洞复现文章只展示最后那条成功的路,不展示中间绕过的弯——但那些弯才是最有信息量的部分。

第二,这个模式比我预想的更普遍。 一开始我只是想深入分析 TeamCity 这一个漏洞。写完第一篇之后不甘心,横着翻了翻 Shiro、Envoy、Nginx 相关的审计资料,发现根因结构高度相似。五个条件对齐之后,这个漏洞不再是"TeamCity 的问题",而是"多解析器架构的问题"。对我自己的启发是:以后审计任何系统,画出路径解析全景图应该是第一步,而不是最后一步。

第三,CI/CD 平台的风险被低估了。 CI/CD 一旦被绕过认证进入管理面,影响对象就不是单台服务,而是项目配置、构建链路、制品分发与下游部署流程。这也是该漏洞被评为 Critical 并在披露后迅速武器化的根本原因。做防御的人不能把 CI/CD 当成"又一个内部系统"来对待——它的风险放大效应是普通应用服务器的好几倍。

6.2 给防守方的实操建议

如果只想带走一句话:下次审计一个 Web 系统,在跑扫描器之前,先把请求链路里每一个"看路径做决定"的节点标出来,问自己:它们看到的是同一个路径吗? 不一致的地方,就是下一步该深挖的地方。


本系列第一篇(技术复现与发现方法论):[[teamcity-cve-2024-27198-discovery]]

7. 参考与来源

TeamCity 漏洞相关

  1. Rapid7 原始漏洞披露声明:
  2. JetBrains 官方安全通报:
  3. JetBrains 官方回应争议的专门文章:
  4. JetBrains 披露时间线总结:
  5. 在野利用证据(政府与安全厂商):

语义漂移同类案例

  1. Apache Shiro + Spring 认证绕过系列:
    • CVE-2020-13933 / CVE-2020-17523 / CVE-2020-1957 / CVE-2022-40664 — 多个独立研究者和厂商 advisory 中有详细技术分析,建议以 NVD 条目和对应的 Apache Shiro 安全公告为入口
  2. Envoy Proxy OAuth2 Filter 绕过:
  3. F5 BIG-IP Auth Bypass: