{"id":"CVE-2026-33540","aliases":["GHSA-3p65-76g6-3w7r","GO-2026-5094"],"title":"Distribution affected by pull-through cache credential exfiltration via www-authenticate bearer realm","summary":"Distribution affected by pull-through cache credential exfiltration via www-authenticate bearer realm","severity":"high","cvss":7.5,"cvssVector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N","vendor":"distribution","product":"github.com/distribution/distribution/v3","ecosystem":"go","affected":["github.com/distribution/distribution/v3 < 3.1.0","github.com/distribution/distribution <= 2.8.3"],"patched":["github.com/distribution/distribution/v3 3.1.0"],"published":"2026-04-06","updated":"2026-09-10","sourceUpdated":"2026-09-10T03:50:42.426312501Z","source":"OSV","sourceUrl":"https://osv.dev/vulnerability/GHSA-3p65-76g6-3w7r","references":[{"url":"https://github.com/distribution/distribution/security/advisories/GHSA-3p65-76g6-3w7r"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-33540"},{"url":"https://github.com/distribution/distribution/commit/cc5d5fa4ba02157501e6afa2cc6a903ad0338e7b"},{"url":"https://github.com/distribution/distribution"}],"tags":["osv","go"],"epss":0.00274,"epssPercentile":0.20051,"ingestedAt":"2026-07-10T18:56:50.809Z","slug":"CVE-2026-33540","body":"## Overview\n\nhi guys,\n\ncommit: 40594bd98e6d6ed993b5c6021c93fdf96d2e5851 (as-of 2026-01-31)\ncontact: GitHub Security Advisory (https://github.com/distribution/distribution/security/advisories/new)\n\n## summary\n\nin pull-through cache mode, distribution discovers token auth endpoints by parsing `WWW-Authenticate` challenges returned by the configured upstream registry. the `realm` URL from a bearer challenge is used without validating that it matches the upstream registry host. as a result, an attacker-controlled upstream (or an attacker with MitM position to the upstream) can cause distribution to send the configured upstream credentials via basic auth to an attacker-controlled `realm` URL.\n\nthis is the same vulnerability class as CVE-2020-15157 (containerd), but in distribution’s pull-through cache proxy auth flow.\n\n## severity\n\nHIGH\n\nnote: the baseline impact is credential disclosure of the configured upstream credentials. if a deployment uses broader credentials for upstream auth (for example cloud iam credentials), the downstream impact can be higher; i am not claiming this as default for all deployments.\n\n## impact\n\ncredential exfiltration of the upstream authentication material configured for the pull-through cache.\n\nattacker starting positions that make this realistic:\n- supply chain / configuration: an operator configures a proxy cache to use an upstream that becomes attacker-controlled (compromised registry, stale domain, or a malicious mirror)\n- network: MitM on the upstream connection in environments where the upstream is reachable over insecure transport or a compromised network path\n\n## affected components\n\n- `registry/proxy/proxyauth.go:66-81` (`getAuthURLs`): extracts bearer `realm` from upstream `WWW-Authenticate` without validating destination\n- `internal/client/auth/session.go:485-510` (`fetchToken`): uses the realm URL directly for token fetch\n- `internal/client/auth/session.go:429-434` (`fetchTokenWithBasicAuth`): sends credentials via basic auth to the realm URL\n\n## reproduction\n\nattachment: `poc.zip` (local harness) with canonical and control runs.\n\nthe harness is local and does not contact a real registry: it uses two local HTTP servers (upstream + attacker token service) to demonstrate whether basic auth is sent to an attacker-chosen realm.\n\n```bash\nunzip -q -o poc.zip -d poc\ncd poc\nmake canonical\nmake control\n```\n\nexpected output (excerpt):\n\n```\n[CALLSITE_HIT]: getAuthURLs::configureAuth\n[PROOF_MARKER]: basic_auth_sent=true realm_host=127.0.0.1 account_param=user authorization_prefix=Basic\n```\n\ncontrol output (excerpt):\n\n```\n[CALLSITE_HIT]: getAuthURLs::configureAuth\n[NC_MARKER]: realm_validation=PASS basic_auth_sent=false\n```\n\n## suggested remediation\n\nvalidate that the token `realm` destination is within the intended trust boundary before associating credentials with it or sending any authentication to it. one conservative option is strict same-host binding: only accept a realm whose host matches the configured upstream host.\n\n## fix accepted when\n\n- distribution does not send configured upstream credentials to an attacker-chosen realm URL\n- a regression test covers the canonical and blocked cases\n\n[addendum.md](https://github.com/user-attachments/files/24984637/addendum.md)\n[poc.zip](https://github.com/user-attachments/files/24984638/poc.zip)\n[PR_DESCRIPTION.md](https://github.com/user-attachments/files/24984639/PR_DESCRIPTION.md)\n[RUNNABLE_POC.md](https://github.com/user-attachments/files/24984640/RUNNABLE_POC.md)\n\n\nbest,\noleh\n\n## Affected packages\n\n- `github.com/distribution/distribution/v3 < 3.1.0`\n- `github.com/distribution/distribution <= 2.8.3`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `github.com/distribution/distribution/v3 3.1.0`","depth":"twilight","depthScore":41,"depthScoreParts":{"impact":41.3,"likelihood":0.1,"exploitation":0,"ransomware":0},"changes":[{"seq":201606,"id":"CVE-2026-33540","ts":1789399526629,"field":"cvss","old":null,"new":"7.5"},{"seq":201605,"id":"CVE-2026-33540","ts":1789399526629,"field":"severity","old":"none","new":"high"},{"seq":200338,"id":"CVE-2026-33540","ts":1789397123270,"field":"cvss","old":"7.5","new":null},{"seq":200337,"id":"CVE-2026-33540","ts":1789397123270,"field":"severity","old":"high","new":"none"},{"seq":198262,"id":"CVE-2026-33540","ts":1789391763764,"field":"cvss","old":null,"new":"7.5"},{"seq":198261,"id":"CVE-2026-33540","ts":1789391763764,"field":"severity","old":"none","new":"high"},{"seq":196055,"id":"CVE-2026-33540","ts":1789383418032,"field":"cvss","old":"7.5","new":null},{"seq":196054,"id":"CVE-2026-33540","ts":1789383418032,"field":"severity","old":"high","new":"none"},{"seq":194984,"id":"CVE-2026-33540","ts":1789380327923,"field":"cvss","old":null,"new":"7.5"},{"seq":194983,"id":"CVE-2026-33540","ts":1789380327923,"field":"severity","old":"none","new":"high"},{"seq":193771,"id":"CVE-2026-33540","ts":1789378283887,"field":"cvss","old":"7.5","new":null},{"seq":193770,"id":"CVE-2026-33540","ts":1789378283887,"field":"severity","old":"high","new":"none"},{"seq":192558,"id":"CVE-2026-33540","ts":1789376249048,"field":"cvss","old":null,"new":"7.5"},{"seq":192557,"id":"CVE-2026-33540","ts":1789376249048,"field":"severity","old":"none","new":"high"},{"seq":191345,"id":"CVE-2026-33540","ts":1789373151849,"field":"cvss","old":"7.5","new":null},{"seq":191344,"id":"CVE-2026-33540","ts":1789373151849,"field":"severity","old":"high","new":"none"},{"seq":190130,"id":"CVE-2026-33540","ts":1789369139809,"field":"cvss","old":null,"new":"7.5"},{"seq":190129,"id":"CVE-2026-33540","ts":1789369139809,"field":"severity","old":"none","new":"high"},{"seq":188917,"id":"CVE-2026-33540","ts":1789368053132,"field":"cvss","old":"7.5","new":null},{"seq":188916,"id":"CVE-2026-33540","ts":1789368053132,"field":"severity","old":"high","new":"none"},{"seq":187700,"id":"CVE-2026-33540","ts":1789365013202,"field":"cvss","old":null,"new":"7.5"},{"seq":187699,"id":"CVE-2026-33540","ts":1789365013202,"field":"severity","old":"none","new":"high"},{"seq":186487,"id":"CVE-2026-33540","ts":1789363027794,"field":"cvss","old":"7.5","new":null},{"seq":186486,"id":"CVE-2026-33540","ts":1789363027794,"field":"severity","old":"high","new":"none"},{"seq":185273,"id":"CVE-2026-33540","ts":1789360973672,"field":"cvss","old":null,"new":"7.5"},{"seq":185272,"id":"CVE-2026-33540","ts":1789360973672,"field":"severity","old":"none","new":"high"},{"seq":184060,"id":"CVE-2026-33540","ts":1789357953662,"field":"cvss","old":"7.5","new":null},{"seq":184059,"id":"CVE-2026-33540","ts":1789357953662,"field":"severity","old":"high","new":"none"},{"seq":182312,"id":"CVE-2026-33540","ts":1789354111158,"field":"cvss","old":null,"new":"7.5"},{"seq":182311,"id":"CVE-2026-33540","ts":1789354111158,"field":"severity","old":"none","new":"high"},{"seq":181105,"id":"CVE-2026-33540","ts":1789352966430,"field":"cvss","old":"7.5","new":null},{"seq":181104,"id":"CVE-2026-33540","ts":1789352966430,"field":"severity","old":"high","new":"none"},{"seq":179898,"id":"CVE-2026-33540","ts":1789350015850,"field":"cvss","old":null,"new":"7.5"},{"seq":179897,"id":"CVE-2026-33540","ts":1789350015850,"field":"severity","old":"none","new":"high"},{"seq":178691,"id":"CVE-2026-33540","ts":1789347841930,"field":"cvss","old":"7.5","new":null},{"seq":178690,"id":"CVE-2026-33540","ts":1789347841930,"field":"severity","old":"high","new":"none"},{"seq":177484,"id":"CVE-2026-33540","ts":1789346192778,"field":"cvss","old":null,"new":"7.5"},{"seq":177483,"id":"CVE-2026-33540","ts":1789346192778,"field":"severity","old":"none","new":"high"},{"seq":176277,"id":"CVE-2026-33540","ts":1789342764879,"field":"cvss","old":"7.5","new":null},{"seq":176276,"id":"CVE-2026-33540","ts":1789342764879,"field":"severity","old":"high","new":"none"},{"seq":176061,"id":"CVE-2026-33540","ts":1789342379156,"field":"cvss","old":null,"new":"7.5"},{"seq":176060,"id":"CVE-2026-33540","ts":1789342379156,"field":"severity","old":"none","new":"high"},{"seq":175601,"id":"CVE-2026-33540","ts":1789338394171,"field":"cvss","old":"7.5","new":null},{"seq":175600,"id":"CVE-2026-33540","ts":1789338394171,"field":"severity","old":"high","new":"none"},{"seq":174396,"id":"CVE-2026-33540","ts":1789334608212,"field":"cvss","old":null,"new":"7.5"},{"seq":174395,"id":"CVE-2026-33540","ts":1789334608212,"field":"severity","old":"none","new":"high"},{"seq":173191,"id":"CVE-2026-33540","ts":1789333222945,"field":"cvss","old":"7.5","new":null},{"seq":173190,"id":"CVE-2026-33540","ts":1789333222945,"field":"severity","old":"high","new":"none"},{"seq":172005,"id":"CVE-2026-33540","ts":1789330905116,"field":"cvss","old":null,"new":"7.5"},{"seq":172004,"id":"CVE-2026-33540","ts":1789330905116,"field":"severity","old":"none","new":"high"}]}