Post

GHSA-8rvj-mm4h-c258: cert-manager direct Challenge solver policy bypass

GHSA-8rvj-mm4h-c258: cert-manager direct Challenge solver policy bypass

I reported a cert-manager ACME Challenge authorization issue that was published as GHSA-8rvj-mm4h-c258.

The issue affected direct Challenge resources under acme.cert-manager.io. A namespace user who could create Challenge objects could provide an attacker-controlled Challenge.spec.solver. When that direct Challenge referenced a ClusterIssuer, cert-manager derived credential authority from the ClusterIssuer but accepted DNS01 provider configuration from the user-controlled Challenge.

That let a namespace user bypass Issuer or ClusterIssuer solver policy and cause the cert-manager controller to use platform-owned DNS credentials for attacker-selected provider settings. In the acme-dns provider case, the issue could disclose the referenced account credential to an attacker-controlled endpoint.

Advisory

  • CVE: No known CVE
  • GitHub Advisory: GHSA-8rvj-mm4h-c258
  • Package: github.com/cert-manager/cert-manager
  • Ecosystem: Go module
  • Affected versions: >= 1.18.0, <= 1.20.2
  • Patched versions: 1.19.6, 1.20.3
  • Severity: High 7.3/10
  • CVSS: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N
  • CWE: CWE-863: Incorrect Authorization

Preconditions

The vulnerable deployment shape required all of the following:

  • the attacker could create challenges.acme.cert-manager.io resources in at least one namespace;
  • the cert-manager controller was running and reconciling ACME Challenge resources cluster-wide;
  • a usable ClusterIssuer existed in the cluster;
  • the referenced ClusterIssuer used DNS01 credentials from the cluster resource namespace, or ambient credentials were enabled for ClusterIssuer;
  • the cluster had a multi-tenant or delegated-administration boundary between namespace users and platform-owned Issuer, ClusterIssuer, and DNS credential management.

This was especially relevant where aggregate Kubernetes roles allowed namespace users to create cert-manager ACME workflow resources that are normally treated as internal implementation objects.

Root Cause

cert-manager’s documented ACME flow creates Challenges from Orders after selecting a solver from the referenced Issuer or ClusterIssuer. In that normal flow, solver selectors such as dnsZones, dnsNames, and matchLabels decide which DNS01 provider configuration should be used.

The vulnerable path skipped that flow.

A user-created direct Challenge object could be admitted without proving that it was tied to a cert-manager-created Order, a valid owner reference, or an Issuer-selected solver. The Challenge controller then reconciled the object and used the solver field already present in the Challenge.

For ClusterIssuer references, cert-manager treated the issuer reference as authority to use cluster-scoped credential behavior:

  • secrets were resolved from the cluster resource namespace;
  • ambient credential permission followed the ClusterIssuer reference;
  • the DNS provider configuration still came from the user-controlled Challenge.spec.solver.

That crossed the security boundary. The namespace user controlled the provider settings, while the cluster controller supplied platform credential authority.

Attack Shape

A low-privileged namespace user creates a direct DNS-01 Challenge that references a platform-owned ClusterIssuer.

The Challenge can keep the same target DNS name expected by the legitimate ClusterIssuer policy, but replace sensitive provider settings. In the acme-dns case, the attacker points the solver host at an attacker-controlled endpoint while referencing the account secret name used by the ClusterIssuer.

The namespace user does not need to read the target secret or the ClusterIssuer. The controller performs that privileged work during reconciliation:

  1. it accepts the direct Challenge;
  2. it loads issuer context based on issuerRef;
  3. it resolves DNS credential location and ambient credential behavior as a ClusterIssuer operation;
  4. it builds the DNS01 provider from the attacker-controlled solver configuration;
  5. it calls the provider to present the DNS challenge.

For acme-dns, that provider call sends the account username and key as HTTP headers to the configured host. If the host is attacker-controlled, the account credential is disclosed.

Impact

The impact is an authorization bypass across the namespace-user to cluster-controller boundary.

Practical impact includes:

  • bypassing Issuer or ClusterIssuer solver selectors such as DNS zones, DNS names, and label matches;
  • causing the cert-manager controller to use ClusterIssuer DNS credentials for attacker-selected provider settings;
  • disclosing acme-dns account credentials to an attacker-controlled endpoint;
  • causing unauthorized DNS mutation or credential use with other DNS01 providers, depending on the provider and credential shape.

The concrete credential-disclosure proof applies to acme-dns. For other DNS providers, the safer framing is credential use, solver-policy bypass, and DNS mutation risk unless a provider-specific disclosure path is separately shown.

Why This Was Easy To Miss

The bug sits in the gap between Kubernetes RBAC and controller trust.

Challenge resources look like low-level ACME workflow objects, and normal end users do not need to create them directly. But if RBAC gives namespace users create permission, the controller still has to treat those objects as untrusted. In the vulnerable flow, the controller accepted the user-supplied solver as authoritative while simultaneously giving it ClusterIssuer credential context.

That is why this is not just a local RBAC mistake. The security boundary is the Issuer or ClusterIssuer solver policy plus its credential configuration. Direct Challenge creation bypassed that boundary because the object did not have to prove it came from the Order controller’s solver-selection path.

Fix Direction

The fix needs to make direct Challenge reconciliation prove its lineage and solver authority before credentials are used.

Defensive principles:

  • reject or ignore standalone user-created Challenges unless they can be tied to a valid cert-manager-created Order;
  • verify owner references, UIDs, labels, DNS names, authorization URLs, challenge URLs, token/key material, and issuer references against the expected Order flow;
  • ensure the Challenge solver was selected from the referenced Issuer or ClusterIssuer by cert-manager, not supplied directly by a namespace user;
  • remove Challenge and Order creation from aggregate edit-style roles unless direct creation is intentionally supported as a security boundary;
  • add admission validation or CEL rules for standalone Challenges and sensitive solver provider fields;
  • add regression tests for direct Challenge creation with ClusterIssuer DNS credentials.

The important invariant is that a namespace user should not be able to make cert-manager combine user-controlled DNS provider settings with platform-owned ClusterIssuer credential authority.

References

This post is licensed under CC BY 4.0 by the author.