# kcp's cache server is accessible without authentication or authorization checks

kcp's cache server is proxied in the pre-mux chain before authn/authz, letting anyone reaching the root shard read replicated RBAC, topology and API data unauthenticated.

- Payout: Research
- Severity: high
- Program: kcp
- Found by: ntnn
- Disclosed: 2026-04-08
- CVE: CVE-2026-39429
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-3j3q-wp9x-585p
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-3j3q-wp9x-585p
- Tags: area:cloud domain:api domain:cloud impact:data-leak impact:privesc kind:research platform:ghsa sev:high technique:race-window technique:source-review vuln:access-control vuln:auth-bypass vuln:info-disclosure vuln:race-condition

**Target:** kcp root shard cache server (/services/cache/*)
**Root cause:** The cache server is routed in preHandlerChainMux, which runs before any authentication/authorization in the shard handler chain, so requests to /services/cache/* are proxied without authn/authz.
**Steps:**
1. Start a kcp root shard with an externally reachable shard URL.
2. Send `curl --insecure 'https://root.vespucci.genericcontrolplane.io:6443/services/cache/shards/root/clusters/root/apis/apis.kcp.io/v1alpha1'` with no credentials.
3. Observe replicated resources returned; enumerate clusterroles/clusterrolebindings, logicalclusters, shards, apiexports, webhook configurations.
4. Optionally POST a ClusterRole + ClusterRoleBinding to the cache and race a privileged API request within the sub-second window before the replication controller deletes it.
**How found:** Code reading of pkg/server/config.go: noticed the cache server is registered in preHandlerChainMux (lines 514-518), which is handled before authn/authz, and that the authorization informer shares CacheKubeSharedInformerFactory with the replication controller (config.go:361), creating a race between informer update and reconcile-delete.
**Exploitation:** Unauthenticated GET to /services/cache/* on the root shard returns all replicated resources (annotated RBAC, logicalclusters, shards, apiexports, admission webhooks). Write path: inject RBAC objects into cache etcd; the shared CacheKubeSharedInformerFactory updates the authorizer store before the replication controller deletes the object, giving a sub-second window to fire a privileged request.
**Escalation:** Injected RBAC objects visible to GlobalAuthorizer/WorkspaceContentAuthorizer during the race window could grant temporary privilege escalation; practical exploitability rated low due to sub-second window.
**Insight:** When a component is mounted in a pre-mux/pre-handler chain, it bypasses the entire auth middleware stack — grep for mux registration order rather than assuming handlers are protected. Also check whether an authorizer's informer store and a cleanup controller share a factory: shared informers make injected objects visible to authz before deletion.

## Proof of concept (copied exactly from the original report)

**Unauthenticated curl to cache server API**

```
curl --insecure 'https://root.vespucci.genericcontrolplane.io:6443/services/cache/shards/root/clusters/root/apis/apis.kcp.io/v1alpha1'
```

