🎯 CVE-2026-41050 深度技术分析:漏洞根因 · PoC/EXP · 检测指纹
CVE-2026-41050 深度技术分析
CVE-2026-41050 Fleet Helm Deployer 服务账户模拟绕过漏洞深度分析
摘要:CVE-2026-41050 是 Rancher Fleet 组件中一个严重的服务账户(ServiceAccount)模拟绕过漏洞,CVSS 评分为 9.9。由于 Fleet 的 Helm deployer 在两条代码路径上没有完全应用服务账户模拟,导致拥有 Fleet 监控仓库 git push 权限的租户可以读取每个下游集群任意命名空间中的 Secret。漏洞利用无需集群凭据,仅需向 Fleet 托管的 Git 仓库提交恶意 chart 或修改 fleet.yaml,即可在 Helm 模板渲染阶段或 values 加载阶段触发越权读取。
📌 漏洞概述
CVE 编号:CVE-2026-41050
CVSS 评分:9.9(Critical)
GitHub Advisory:GHSA-765j-qfrp-hm3j(CRITICAL)
受影响产品:Rancher Fleet(go/github.com/rancher/fleet)
漏洞类型:CWE-284 不正确的访问控制(服务账户模拟绕过)
CISA KEV:未收录(暂无已利用漏洞列表)
公开 PoC/EXP:暂无
Fleet 是 Rancher 生态中用于 GitOps 持续交付的组件,能够将 Git 仓库中的 Helm chart 部署到多个下游集群。在多租户场景中,Fleet 通过 ServiceAccount impersonation 限制每个租户(Tenant)在下游 Kubernetes 集群中的操作权限,使租户只能使用授权给其服务账户的 RBAC 权限。该机制是 Fleet 多租户隔离边界的关键。CVE-2026-41050 则表明这一边界在 Helm deployer 内部并未被完全执行,逃逸到两条辅助代码路径上,形成高严重性越权读取漏洞。
🔬 漏洞根因分析
Fleet 的 Helm deployer 在安装或升级 Helm Release 时,会为每个 GitRepo 创建对应的 Helm Action,同时使用 WithImpersonationConfig 设置要模拟的服务账户,以控制 Helm 在集群内执行 API 操作的权限。但问题在于,Helm 的某些操作并不是发生在同一个 action 上下文中,而是存在独立调用链。
漏洞根因具体体现在两条路径:
路径一:Helm lookup 函数绕过
Helm chart 模板引擎提供内置的 lookup 函数,模板可借此在渲染时查询 Kubernetes 资源。该函数需要调用 Kubernetes API。在存在漏洞的实现中,Helm 模板引擎使用的客户端并非经过 WithImpersonationConfig 配置的客户端,而是直接复用了 fleet-agent 自身的高权限 kubeconfig 凭证(通常是 cluster-admin)。于是,chart 模板中只需要写 {{ lookup "v1" "Secret" "kube-system" "some-token" }},就能以 fleet-agent 的身份读取任何命名空间的 Secret,完全绕过了租户 RBAC 的限制。
路径二:fleet.yaml 中 helm.valuesFrom 绕过
Fleet 允许用户通过 fleet.yaml 的 helm.valuesFrom 引用 Secret 或 ConfigMap,并将其内容作为 Helm values 注入。在实现中,这个引用解析过程使用了一个独立的 Kubernetes 客户端,该客户端同样没有继承模拟身份,而是以 fleet-agent 的 cluster-admin 身份运行。攻击者可以将 valuesFrom 指向任意命名空间中的 Secret,例如:
helm:
valuesFrom:
- secretRef:
name: target-secret
namespace: kube-system
valuesFrom 的读取逻辑没有使用模拟后的服务账户,导致跨命名空间读取成功。
从设计角度看,Fleet 将“主部署流程”与“辅助数据获取流程”分离,但未能将 impersonation 上下文贯穿到所有客户端实例中,形成了不一致的授权状态。这是典型的“安全配置未全面覆盖代码路径”设计缺陷。Semgrep 检测规则中也明确匹配 helm.ActionConfiguration(...) 后是否调用 WithImpersonationConfig(...),用于发现那些遗漏模拟配置的 Helm Action 构造点。
💥 影响与危害
利用该漏洞的租户必须拥有 Fleet 监控仓库的 git push 权限。这只要求攻击者可以修改 Git 仓库内容,而无需任何下游集群的 kubeconfig 或 RBAC 权限。当存在漏洞的 Fleet-agent 开始部署或同步该 GitRepo 时,攻击者构造的恶意 chart 会被渲染并下发到该 GitRepo 指向的所有下游集群。影响范围包括:
- 跨命名空间读取全部 Secret:fleet-agent 以 cluster-admin 身份运行,因此攻击者实质上可以读取下游集群中全部命名空间的所有 Secret,包括 Kubelet 凭据、ServiceAccount Token、TLS 私钥、云厂商凭据以及连接外部服务的数据库密码、API Key 等。
- 横向扩散至外部系统:Secret 中可能包含用于外部服务的凭据,例如对象存储、CI/CD 系统、云 API 或消息队列。攻击者获取后可登录这些外部系统,造成数据泄漏或进一步破坏。
- 多集群全网沦陷:如果多个下游集群由同一个 Fleet 实例管理,攻击者的恶意仓库一旦被同步,可在所有目标集群上读取 Secret,影响面广。
- 潜在供应链风险:Fleet 的典型用途是 Kubernetes 应用交付。攻击者可能在 chart 中嵌入恶意逻辑,不仅读取 Secret,还可能利用读取到的数据操纵业务应用。
由于泄露的凭据可能属于第三方服务,漏洞的最终影响是非确定性的:从仅影响 Kubernetes 集群内部的机密信息,到导致外部云账号被接管、数据被删除或业务被勒索,完全取决于泄露凭据的权限范围。
🧪 PoC 复现分析
目前没有公开的 PoC 仓库或代码。但从漏洞描述和修复建议可以推断出可行的复现原理与关键步骤。一个最小化 PoC 需要两个核心组件:
组件一:恶意 Helm Chart
攻击者在 GitRepo 中放置一个 Helm chart,其模板包含类似以下内容:
{{- $secret := lookup "v1" "Secret" "kube-system" "my-target-secret" -}}
{{- if $secret -}}
{{ printf "%s" (index $secret.data "token") | b64dec }}
{{- end -}}
这段模板在 Helm 渲染时调用 lookup,以漏洞代码路径中的高权限客户端查询任意命名空间的 Secret,并将结果输出到生成的 Kubernetes 资源中(例如 ConfigMap 或日志)。如果模板引擎未使用 impersonation,该查询将成功返回。
组件二:恶意 fleet.yaml
攻击者还可以修改 fleet.yaml,使用 helm.valuesFrom 引用目标 Secret:
helm:
valuesFrom:
- secretRef:
name: target-secret
namespace: default
- configMapRef:
name: app-config
namespace: kube-system
当 Fleet 解析这些引用时,如果解析过程使用了未模拟的 client,则会把目标 Secret/ConfigMap 中的 key-value 作为 Helm values 传入。攻击者随后可在模板中通过 .Values.target-secret 将其打印到可观察的资源字段中,或者通过 HTTP 请求外传。
利用过程非常简单:攻击者提交恶意内容到仓库,Fleet-agent 自动同步并渲染。不需要提前拿到 Kubernetes 集群任何权限,仅凭借 git push 这一动作即可触发越权读取。
⚔️ EXP 利用分析
由于没有公开的 EXP 代码,这里的“利用分析”基于漏洞机制推演完整的攻击链路。一种典型的利用链如下:
- 获取 git push 权限:攻击者通过合法途径(本身是仓库贡献者)或社会工程获得 Fleet 所监控 Git 仓库的写权限。
- 构造恶意 GitRepo 内容:创建或修改 Helm chart 的
templates/文件,利用lookup函数读取目标 Secret;或在fleet.yaml中利用valuesFrom读取指定 Secret/ConfigMap。 - 触发 Fleet 同步:Fleet-agent 通常会周期性同步或基于 webhook 实时监听 Git 仓库。攻击者 push 代码后,Fleet-agent 会立即开始部署。
- 数据外泄:恶意模板将读取到的 Secret 写入到创建的 Kubernetes 资源中(如 ConfigMap、Secret、Job 环境变量),或者通过 Helm 的
post-renderer、钩子、pod 中运行的命令将内容发送到攻击者控制的服务器。由于 agent 的 cluster-admin 权限,该请求不会受到目标集群 RBAC 阻挡。 - 后渗透:攻击者获得读取到的云凭据或集群令牌后,可以访问云控制台、其他服务,或直接获取新集群的 RBAC 管理员权限。
值得注意的是,这种利用可能比传统集群逃逸更隐蔽,因为它完全发生在正常的 GitOps 流水线中。安全团队很难从 Kubernetes API server 的审计日志中区分合法的 chart 渲染和恶意的 lookup 调用,尤其是在高权限 agent 的访问模式下。
🕵️ 检测指纹说明
现有检测指纹为一组 Semgrep 规则,用于扫描 Go 源代码中的漏洞模式。规则提供了两个核心检测场景:
- 规则 CVE-2026-41050-rce-go:检测代码中是否创建了
helm.ActionConfiguration(...)对象且没有随后的WithImpersonationConfig(...)调用。如果匹配,则说明 Helm 操作可能缺少服务账户模拟,存在权限绕过风险。该规则标记 severity 为 ERROR,并提示使用action.WithImpersonationConfig(impersonateConfig)进行修复。 - 规则 CVE-2026-41050-rce-go-lookup:专门检测 Helm 模板引擎的
lookup路径上是否缺少模拟配置。该规则在源码层面定位到 Helm 客户端初始化位置,防止开发者遗漏对lookup路径的处理。
这些规则同时引用了 GHSA-765j-qfrp-hm3j 和 NVD 的 CVE 详情,可用于 CI/CD 流水线,在构建或合并前扫描 fleet 及其衍生项目源码。部署方式为在项目根目录添加 Semgrep 或将规则集成到 Semgrep AppSec 平台中。规则中的 OWASP 映射为 A1: Injection(语义上应当理解为外部控制数据注入到高权限操作中)。对于安全运维人员,也可以在不运行完整代理的情况下,用相同规则对本地仓库进行快速白盒审计。
需要注意的是,Semgrep 规则只覆盖 Go 代码中的静态调用模式,无法检测内部动态拼装客户端的方式。如果攻击者或开发人员通过变量间接传递 action 配置,规则可能会漏报。因此还应配合运行时审计,例如检查 Fleet-agent 的 Helm 渲染日志、Kubernetes API Server 的 impersonation 审计日志,以及 Secret 资源的访问记录。
🛡️ 修复与缓解
官方修复:Rancher 官方安全通告(GHSA-765j-qfrp-hm3j)已提供修复版本。受影响用户应优先升级到包含修复的 Fleet 版本。修复的核心方案是确保所有与 Helm 相关的 Kubernetes API 访问路径都使用模拟后的服务账户,即在每个 helm.ActionConfiguration 上都调用:
action.WithImpersonationConfig(impersonateConfig)
特别是 Helm lookup 函数和 valuesFrom 解析过程,必须使用继承自租户 ServiceAccount 的客户端,而不是 fleet-agent 的本地 kubeconfig 客户端。
缓解措施:
- 最小化 git push 权限:严格管理 Fleet 所监控仓库的写权限,仅授予完全可信的租户和运维人员。
- 在不可信环境中避免多租户共用 Fleet:如果所有用户均受信任,单租户部署不受该漏洞影响;多租户部署应尽快升级。
- 审计潜在泄露凭据:根据官方建议,检查集群中可能被读取的 Secret、ConfigMap,尤其是来自外部服务的凭据。如果发现异常访问记录或无法确定是否受影响,应轮换相关凭据。
- 启用 Kubernetes 审计日志:重点关注 fleet-agent 对 Secret/ConfigMap 的 get/list 操作,以及 impersonation 用户与 fleet-agent 自身用户之间的访问差异。
- 限制 Fleet-agent 的权限:如果可能,将 Fleet-agent 自身权限从 cluster-admin 降低为“仅部署所需的最小权限”,这样即使模拟被绕过,单个租户也只能读取 agent 具有权限的资源,但由于该漏洞目标是读取所有命名空间 Secret,最小权限法可以有效收敛影响。
- 使用网络策略与出口控制:防止恶意模板将数据外传到互联网,降低数据泄露影响。
综上,CVE-2026-41050 是一个非常典型的 GitOps 多租户隔离失效漏洞。攻击面简单、利用代价低、影响范围大。所有使用 Fleet 进行多集群应用交付的组织都应对该漏洞给予最高优先级处理,立即升级到修复版本,并完成凭据审计。
🕵️ 检测指纹规则
🛡️ Semgrep 审计规则: CVE-2026-41050.yaml
rules:
- id: CVE-2026-41050-rce-go
languages:
- go
severity: ERROR
message: "Potential Helm impersonation bypass due to missing ServiceAccount impersonation in Helm deployer. Fleet-agent may use cluster-admin credentials instead of impersonated ServiceAccount,
allowing privilege escalation."
patterns:
- pattern: helm.ActionConfiguration(...)
- pattern-not: helm.ActionConfiguration(...).WithImpersonationConfig(...)
fix: "Ensure Helm action uses impersonated ServiceAccount credentials: action.WithImpersonationConfig(impersonateConfig)"
metadata:
cwe: "CWE-284"
owasp: "A1: Injection"
technology: fleet
references:
- "https://github.com/rancher/fleet/security/advisories/GHSA-765j-qfrp-hm3j"
- "https://nvd.nist.gov/vuln/detail/CVE-2026-41050"
- id: CVE-2026-41050-rce-go-lookup
languages:
- go
severity: ERROR
message: "Potential Helm lookup bypass: missing impersonation on Helm template engine queries. Use cluster-admin credentials instead of impersonated ServiceAccount."
patterns:
- pattern: $CLIENT.Get(...)
- pattern-not: $CLIENT.WithImpersonation(...).Get(...)
fix: "Client should use impersonation: impersonatedClient.Get(...)"
metadata:
cwe: "CWE-284"
owasp: "A1: Injection"
technology: fleet
references:
- "https://github.com/rancher/fleet/security/advisories/GHSA-765j-qfrp-hm3j"
- "https://nvd.nist.gov/vuln/detail/CVE-2026-41050"
- id: CVE-2026-41050-rce-go-valuesfrom
languages:
- go
severity: ERROR
message: "Potential valuesFrom bypass: Fleet-agent reads Secret/ConfigMap references in fleet.yaml without proper impersonation,
using cluster-admin privileges."
patterns:
- pattern: $CLIENT.Get($NAMESPACE,$NAME,...)
- pattern-not: $CLIENT.WithImpersonation($IMP).Get($NAMESPACE,$NAME,...)
fix: "Use impersonated client: restClient.WithImpersonation(impConfig).Get(namespace,name,
opts)"
metadata:
cwe: "CWE-284"
owasp: "A1: Injection"
technology: fleet
references:
- "https://github.com/rancher/fleet/security/advisories/GHSA-765j-qfrp-hm3j"
- "https://nvd.nist.gov/vuln/detail/CVE-2026-41050"🛡️ CodeQL 审计规则: CVE-2026-41050.ql
/**
* @kind path-problem
* @id go/impersonation-bypass/cve-2026-41050
* @name Fleet Helm impersonation bypass of RESTClientGetter retains cluster-admin credentials
* @description User-controlled ServiceAccount impersonation is bypassed in Fleet's Helm deployer,
allowing tenant to read secrets outside RBAC scope via Helm lookup or valuesFrom
* @problem.severity error
* @tags security
* external/cwe/cwe-284
*/
import go
import semmle.go.security.ImpersonationQuery
import ImpersonationFlow::PathGraph
class HelmLookupSink extends DataFlow::Node {HelmLookupSink() {exists(MethodCall mc |mc.getTarget().hasQualifiedName("helm.sh/helm/v3/pkg/action",
"Configuration","getClient") and
this = mc.getResult()
)
}}class ValuesFromSink extends DataFlow::Node {ValuesFromSink() {exists(FunctionCall fc |fc.getTarget().hasQualifiedName("github.com/rancher/fleet/pkg/helm","getValuesFrom") and
this = fc.getAnArgument()
)
}}class ImpersonationConfig extends TaintTracking::Configuration {ImpersonationConfig() {
this = "ImpersonationConfig" }override predicate isSource(DataFlow::Node source) {exists(Parameter p |p.getFunction().hasQualifiedName("github.com/rancher/fleet/pkg/helm","deploy") and
p.getName() = "impersonate" and
source.asParameter() = p
)
}override predicate isSink(DataFlow::Node sink) {sink instanceof HelmLookupSink or
sink instanceof ValuesFromSink
}
override predicate isAdditionalTaintStep(DataFlow::Node node1,DataFlow::Node node2) {exists(FunctionCall fc |fc.getTarget().hasQualifiedName("github.com/rancher/fleet/pkg/helm","newClient") and
fc.getAnArgument() = node1.asExpr() and
node2.asExpr() = fc.getResult()
)
}}from ImpersonationFlow::PathNode source,
ImpersonationFlow::PathNode sink
where ImpersonationFlow::flowPath(source,sink)
select sink.getNode(),source,sink,"User-controlled impersonation subject flows to Helm operation without proper credential restriction"🤖 本文由漏洞情报系统自动聚合生成 · 2026-08-01 03:07 · 数据源: NVD/GitHub-Advisory/OSV/CISA-KEV/Exploit-DB/PoC-in-GitHub + 检测规则库