CVE-2026-75033: one Rancher annotation copies another cluster's secrets

CVE-2026-75033: one Rancher annotation copies another cluster's secrets

How to fix CVE-2026-75033: upgrade Rancher to 2.15.1, 2.14.5, 2.13.9, or 2.12.13, and move rancher-webhook with it.

6 min read1,328 words
Contents

A Rancher user who can create a namespace on one cluster can pull Project Secrets out of a different cluster. Rancher's project-scoped secret controller copied Project Secrets into a namespace based only on that namespace's field.cattle.io/projectId annotation, and never checked that the referenced project lived in the same downstream cluster. Set the annotation to c-otherclusterid:p-someproject and the controller delivers that project's credentials, API keys, and TLS certificates into a namespace the caller controls.

The fixes shipped between 26 and 28 August 2026 in Rancher 2.12.13, 2.13.9, 2.14.5, and 2.15.1. The CVE records only became public on 3 September. If a scanner started flagging your Rancher install this morning, the patch has been available for a week, and two sibling issues from the same release train are worth the same upgrade window.

One root cause, three broken boundaries

All three records published today describe the same mistake in different controllers: a field a client can write is used as authorization input.

  • CVE-2026-75033 (CVSS 7.7 from the CNA, pkg/controllers/managementuser/secret/). One instance of the secret controller runs per downstream cluster. The namespace handler propagated Project Secrets on the strength of the projectId annotation alone. Preconditions per the advisory: an authenticated Rancher user with namespace creation rights on at least one downstream cluster, such as a Cluster Owner, who knows or guesses a target project ID in the c-xxxxx:p-yyyyy format. No victim interaction. SUSE confirmed it on Rancher 2.14.3.
  • CVE-2026-71404 (CVSS 8.7, globalrole_handler.go). The GlobalRole controller took the name of its backing ClusterRole from the user-settable authz.management.cattle.io/cr-name annotation and overwrote that object's rules with no ownership check. A user with delegated create or update on management.cattle.io/globalroles could point the annotation at cluster-admin, ship an empty rule set, and strip the permissions of everyone bound to it. The controller added no owner reference on that path, so deleting the malicious GlobalRole does not undo it. Rancher's webhook escalation check validated the GlobalRole's rules against the caller's own permissions and never looked at the annotation, which is why an empty rule set sailed through.
  • CVE-2026-71403 (CVSS 6.1, pkg/auth/api/user/user_store.go). The Norman /v3/users update path accepted changes to username, principalIds, and displayName on an existing User. A caller with update on users.management.cattle.io could inject a foreign identity provider principal into any account, and the next login by the owner of that principal bound to the victim's User and inherited its GlobalRoleBindings, ClusterRoleTemplateBindings, and ProjectRoleTemplateBindings. The same rebind primitive was closed on the create path in CVE-2024-58260, but that fix never covered update.

What the patches change, and what they do not

For CVE-2026-71404, the controller now computes the ClusterRole name itself with name.SafeConcatName("cattle-globalrole", grName), finds the object through the authz.management.cattle.io/gr-owner label, and writes the cr-name annotation as a read-only breadcrumb. A tampered annotation self-heals after the upgrade. An emptied rule set does not. If a non-aggregated ClusterRole was already blanked, it stays blanked until someone repairs it. Aggregated roles are different, and that difference matters below.

For CVE-2026-71403, the Norman half of the fix is two lines that delete principalIds and username from the update payload before it reaches the API server. SUSE is explicit that such requests still return HTTP 200 with the fields dropped silently, so attempts leave no error behind and any client that trusts its own write must re-read the object. The Kubernetes API path is guarded separately by rancher-webhook, whose users validator rejects the change with field is immutable. Upgrading Rancher without moving the webhook leaves the direct Kubernetes path open, and the manage-users verb does not exempt anyone from that check.

One upgrade side effect worth knowing: because principalIds can no longer be supplied by a client, local user creation now waits for them to be populated asynchronously, five attempts with a growing delay, then logs that the principal IDs "were not populated in time" and returns the user without them. Provisioning automation that reads principalIds straight out of the create response needs to tolerate an empty list and re-read.

Who is not in scope

  • A Rancher instance managing a single cluster is not exposed to CVE-2026-75033. The advisory scopes it to deployments managing more than one cluster, since the attack crosses from one downstream cluster to another.
  • A Rancher using only local authentication is not exposed to the CVE-2026-71403 takeover. It needs an external provider such as LDAP, OIDC, SAML, or Active Directory, and it needs the targeted principal to log in afterward. Standard authenticated users do not hold update on users.management.cattle.io and receive HTTP 403 before reaching the code path.
  • CVE-2026-71404 grants no new permissions. It is a denial of service, and the built-in aggregated roles admin, edit, and view are rebuilt by the Kubernetes aggregation controller. The durable damage lands on explicit-rule ClusterRoles such as cluster-admin.
  • Branch coverage is not uniform. SUSE lists the 2.12 line as affected for CVE-2026-75033 and fixed in 2.12.13, but lists only the 2.13, 2.14, and 2.15 lines for CVE-2026-71403 and CVE-2026-71404. The CVE records, by contrast, carry a flat "before 2.15.1" range, so a scanner keyed to the CVE range alone will flag a fully patched 2.14.5 or 2.13.9. NVD has all three records at status Received and has not published its own analysis yet.

Checks to run before and after the upgrade

Confirm both halves of the fix are actually deployed:

kubectl -n cattle-system get deploy rancher rancher-webhook \
  -o custom-columns=NAME:.metadata.name,IMAGE:'.spec.template.spec.containers[0].image'

Prove the identity fields are immutable on the Kubernetes API path without changing anything. A server-side dry run still runs admission, so a patched webhook answers with field is immutable instead of accepting the patch:

kubectl patch users.management.cattle.io u-abc123 --type merge \
  -p '{"principalIds":["openidconnect_user://attacker"]}' --dry-run=server

Hunt for the CVE-2026-75033 artifact on each downstream cluster. Any namespace whose annotation names a cluster ID other than the one it lives in is the exploit, not a misconfiguration:

kubectl get ns -o json | jq -r '.items[]
  | select(.metadata.annotations["field.cattle.io/projectId"])
  | [.metadata.name, .metadata.annotations["field.cattle.io/projectId"]] | @tsv'

Map those c- prefixes with kubectl get clusters.management.cattle.io on the local management cluster. Then audit GlobalRole annotations and the integrity of the role most worth attacking:

kubectl get globalroles.management.cattle.io -o json | jq -r '.items[]
  | [.metadata.name, (.metadata.annotations["authz.management.cattle.io/cr-name"] // "-")] | @tsv'

kubectl get clusterrole cluster-admin -o json \
  | jq '{rules: .rules, grOwner: .metadata.labels["authz.management.cattle.io/gr-owner"]}'

An annotation that is not cattle-globalrole-<globalrole name> was set by a client. A gr-owner label on a built-in role, or an empty rules array on cluster-admin, means the overwrite already happened.

How to fix

helm upgrade rancher rancher-latest/rancher \
  --namespace cattle-system --version 2.15.1 --reuse-values

Stay on your own line if you are not moving minors: 2.14.5 or 2.13.9, and 2.12.13 for the secret leak. Rancher moves the rancher-webhook chart as part of its upgrade, so verify the webhook image changed too, because CVE-2026-71403 is only half fixed without it. If the audit above found a blanked ClusterRole, restore it explicitly, since nothing in the patch repairs prior damage:

kubectl patch clusterrole cluster-admin --type merge -p '{"rules":[
  {"apiGroups":["*"],"resources":["*"],"verbs":["*"]},
  {"nonResourceURLs":["*"],"verbs":["*"]}]}'

If an upgrade has to wait, the mitigations that matter are narrow: remove update on users.management.cattle.io and create or update on globalroles from every non-administrative custom GlobalRole, and restrict namespace creation on downstream clusters. There is no configuration flag that turns any of these three off.

What this is not

None of the three is remote code execution, and none can be reached by an unauthenticated request to Rancher, since each one starts from an account Rancher already trusts with a specific verb.

The pattern is the part worth carrying into your own controllers: a label selector, an annotation, or an identity field that a client can write must never be the thing that decides what the client is allowed to see. The source records for this batch are in HOL Guard, and the primary references are the SUSE advisories GHSA-5hf4-f4mp-g6h4, GHSA-92jp-phmj-wv65, and GHSA-wfvm-w99r-cjgc, the patches in rancher/rancher#56593 and #56616, and the v2.15.1 release notes.

Continue reading

All posts