HAERIN-L/POC_CVE-2026-42880
GitHub: HAERIN-L/POC_CVE-2026-42880
该项目提供了一套可一键拉起的实验环境,用于复现和检测 ArgoCD 中因 ServerSideDiff 功能导致 Kubernetes Secret 数据向只读用户泄露的高危漏洞。
Stars: 0 | Forks: 0
# CVE-2026-42880 — 通过 ServerSideDiff 导致的 ArgoCD Secret 泄露
用于复现和检测 **CVE-2026-42880** 的实验环境。这是 Argo CD 中的一个严重漏洞,其 `ServerSideDiff` gRPC 处理程序会将 Kubernetes Secret 数据泄露给只读用户。
## 漏洞概述
| 字段 | 详情 |
|-------|---------|
| CVE ID | CVE-2026-42880 |
| GHSA | [GHSA-3v3m-wc6v-x4x3](https://github.com/advisories/GHSA-3v3m-wc6v-x4x3) |
| CVSS | 9.6 (严重) — `AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N` |
| 受影响版本 | ArgoCD 3.2.0–3.2.10, 3.3.0–3.3.8 |
| 已修复版本 | 3.2.11, 3.3.9+ |
| CWE | CWE-200, CWE-212 |
### 根本原因
ArgoCD 的 gRPC 处理程序中的 `serverSideDiff()` 调用了 Kubernetes SSA dry-run 并返回了 `predictedLive`,**而没有调用 `hideSecretData()`**,从而在响应中暴露了 base64 编码的 Secret 值。
```
Vulnerable path (v3.2.0):
argocd app diff --server-side-diff
→ gRPC ServerSideDiff handler
→ Kubernetes SSA dry-run (merges ALL field managers)
← predictedLive returned (includes external-controller's data)
❌ hideSecretData() NOT called → real Secret values exposed
Patched path (v3.2.11):
...same SSA dry-run...
✅ HideSecretData() called → values replaced with ++++
```
### 攻击前提条件
必须同时满足以下三个条件:
| # | 条件 | 详情 |
|---|-----------|---------|
| 1 | 存在漏洞的 ArgoCD 版本 | 3.2.0–3.2.10 或 3.3.0–3.3.8 |
| 2 | Application annotation | `argocd.argoproj.io/compare-options: ServerSideDiff=true,IncludeMutationWebhook=true` |
| 3 | Secret 数据上的外部 field manager | Secret `data` 字段由非 ArgoCD manager 管理(例如 External Secrets Operator、Helm、kubectl) |
只需具有 `role:readonly` 权限 —— 无需写权限。
## 实验架构
```
Host Machine
├── localhost:30080 ──→ Kind Cluster: cve-vuln (ArgoCD v3.2.0 ⚠ VULNERABLE)
│ └── ns: production
│ ├── Secret: db-credentials
│ │ metadata → argocd-controller (synced from Git)
│ │ data.* → external-controller ⚠ (injected separately)
│ └── Secret: api-credentials (same setup)
│
├── localhost:30081 ──→ Kind Cluster: cve-patched (ArgoCD v3.2.11 ✓ PATCHED)
│ └── (identical config — only ArgoCD version differs)
│
└── localhost:3010 ──→ Docker Container: cve-lab-gitea
└── repo: gitadmin/manifests.git
└── secret.yaml (no data field — CVE prerequisite)
```
### 为什么 field manager 分离很重要
```
db-credentials Secret (namespace: production)
┌──────────────────────────────────────────────────────────────┐
│ metadata.* → argocd-controller (ArgoCD syncs from Git) │
│ data.* → external-controller (injected by setup script)│
└──────────────────────────────────────────────────────────────┘
SSA dry-run: Kubernetes merges both managers' fields into predictedLive
→ ArgoCD does NOT own data → data is not masked by ArgoCD
→ v3.2.0 returns predictedLive without hideSecretData() → EXPOSED
```
## 前置条件
| 工具 | 安装 |
|------|---------|
| [kind](https://kind.sigs.k8s.io/) | `brew install kind` |
| [kubectl](https://kubernetes.io/docs/tasks/tools/) | `brew install kubectl` |
| [Docker Desktop](https://www.docker.com/) | docker.com |
| [argocd CLI](https://argo-cd.readthedocs.io/en/stable/cli_installation/) | `brew install argocd` |
| [nuclei](https://github.com/projectdiscovery/nuclei) | `brew install nuclei` |
| curl, jq, git | macOS 预装或使用 `brew install jq` |
**资源要求:** 8GB+ 可用 RAM,15GB+ 可用磁盘空间,端口 30080 / 30081 / 3010 可用。
## 如何运行
### 步骤 1 — 设置存在漏洞的环境(ArgoCD v3.2.0)
```
bash scripts/01-setup-vuln.sh
# 或:make setup-vuln
```
大约需要 10 分钟。完成后:
```
══════════════════════════════════════════════════════
Vulnerable ArgoCD lab ready!
══════════════════════════════════════════════════════
ArgoCD UI : http://localhost:30080
Admin pass :
Viewer pass : viewerpass123
Token file : .vuln-viewer-token
══════════════════════════════════════════════════════
```
### 步骤 2 — 设置已修复的环境进行对比(可选)
```
bash scripts/02-setup-patched.sh
# 或:make setup-patched
```
### 步骤 3 — 触发 CVE
```
bash scripts/03-trigger-cve.sh
# 或:make trigger
```
**预期输出 —— 存在漏洞 (v3.2.0):**
```
===== /Secret production/db-credentials ======
< db_password: ++++++++ ← masked live state
---
> db_password: U3VwM3JTM2NyM3REQiFQYXNzIzIwMjY= ← EXPOSED predictedLive!
[EXPOSED] decoded: Sup3rS3cr3tDB!Pass#2026
⚠ RESULT: SECRET DATA EXPOSED — VULNERABLE
```
**预期输出 —— 已修复 (v3.2.11):**
```
> db_password: ++++++++ ← masked
✓ RESULT: no unmasked data in predictedLive — PATCHED
```
### 步骤 4 — Nuclei 检测
```
# Vulnerable cluster → 应产生一个 [critical] finding
nuclei -t nuclei/CVE-2026-42880.yaml \
-u http://localhost:30080 \
-var username=viewer \
-var password=viewerpass123
# Patched cluster → 应不产生任何 finding
nuclei -t nuclei/CVE-2026-42880.yaml \
-u http://localhost:30081 \
-var username=viewer \
-var password=viewerpass123
```
### 步骤 5 — 清理环境
```
bash scripts/99-teardown.sh
# 或:make teardown
```
## 目录结构
```
argocd-cve-2026-42880-lab2/
├── README.md
├── LAB_SETUP_GUIDE.md # Lab setup guide + troubleshooting (English)
├── VULNERABILITY_ANALYSIS.md # Code-level vulnerability analysis (English)
├── Nuclei_Template_Report.md # Nuclei template design and test results (English)
├── Makefile
│
├── REPORT/ # Korean reports
│ ├── LAB_REPORT_KR.md
│ ├── Nuclei_Template_Report_KR.md
│ └── Vulnerability_Analysis_KR.md
│
├── kind/
│ ├── cluster-vuln.yaml # Kind cluster: cve-vuln (port 30080)
│ └── cluster-patched.yaml # Kind cluster: cve-patched (port 30081)
│
├── git-manifests/
│ └── secret.yaml # Secret without data field (CVE prerequisite)
│
├── manifests/
│ ├── application.yaml # ArgoCD Application with vulnerable annotation
│ ├── argocd-cm-patch.yaml # ConfigMap: TLS off, viewer account, ServerSideDiff
│ ├── argocd-rbac-patch.yaml # RBAC: viewer → role:readonly
│ ├── argocd-nodeport.yaml # NodePort 30080 (vuln cluster)
│ └── argocd-nodeport-patched.yaml# NodePort 30081 (patched cluster)
│
├── nuclei/
│ └── CVE-2026-42880.yaml # Nuclei detection template
│
└── scripts/
├── 01-setup-vuln.sh # Full automated setup: vulnerable env
├── 02-setup-patched.sh # Full automated setup: patched env
├── 03-trigger-cve.sh # Trigger CVE + compare both clusters
└── 99-teardown.sh # Remove all lab resources
```
## Nuclei 模板检测逻辑
该模板使用 4 步 HTTP chain 来验证所有 CVE 前提条件,而不会触发实际的 Secret 提取:
```
Step 1 GET /api/version
→ extract argocd_version (no auth required)
Step 2 POST /api/v1/session
→ authenticate as viewer (role:readonly), extract token
Step 3 GET /api/v1/applications
→ find app with ServerSideDiff=true,IncludeMutationWebhook=true
Step 4 GET /api/v1/applications/{app}/managed-resources
→ verify: version in range + Secret present + f:data owned by external manager
→ FINDING reported only if all 5 matchers pass (AND condition)
```
## 参考资源
- [NVD — CVE-2026-42880](https://nvd.nist.gov/vuln/detail/CVE-2026-42880)
- [GHSA-3v3m-wc6v-x4x3](https://github.com/advisories/GHSA-3v3m-wc6v-x4x3)
- [修复 PR #27598](https://github.com/argoproj/argo-cd/pull/27598)
- [ArgoCD Server-Side Diff 文档](https://argo-cd.readthedocs.io/en/stable/user-guide/diff-strategies/#server-side-diff)
- [Kubernetes Server-Side Apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/)
标签:ArgoCD, StruQ, 子域名突变, 安全漏洞复现, 应用安全, 请求拦截, 靶场环境