Announcement

👇Official Account👇

Welcome to join the group & private message

Article first/tail QR code

Skip to content

CVE-2026-63077:TeamCity CI/CD 未认证反序列化 RCE 深度分析与供应链防御

漏洞概览

2026 年 7 月 28 日,JetBrains 披露了影响 TeamCity On-Premises 的关键安全漏洞 CVE-2026-63077。该漏洞 CVSS 3.1 评分 9.8(Critical),允许未认证的远程攻击者通过 Agent 轮询协议执行任意操作系统命令。

CISA 在 8 月 5 日将其加入已知被利用漏洞(KEV)目录,联邦民用机构需在 8 月 8 日前完成修复。

这不是一个普通的 CVE。TeamCity 作为企业 CI/CD 基础设施的核心,其被攻破意味着源代码、构建密钥、部署凭据和软件供应链的全部暴露。

漏洞技术分析

Agent 轮询协议:被忽视的攻击面

TeamCity 的构建系统由 Server 和 Build Agent 两部分组成。Build Agent 通过轮询协议(polling protocol)定期向 Server 发送心跳并获取构建任务——这个协议是 Agent 和 Server 之间的通信桥梁。

关键问题在于:Agent 轮询协议的初始握手不需要认证

[Build Agent]                    [TeamCity Server]
     │                                   │
     │── HTTP(S) GET /app/agent/poll ──→│
     │                                   │
     │←── 构建任务 / 空响应 ──────────────│
     │                                   │
     │── POST /app/agent/poll ──────────→│
     │   (携带构建结果/日志)              │
     │                                   │
     │←── ACK ───────────────────────────│

在受影响的版本中,TeamCity Server 在处理 Agent 轮询请求时,对传入的序列化数据执行了不安全的反序列化操作。攻击者可以构造恶意序列化对象,通过轮询端点发送给服务器,在服务器进程中执行任意代码。

反序列化漏洞机制

Java 反序列化漏洞的核心在于:ObjectInputStream.readObject() 在还原对象时会调用对象的 readObject()readResolve() 等方法。如果 classpath 中存在可被链式调用的"gadget class",攻击者可以构造一个序列化对象,使其在反序列化时执行任意代码。

攻击者构造的序列化流

       ├─ Gadget Chain:
       │   ├─ HashMap.readObject()
       │   │   └─ 调用 hashCode()
       │   │       └─ 调用 TemplatesImpl.getOutputProperties()
       │   │           └─ newTransformer()
       │   │               └─ defineClass() ← 注入恶意字节码
       │   │                   └─ Runtime.exec("whoami")

       └─ TeamCity classpath 中存在可用的 gadget classes

TeamCity 作为 Java 企业应用,其 classpath 包含大量第三方库(Spring、Hibernate、各种 Apache Commons 库等),这些库中可能包含可利用的反序列化 gadget。

漏洞利用条件

利用前提:
  1. 目标运行 TeamCity On-Premises(Cloud 版不受影响)
  2. 版本低于 2026.1.3 或 2025.11.7
  3. 攻击者能够通过网络访问 TeamCity 的 HTTP(S) 端口

无需:
  - 认证凭据
  - 有效用户会话
  - 已有的内部立足点
  - 用户交互

这意味着任何互联网暴露的 TeamCity 服务器都处于风险中。攻击者只需发送一个 HTTP 请求即可触发漏洞。

影响范围与修复版本

受影响版本

  • 所有 TeamCity On-Premises 版本(2026.1.3 之前和 2025.11.7 之前)
  • TeamCity Cloud 不受影响(已单独修复)

修复版本

分支修复版本Build 号
2026.x2026.1.3222742
2025.11.x2025.11.7208264

安全补丁插件

对于无法立即升级的组织,JetBrains 提供了兼容 TeamCity 2017.1 及更高版本的安全补丁插件。这是一个临时措施,仅修复此单个 CVE。

发现者

漏洞由安全研究员 Antoni Tremblay 于 2026 年 7 月 10 日报告给 JetBrains。

为什么 CI/CD 漏洞比普通 RCE 更危险

CI/CD 服务器的"钥匙柜"属性

TeamCity 服务器通常存储以下敏感信息:

[TeamCity Server]

  ├─ 源代码访问
  │   ├─ Git 仓库 SSH 密钥
  │   ├─ GitHub/GitLab API Token
  │   └─ 私有仓库认证信息

  ├─ 部署凭据
  │   ├─ 容器镜像仓库凭据
  │   ├─ 云提供商 Access Key(AWS/GCP/Azure)
  │   ├─ Kubernetes 部署凭据
  │   └─ 生产环境数据库连接字符串

  ├─ 构建配置
  │   ├─ 环境变量(含密钥)
  │   ├─ 构建脚本中的硬编码密钥
  │   └─ 第三方服务 API Key

  └─ 构建产物
      ├─ 编译后的二进制文件
      ├─ Docker 镜像
      └─ 签名密钥

供应链攻击路径

攻破 TeamCity 后,攻击者可以:

  1. 篡改构建产物:在编译过程中注入恶意代码,使下游所有部署都携带后门
  2. 窃取源代码:访问所有连接的代码仓库
  3. 获取部署凭据:使用存储的云密钥横向移动到生产环境
  4. 植入持久化后门:在构建流水线中植入隐蔽的恶意步骤
攻击者

  ├─ Step 1: 利用 CVE-2026-63077 获取 TeamCity 服务器 RCE

  ├─ Step 2: 读取服务器配置和凭据存储
  │   ├─ 获取 Git SSH 密钥 → 克隆所有源码仓库
  │   ├─ 获取云 Access Key → 访问生产环境
  │   └─ 获取镜像仓库凭据 → 篡改镜像

  ├─ Step 3: 修改构建配置
  │   └─ 在构建脚本中植入恶意步骤
  │       (如下载后门、修改编译输出)

  ├─ Step 4: 等待自动部署触发
  │   └─ 恶意构建产物被部署到所有环境

  └─ Step 5: 持久化
      └─ 后门随正常部署传播

检测与应急响应

检测是否被利用

bash
#!/bin/bash
# TeamCity CVE-2026-63077 检测脚本

TEAMCITY_HOME="${TEAMCITY_HOME:-/opt/TeamCity}"
LOG_DIR="$TEAMCITY_HOME/logs"

echo "=== TeamCity 版本检查 ==="
# 检查 TeamCity 版本
BUILD_FILE="$TEAMCITY_HOME/buildAgent/conf/buildAgent.properties"
if [ -f "$BUILD_FILE" ]; then
    grep "teamcity.agent.server" "$BUILD_FILE"
fi

echo "=== 检查异常 Agent 轮询请求 ==="
# 搜索异常大的 POST 请求(可能包含序列化 payload)
ACCESS_LOG="$LOG_DIR/teamcity-access.log"
if [ -f "$ACCESS_LOG" ]; then
    # 查找 /app/agent/poll 端点的大请求
    grep "POST.*app/agent/poll" "$ACCESS_LOG" | \
    awk '{if ($10 > 10000) print $0}' | \
    tail -20
fi

echo "=== 检查异常进程执行 ==="
# 查找 TeamCity 进程产生的异常子进程
TEAMCITY_PID=$(pgrep -f "teamcity" | head -1)
if [ -n "$TEAMCITY_PID" ]; then
    # 查找 TeamCity 子进程中的可疑命令
    pstree -p "$TEAMCITY_PID" | grep -iE "bash|sh|python|curl|wget|nc|bash"
fi

echo "=== 检查新增可疑文件 ==="
# 查找 TeamCity 目录中最近 7 天的新文件
find "$TEAMCITY_HOME" -newer "$TEAMCITY_HOME/bin/teamcity-server.sh" \
     -name "*.jar" -o -name "*.sh" -o -name "*.py" 2>/dev/null | \
    head -20

echo "=== 检查网络外连 ==="
# 检查 TeamCity 服务器的异常出站连接
ss -tnp | grep "$TEAMCITY_PID" | grep -vE ":(8080|8443|443|80) "

应急响应清单

python
# CI/CD 漏洞应急响应自动化脚本

incident_response = {
    "immediate": [
        "1. 立即将 TeamCity 从互联网隔离(防火墙规则/安全组)",
        "2. 确认当前版本,与修复版本对比",
        "3. 如果版本受影响:立即升级或安装安全补丁插件",
        "4. 禁用匿名访问",
        "5. 限制 TeamCity HTTP(S) 端口仅允许 Agent 和管理员 IP 访问",
    ],
    "short_term": [
        "6. 审查所有 Agent 轮询日志,查找异常请求模式",
        "7. 检查最近的构建配置变更(是否有未授权的修改)",
        "8. 轮换所有存储在 TeamCity 中的凭据",
        "9. 验证最近的构建产物完整性(与已知良好版本对比)",
        "10. 检查 TeamCity 服务器是否有持久化后门",
    ],
    "long_term": [
        "11. 实施 TeamCity 最小权限原则(服务账户降权)",
        "12. 将 TeamCity 服务器与构建 Agent 网络隔离",
        "13. 部署 WAF 规则检测反序列化攻击模式",
        "14. 建立 CI/CD 变更监控告警(构建配置变更自动通知)",
        "15. 定期进行 CI/CD 安全审计",
    ]
}

for phase, tasks in incident_response.items():
    print(f"\n{'='*20} {phase.upper()} {'='*20}")
    for task in tasks:
        print(f"  {task}")

防御策略:CI/CD 基础设施加固

网络分层

yaml
# CI/CD 网络安全架构
network_architecture:
  internet_facing:
    - component: "Reverse Proxy (nginx/HAProxy)"
    - rule: "仅暴露 Web UI,Agent 轮询端口不对外"

  dmz:
    - component: "TeamCity Server"
    - rule: "仅允许 Agent IP 和管理员 VPN 访问"
    - rule: "Agent 轮询端口(通常 8111)限制为 Agent 网段"

  internal:
    - component: "Build Agents"
    - rule: "Agent 与 Server 通信走专用网络"
    - rule: "Agent 无需公网访问(构建依赖通过内部镜像)"

  secrets:
    - component: "凭据管理服务(HashiCorp Vault/AWS Secrets Manager)"
    - rule: "CI/CD 凭据不存储在服务器本地"
    - rule: "运行时注入,用后即焚"

凭据管理最佳实践

python
# 不要这样做:在构建配置中硬编码凭据
# teamcity-build-config.kts
steps {
    script {
        name = "Deploy"
        scriptContent = """
            export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
            export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
            kubectl apply -f deployment.yaml
        """
    }
}

# 正确做法:运行时从 Vault 获取凭据
steps {
    script {
        name = "Deploy"
        scriptContent = """
            # 从 HashiCorp Vault 动态获取凭据
            export VAULT_ADDR="https://vault.internal:8200"
            AWS_CREDS=$(vault read -format=json aws/creds/deploy-role)

            export AWS_ACCESS_KEY_ID=$(echo $AWS_CREDS | jq -r .data.access_key)
            export AWS_SECRET_ACCESS_KEY=$(echo $AWS_CREDS | jq -r .data.secret_key)

            # 部署完成后凭据自动过期(TTL=1h)
            kubectl apply -f deployment.yaml

            # 撤销凭据
            vault token revoke -self
        """
    }
}

构建产物完整性验证

python
# 构建产物签名与验证流程

import hashlib
import subprocess
from datetime import datetime

class BuildIntegrityManager:
    """构建产物完整性管理"""

    def __init__(self, build_id: str):
        self.build_id = build_id
        self.timestamp = datetime.utcnow().isoformat()

    def sign_artifact(self, artifact_path: str) -> dict:
        """对构建产物签名"""
        # 计算哈希
        sha256 = hashlib.sha256(
            open(artifact_path, 'rb').read()
        ).hexdigest()

        # 使用 cosign 签名
        subprocess.run([
            "cosign", "sign-blob",
            "--key", "env://COSIGN_PRIVATE_KEY",
            artifact_path,
            "--output-signature", f"{artifact_path}.sig",
            "--output-certificate", f"{artifact_path}.cert"
        ], check=True)

        return {
            "build_id": self.build_id,
            "artifact": artifact_path,
            "sha256": sha256,
            "signature": f"{artifact_path}.sig",
            "timestamp": self.timestamp,
            "teamcity_version": "2026.1.3"  # 确保使用修复版本
        }

    def verify_artifact(self, artifact_path: str, expected_sha256: str) -> bool:
        """验证构建产物完整性"""
        actual_sha256 = hashlib.sha256(
            open(artifact_path, 'rb').read()
        ).hexdigest()

        if actual_sha256 != expected_sha256:
            print(f"[ALERT] 产物哈希不匹配!")
            print(f"  Expected: {expected_sha256}")
            print(f"  Actual:   {actual_sha256}")
            return False

        # 验证签名
        result = subprocess.run([
            "cosign", "verify-blob",
            "--key", "env://COSIGN_PUBLIC_KEY",
            "--signature", f"{artifact_path}.sig",
            artifact_path
        ], capture_output=True)

        return result.returncode == 0

2026 年 CI/CD 安全趋势

CI/CD 漏洞正在成为首选攻击目标

2026 年以来,多个 CI/CD 平台曝出关键漏洞:

平台CVECVSS日期
JetBrains TeamCityCVE-2026-630779.82026-07-28
GitLab CE/EECVE-2026-344867.52026-07
JFrog ArtifactoryCVE-2026-65617 等 8 个9.8+2026-07
N-able N-centralCVE-2026-185778.22026-08

CI/CD 系统正成为攻击者的首选目标,因为一次攻破即可影响整个软件供应链。

CISA KEV 的 3 天窗口趋势

值得注意的是,CISA 对 CI/CD 相关漏洞的修复窗口正在缩短。CVE-2026-63077 的联邦修复期限仅为 3 天(8 月 5 日收录,8 月 8 日截止)。这反映了 CISA 对供应链风险的高度重视。

修复指南

升级路径

bash
# 1. 确认当前版本
# TeamCity Web UI → Administration → Diagnostics
# 或检查安装目录
cat /opt/TeamCity/webapps/ROOT/WEB-INF/build-version.txt

# 2. 备份当前配置
/teamcity/bin/maintainDB backup all

# 3. 下载修复版本
# 2026.x 分支 → 2026.1.3 (build 222742)
# 2025.11.x 分支 → 2025.11.7 (build 208264)
# 从 https://www.jetbrains.com/teamcity/download/ 下载

# 4. 安装安全补丁插件(如果无法立即升级)
# TeamCity Web UI → Administration → Plugins List → Install Plugin
# 上传 security-patch-plugin.zip

# 5. 验证修复
curl -s http://localhost:8111/app/rest/server | grep version

临时缓解措施

如果无法立即升级或安装插件:

bash
# 限制 TeamCity HTTP(S) 端口访问
# iptables 示例
iptables -A INPUT -p tcp --dport 8111 -s <agent-ip-range> -j ACCEPT
iptables -A INPUT -p tcp --dport 8111 -s <admin-vpn-range> -j ACCEPT
iptables -A INPUT -p tcp --dport 8111 -j DROP

# 禁用匿名访问
# TeamCity Web UI → Administration → Authentication
# 取消勾选 "Allow anonymous users to access the server"

结语

CVE-2026-63077 再次证明:CI/CD 基础设施的安全态势直接决定了软件供应链的安全态势。一个未认证的反序列化漏洞,可以让攻击者在几分钟内获取从源代码到生产部署密钥的完整访问权。

三个要点值得记住:

  1. Agent 轮询端口是攻击面,不能因为 Web UI 在 SSO 后面就认为服务器是安全的
  2. CI/CD 凭据是高价值目标,应该使用 Vault 等动态凭据管理而非硬编码
  3. 构建产物需要完整性验证,签名和哈希校验是供应链安全的最后一道防线

参考资料

上次更新于: