Live › vulnerability › CVE-2026-62290
A namespace user who can create ACME Challenge resources can supply their own DNS solver configuration, which cert-manager then uses with the ClusterIssuer's DNS credentials. With acme-dns, the attacker-chosen host receives the referenced account credentials as HTTP headers, and the Issuer's DNS name policy can be bypassed.
Summary written from the sources below. Check the originals before acting.
First seen by InfraStack 2026-10-29 16:44 UTC, 0h before NVD. All times on this page are UTC.
Clusters running cert-manager in the affected ranges that also have a ClusterIssuer with a DNS01 solver backed by a Secret or ambient credentials, and where some namespace users are not fully trusted. Any user or role that can create challenges.acme.cert-manager.io, including through cert-manager's aggregate edit role, can trigger it. Single-tenant clusters where only platform staff can create Challenges are less exposed.
| Product | Ecosystem | Affected | Fixed in |
|---|---|---|---|
| github.com/cert-manager/cert-manager | go | >= 1.18.0, < 1.19.6 | 1.19.6 |
| github.com/cert-manager/cert-manager | go | >= 1.20.0-alpha.0, < 1.20.3 | 1.20.3 |
| linuxfoundation cert-manager | vendor | >= 1.18.0, < 1.19.6 | 1.19.6 |
| linuxfoundation cert-manager | vendor | >= 1.20.0, < 1.20.3 | 1.20.3 |
kubectl get deployment -A -o jsonpath='{.items[*].spec.template.spec.containers[*].image}' and look for cert-manager images below 1.19.6 or in the 1.20.0-alpha.0 to 1.20.2 range.kubectl auth can-i create challenges.acme.cert-manager.io -n <namespace> --as=<user> for namespace users to see who can create direct Challenges.kubectl get clusterissuer -o yaml.1.18.x: move to 1.19.61.19.x: move to 1.19.61.20.x: move to 1.20.3Each row is the first time that source carried this signal. The analyst’s rows are its own findings.
Challenge resources under acme.cert-manager.io can be created directly by namespace users. On create, cert-manager performs no admission validation tying the Challenge to an Order, an owner reference, or an Issuer-selected solver. The ACME Challenge controller then reconciles the direct resource and uses Challenge.spec.solver as the DNS01 provider configuration.
When the direct Challenge references a ClusterIssuer, cert-manager derives the DNS provider secret namespace and ambient-credential policy from the ClusterIssuer reference, but the DNS provider configuration itself is attacker-controlled through Challenge.spec.solver. This lets a namespace user bypass Issuer/ClusterIssuer solver selectors such as dnsZones, dnsNames, and matchLabels, and make the cert-manager controller use ClusterIssuer DNS credentials for attacker-selected provider settings and DNS names.
In the acme-dns case, this becomes credential disclosure because the attacker-controlled solver host receives the X-Api-User and X-Api-Key headers loaded by cert-manager from the ClusterIssuer resource namespace.
challenges.acme.cert-manager.io resources in at least one namespace.Challenge resources cluster-wide.ClusterIssuer exists in the cluster.ClusterIssuer has a DNS01 solver that uses a Secret in the cluster resource namespace, or relies on ambient credentials enabled for ClusterIssuer.Rationale: a tenant with namespace-level ACME Challenge create permissions, including permissions granted by cert-manager's aggregate edit role, can cause a cluster-scoped controller to use DNS credentials or ambient cloud credentials outside the Issuer's intended solver policy. With acme-dns specifically, this can disclose the referenced acme-dns account credential to an attacker-controlled endpoint. With other DNS providers, the impact is credential use, Issuer policy bypass, and unauthorized DNS mutation risk unless a provider-specific credential disclosure path is separately demonstrated.
cert-manager's documented end-user workflow does not require users to manually create Challenge resources. Challenges are internal ACME workflow objects generated from Orders after cert-manager has selected a solver from the referenced Issuer or ClusterIssuer. If default or aggregate roles grant namespace users permission to create these resources, the controller still should not treat a user-controlled Challenge.spec.solver as authoritative for a referenced ClusterIssuer.
The security boundary is the Issuer/ClusterIssuer solver policy and credential configuration. Direct Challenge creation bypasses that boundary because the controller derives credential namespace and ambient credential permission from issuerRef, while accepting the DNS provider configuration from the untrusted Challenge object.
challenges.acme.cert-manager.ioThe issue was reproduced on:
master commit 2c0f1a6a4v1.20.2 commit e5b7b1845v1.19.5 commit cda7b7896v1.18.6 commit 4699caabbSee VERSION_TEST_MATRIX.md for the command matrix and outputs.
Relevant code paths:
internal/apis/acme/validation/challenge.go:44-45: ValidateChallenge returns no errors on create.deploy/crds/acme.cert-manager.io_challenges.yaml: spec.solver is a required user-controlled field on Challenge.pkg/controller/acmechallenges/scheduler/scheduler.go:51-58: scheduler lists all Challenges.pkg/controller/acmechallenges/controller.go:180-197: scheduler sets status.processing=true.pkg/controller/acmechallenges/sync.go:139-183: controller loads the referenced Issuer and then presents the Challenge.pkg/issuer/acme/dns/dns.go:81: DNS solver Present ignores the issuer object argument.pkg/issuer/acme/dns/dns.go:180-187: DNS provider config and resource namespace are derived from Challenge.spec.pkg/controller/helper.go:39-44: ClusterIssuer references use the cluster resource namespace.The normal Order flow selects a solver from the Issuer:
pkg/util/solverpicker/solverpicker.go: solver selectors are evaluated.pkg/controller/acmeorders/util.go:94-106: solvers are fetched from the Issuer and selected.pkg/controller/acmeorders/util.go:131-142: the selected solver is copied into the generated Challenge.The direct Challenge flow skips that selection entirely because the direct object already contains a solver.
The following script starts k3s in Docker, installs only the relevant CRDs, creates a platform-owned ClusterIssuer, creates a namespace user with Challenge create permission but no secret or ClusterIssuer read permission, and creates a direct DNS01 Challenge referencing that ClusterIssuer and a cluster resource namespace secret.
The ClusterIssuer is configured to use a legitimate acme-dns host for victim.example.com. The direct Challenge uses that same DNS name, but replaces the acme-dns host with an attacker-controlled endpoint. This demonstrates credential disclosure without relying on an out-of-policy DNS name.
A broader DNS policy-bypass variant is also possible when the referenced credential or ambient provider can operate on the attacker-selected DNS name. In that case, the same direct Challenge path bypasses the Issuer/ClusterIssuer solver selectors and provider configuration.
Run from the cert-manager repository root:
set -euo pipefail
name=cm-direct-challenge-poc
docker rm -f "$name" >/dev/null 2>&1 || true
docker run -d --name "$name" --privileged -v "$PWD":/repo rancher/k3s:v1.33.4-k3s1 \
server --disable=traefik --disable=servicelb --write-kubeconfig-mode=644 >/dev/null
cleanup() {
docker rm -f "$name" >/dev/null 2>&1 || true
}
trap cleanup EXIT
for i in $(seq 1 90); do
docker exec "$name" kubectl get --raw=/readyz >/dev/null 2>&1 && break
sleep 2
done
docker exec "$name" kubectl apply -f /repo/deploy/crds/cert-manager.io_clusterissuers.yaml >/dev/null
docker exec "$name" kubectl apply -f /repo/deploy/crds/acme.cert-manager.io_challenges.yaml >/dev/null
docker exec "$name" kubectl create ns tenant >/dev/null
docker exec "$name" kubectl create ns cert-manager >/dev/null
docker exec "$name" kubectl -n tenant create sa bob >/dev/null
docker exec "" kubectl -n cert-manager create secret generic cluster-acmedns-account \
--from-literal=accounts.json= >/dev/null
docker -i kubectl apply -f - >/dev/null <<
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: cluster-prod
spec:
acme:
email: [email protected]
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: cluster-prod-account-key
solvers:
- selector:
dnsZones:
- victim.example.com
dns01:
acmeDNS:
host: https://legit-acmedns.example
This page as .md · .json · in your agent: get_signal("CVE-2026-62290")
GitHub Advisory Database, CC-BY 4.0 · NVD data is provided by NIST and is public domain. Posts quoted belong to their authors.
pkg/controller/helper.go:66-72: ambient credentials are allowed based on issuerRef.kind.internal/apis/config/controller/v1alpha1/defaults.go:68-69: ClusterIssuer ambient credentials default to enabled.deploy/charts/cert-manager/templates/rbac.yaml:517-519: the aggregate edit role can create/update challenges and orders.