Skip to content

Build1 publisher3 min readPublished

Rogue admission webhook mutates pods while SOC dashboards show normal success rates

A dev.to write-up prints the ClusterRole that gives an operator four write verbs on mutating webhook configurations, then the two manifest lines that make the resulting persistence read as a healthy cluster.

The Engineer · Build desk

What happened

  • A dev.to write-up prints a ClusterRole named acme-platform-controller that grants create, update, patch and delete on mutatingwebhookconfigurations in the admissionregistration.k8s.io API group.
  • Its companion MutatingWebhookConfiguration points clientConfig at an external URL and matches CREATE on pods, which is enough to add an init-container to every pod the API server admits.
  • The escalation path starts with an operator's Helm chart templating wildcard webhook permissions and ends with any workload that can exec into the operator's pod or read its ServiceAccount token.
  • The author's proposed rule bars every Role and ClusterRole from granting write verbs on either webhook configuration resource to anyone but a designated cluster administrator.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • exposure The privilege stops somewhere other than where the namespace diagram says it does: reading one ServiceAccount token in a namespace other workloads can write to yields authority over every webhook in the cluster, not just the operator's own.
  • decision Platform teams now choose between an operator's default chart values and templating resourceNames per named webhook, and the second option means owning that diff at every chart upgrade.
  • constraint The setting that stops a wedged webhook from blocking pod creation is the same setting that removes the failure signal an operator would notice, so the safe default costs you the detection.
  • capability Because the check reduces to a write verb on a named resource, it can run as a pre-merge test on chart values rather than a finding written up after an incident.

Read the verdict field before the prose. Stave's observation schema sets `has_webhook_config_access` to true when any rule grants a write verb on either webhook configuration resource [12]. The example role carries four write verbs on one of those two resources [1], which puts eight verb-and-resource combinations behind a single boolean [13]. That is the right granularity for a detector, because which verb you hold matters less than whether the object is writable at all. Keeping the raw `rules` array and the bound ServiceAccount next to the verdict, so a reviewer can see what produced the true, is the part worth copying [11].

The mechanism underneath is short. A mutating webhook intercepts API calls before the server commits them, and can rewrite pod specs or inject sidecars [2]. A subject who can write the configuration decides which server that is [3].

Two lines in the manifest read as hygiene and are not. `failurePolicy: Ignore` lets the request proceed when the webhook is unreachable [4]. You want that when a wedged webhook would otherwise block pod creation cluster-wide, and it is also what makes a hostile webhook intermittent rather than noisy: offline during upgrades, active during normal operation [4]. The dashboard consequence follows directly from the setting. Pod creation success stays at 100 percent because the creations do succeed, and some of them now carry a sidecar [18].

`sideEffects: None` is the second line, and the author calls it a lie the attacker tells the API server [5]. The admission API does not verify the claim; it trusts the webhook author, while the example's actual effect is shipping pod specs to an external URL [5][6]. The API server has no way to check, so the field is documentation with a schema.

One claim in the write-up runs ahead of its own artifact. It says secret reads return the attacker's data [7], while the manifest it prints matches `operations: ["CREATE"]` on `resources: ["pods"]` in the core API group [6]. What that configuration demonstrates is rewriting of pod creation [8]. Interception of reads would need a path the write-up does not show, so treat the pod-spec mutation as the demonstrated capability.

On how common the grant is, the write-up offers a pattern rather than a measurement. It says the misconfiguration ships to production routinely, that it is established enough to have dedicated detection logic, and that names like "platform controller" or "admission controller manager" make the permission set sound foundational [15]. It refers to incident write-ups without naming one, and reports no count of charts or clusters [16], so the manifests carry the mechanism and nothing here carries the frequency.

The compromise the author names is `resourceNames` on a single named webhook instead of the whole resource class [14], which is exactly where the escalation path turns: the Helm chart templated the class [9]. Whether the rest of that path is yours is checkable against objects you already hold. You need a write verb on either webhook resource in a bound role, you need the operator's ServiceAccount sitting in a namespace other workloads can write to, and you need the grant to carry no `resourceNames`. The first is in the ClusterRole, the second in your namespace RBAC, the third in the chart values you either overrode or accepted [9][14].

What to watch

  • Whether the operators named in the write-up, Argo CD and Cert Manager among them, ship chart values that scope webhook permissions with resourceNames instead of the resource class.
  • Whether Stave publishes the has_webhook_config_access rule as an admission-time or CI-time gate rather than an inventory verdict computed after the fact.
  • Any named incident report tying this pattern to a specific cluster compromise, which the write-up references in general but does not cite.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories