Seguridad

CVE-2026-105445 en Docker Hub: el bug GhostPAT que reasignaba tokens personales durante 2 años

TL;DR — Lo que necesitas saber hoy

  • Vulnerabilidad: CVE-2026-105445 (GhostPAT) en Docker Hub, CVSS 4.0 = 9.4 (crítica).
  • Ventana expuesta: 26 de agosto de 2024 al 30 de septiembre de 2026 (más de 2 años).
  • Impacto: un atacante podía reasignarte tokens personales (PAT) creados por él a tu cuenta de Docker Hub.
  • Acción hoy: revisa Docker Hub > Account settings > Personal access tokens y revoca lo que no reconozcas. Para CI/CD usa organization access tokens, no PATs personales.
  • Explotación observada: Docker revisó ~304.000 pushes con tokens transferidos y no encontró evidencia de abuso externo.

Qué pasó exactamente

El 30 de septiembre de 2026 a las 11:40 hora del este de EE. UU., dos investigadores (Peter Molnar y Krisztian Hozak, de mptrdev.com) reportaron a Docker, vía su Vulnerability Disclosure Program a [email protected], una falla en el flujo de conversión de cuenta personal a organización de Docker Hub. Docker confirmó el reporte internamente con cuentas de prueba alrededor de la 1:30 PM, desplegó el fix a producción a las 3:10 PM (menos de cuatro horas después del recibo) y comenzó a notificar a usuarios afectados el 2 de octubre a las 5:30 PM. La divulgación pública se publicó en el blog de Docker el 12 de octubre y el NVD de NIST la indexó el 13 de octubre bajo CVE-2026-105445.

El agujero estuvo abierto, sin ser detectado públicamente, desde el 26 de agosto de 2024 a las 20:33 UTC hasta el 30 de septiembre de 2026 cerca de las 19:10 UTC, es decir, poco más de dos años. La falla fue bautizada GhostPAT por la comunidad de seguridad (y adoptada por Docker) porque el atacante podía fantasma un token en tu cuenta y seguir usándolo sin que te enteraras.

El nombre técnico de la categoría de la falla según CWE es CWE-266: Incorrect Privilege Assignment. La severidad asignada por Docker y confirmada por NVD es CVSS 4.0 = 9.4, en el rango crítico. ¿Por qué tan alta? Porque desde un token robado-sin-robo se puede publicar una imagen de Docker bajo tu nombre oficial, lo cual toca directamente la cadena de suministro de software de cualquier persona que haga docker pull sobre tus repos.

El fallo técnico: CVE-2026-105445 (GhostPAT)

El bug vive en una de las funciones de administración de Docker Hub: convertir una cuenta personal en una organización. Este flujo existe porque Docker Hub no tiene forma nativa de mover un repositorio de un dueño a otro. La forma soportada de “mover” alice/app a un dueño distinto es convertir alice en organización. Antes de la corrección, ese camino transfería además todos los Personal Access Tokens (PATs) que Alice había emitido, sin avisar al nuevo dueño ni rotar ni revocar los secretos.

Atributo Detalle
CVE CVE-2026-105445 (alias GhostPAT)
Componente Docker Hub, flujo Account-to-Organization conversion
Tipo CWE-266: Incorrect Privilege Assignment
Severidad CVSS 4.0 = 9.4 (crítica)
Versiones afectadas Docker Hub entre 2024-08-26 20:33 UTC y 2026-09-30 ~19:10 UTC
Versión parchada Fix en producción (Docker Hub como SaaS, no requiere parche del lado del cliente)
Reportado por Peter Molnar y Krisztian Hozak (mptrdev.com)
Advisory oficial docker.com/blog/docker-hub-security-advisory-cve-2026-105445
Write-up técnico ghostpat.com

Cómo funciona el ataque paso a paso

El ataque no requiere comprometerte. No requiere phishing. No requiere tu contraseña. Tampoco tu segundo factor. El atacante solo necesita estar autenticado en Docker Hub con su propia cuenta, algo que cualquier persona puede hacer registrándose con un correo. La cadena de eventos que los investigadores documentaron es la siguiente:

  1. El atacante crea una cuenta cualquiera en Docker Hub, por ejemplo attacker42.
  2. Desde esa cuenta genera un Personal Access Token (PAT) y guarda el secreto. El PAT tiene los permisos por defecto que el atacante eligió al crearlo (read, write, delete, scope por repositorio u organización).
  3. El atacante invoca la función Convert account to organization sobre attacker42. Como parte del flujo, debe nombrar a un usuario existente en Docker Hub como el nuevo owner de la organización recién creada. Aquí pone tu usuario, por ejemplo victim-maintainer.
  4. Al completarse la conversión, Docker Hub cambiaba el registro de propiedad del PAT. El secreto del token no se rotaba ni se revocaba. El nuevo “dueño” del registro del token pasaba a ser victim-maintainer, pero el secreto seguía en manos del atacante.
  5. Cuando el atacante ejecuta docker login y docker push usando ese PAT, el registro de Docker Hub lo autentica como victim-maintainer y todas las acciones (pull, push, delete) quedan registradas con tu identidad. Si además tienes permisos sobre organizaciones a las que perteneces, las acciones se realizan dentro de esos repositorios de organización también.

Lo más delicado del flujo es lo que no pasa: ni el nombrado como dueño recibe un email, ni una notificación push, ni una entrada de auditoría sobre la transferencia del token. Si no tenías un inventario riguroso de qué tokens tenía tu cuenta y cuándo se crearon, podías mirar tu lista de PATs y pensar que todo estaba normal mientras un atacante empujaba imágenes firmadas con tu nombre.

Reproducción del ataque (en defensa)

Los investigadores publicaron una prueba de concepto en ghostpat.com. Los pasos conceptuales que ellos mismos documentaron son:

# 1. Autenticarse en Docker Hub con una cuenta recién creada
docker login -u attacker_test
# introducir PAT propio cuando Docker lo pida
# 2. Convertir esa cuenta en organización desde la UI web
# (Hub -> Account settings -> Convert account to organization)
# Esto requiere nombrar otra cuenta de Docker Hub como owner.
# Nombrar a un usuario objetivo (la víctima).
# 3. Después de la conversión, ejecutar un push contra el repo de la víctima
docker push victim-maintainer/app-oficial:latest
# Si el push tiene éxito, significa que el PAT original
# fue reasignado y sigue funcionando bajo la identidad de la víctima.

Troubleshooting: si intentas reproducir este flujo hoy en Docker Hub, vas a ver que el paso de conversión ya no transfiere PATs. La documentación oficial actualizada dice: “During the conversion, personal access tokens are not transferred. You must recreate access tokens after the conversion and grant them the appropriate scopes.” Los nuevos organization access tokens (Org-OAT) son la vía recomendada para automatizaciones que pertenecen a una organización.

El fix: tres controles nuevos en Docker Hub

El fix inmediato fue retirar la transferencia de PATs del flujo de conversión. Para que el vector no se cuele otra vez por otro camino similar, Docker está desplegando tres controles adicionales que documentan en el advisory:

  • Consentimiento explícito del nombrado como dueño: cuando un usuario es designado como owner de una organización durante una conversión, debe confirmar activamente. Ya no se asume la aceptación por silencio.
  • Notificación automática al recibir propiedad: el dueño nombrado recibe un email describiendo qué cuenta se convirtió, quién la convirtió, y cuándo. Antes este aviso no existía.
  • Historial auditable de cambios de propiedad de tokens: Docker registra cada cambio de owner de un token en una bitácora consultable. Antes era imposible reconstruir quién tenía qué token en una fecha pasada.

Docker también anunció que está revisando otros flujos que cambian propiedad de credenciales (ownership transfers, account merges, sub-account handoffs) buscando el mismo patrón. Esto es la parte que más impacto tiene a largo plazo: si encontraba otro caso similar, lo anuncia en el mismo hilo del advisory.

Qué podía hacer un atacante y qué no

Es importante dimensionar el riesgo con precisión. El atacante que lograba un PAT reasignado podía, dentro del alcance del token:

  • Hacer docker pull sobre repos privados tuyos y de las organizaciones donde tenías acceso.
  • Hacer docker push sobre esos mismos repos, publicando imágenes con tu identidad. Esto es lo que convierte la falla en un riesgo de cadena de suministro: si publicas imágenes oficiales, un atacante puede plantar una actualización maliciosa bajo tu nombre y cualquiera que haga docker pull se la baja.
  • Hacer docker rmi o borrar tags en repos donde tuviera permisos.

Lo que no podía hacer:

  • Entrar a la interfaz web de Docker Hub con tu identidad (el PAT no es un session token web).
  • Cambiar tu contraseña, tu correo de recuperación ni tu configuración de 2FA.
  • Hacerse dueño pleno de tu cuenta. La puerta era solo a nivel de registro.

La investigación forense de Docker: 304.000 pushes analizados

Después del fix, Docker cruzó el listado completo de conversiones de cuenta ocurridas desde agosto de 2024 con los registros de uso de tokens y con los logs del registry. La métrica concreta que publicaron: alrededor de 304.000 pushes fueron realizados con un token que técnicamente había sido transferido vía este flujo. De esos, ninguno aterrizó en repos de un usuario u organización no relacionada con la conversión.

Los pushes cayeron en dos casos mayoritarios:

  1. En la propia organización recién creada por el dueño que disparó la conversión (caso normal y previsto).
  2. En repos del nuevo owner nombrado, cuando ambos cuentas pertenecían a la misma persona o equipo (el caso típico de “yo me hice una org con mi segunda cuenta para organizar mis proyectos”).

Sobre el subconjunto más riesgoso (imágenes públicas empujadas después de una conversión donde no se pudo establecer que ambas cuentas eran de la misma persona), Docker ejecutó un análisis estático sin ejecutar las imágenes. Resultado: ningún malware, ningún cryptominer, nada de comando y control, ningún harvest de credenciales, ninguna manipulación detectable. La conclusión pública es que no hay evidencia de explotación real de la vulnerabilidad durante la ventana de dos años.

Docker también aclara un límite de esa afirmación: los logs del registry muestran cuándo se usa un token, no cuáles tokens existen. Un PAT transferido pero nunca usado deja cero rastro. Por eso trataron cada token transferido como potencialmente afectado, sin depender de la detección para decidir a quién notificar.

Cómo comprobar si estás afectado (en menos de 5 minutos)

Si Docker te envió un correo el 2 de octubre de 2026 con el asunto “Docker Hub Vulnerability Identified & Resolved”, estabas en el grupo afectado y Docker ya desactivó los PATs transferidos (salvo que hayas pedido conservar alguno antes del 8 de octubre). Si no recibiste ese correo, técnicamente no estás en la lista expuesta. Aun así, vale la pena pasar por esta auditoría manual para descartar transferencias que hayan ocurrido antes del barrido de Docker:

# 1. Inicia sesión en https://hub.docker.com y abre
#    Account settings -> Personal access tokens.
# 2. Revisa cada token listado:
#    - ¿Reconoces cuándo se creó?
#    - ¿Reconoces el alcance (read/write/delete)?
#    - ¿El "Last used" coincide con la última vez que recuerdas usarlo?
# 3. Si un token te suena raro, click en "Revoke" y listo.
#    (Docker ya no permite acciones masivas; hay que revocar uno por uno.)
# 4. Si tu organización usa Docker Hub, abrí la sección
#    "Organization -> Settings -> Access tokens" y revisa los
#    Organization Access Tokens existentes.
#    Los OAT no estaban expuestos al bug, pero es buen momento
#    para confirmar que cada uno sigue siendo necesario.

Troubleshooting: si tu organización ya migró todo a organization access tokens, esta auditoría es trivial, queda vacía. Si sigues usando PATs personales para pipelines heredados, este es el momento de migrar. Cada credential asociado a una persona es un credential que esa persona puede perder por una conversión, un traspaso o un descuido. Los tokens de organización sobreviven al personal.

Auditoría programática de tus tokens con la API REST

Si prefieres evitar la UI web y revisar tus tokens desde una terminal, Docker Hub expone una API REST autenticada por PAT. Listar todos los tokens activos toma un solo curl; lo útil es revisar la fecha de creación (created_at) y la del último uso (last_used) contra tu propio inventario mental.

# 1. Crear una variable con un PAT propio de solo lectura (read-only)
#    para esta auditoría. NO uses un token con scope write/delete.
DOCKER_READONLY_PAT="dckr_pat_xxxxxxxxxxxxxxxxxxxxxxxx"
# 2. Listar los tokens visibles desde tu cuenta
curl -s -u "victim-maintainer:${DOCKER_READONLY_PAT}" 
  https://hub.docker.com/v2/access-tokens/ 
  | jq '.tokens[] | {uuid: .uuid, created_at: .created_at, last_used: .last_used, scope: .scope}'
# 3. Buscar tokens con last_used nulo o anterior a 2024-08-26
#    (eso sugiere que el token fue reasignado y nunca se usó desde tu lado).

Troubleshooting: si jq no está en la máquina, curl ... | python3 -m json.tool produce la misma legibilidad. Si Docker Hub rate-limita la API (suele pasar en auditorías masivas desde la misma IP), espera sesenta segundos y reintenta; el endpoint /v2/access-tokens/ no necesita paginación salvo que tengas más de 50 tokens.

Contramedidas para tu pipeline CI/CD

Independientemente del estado de tu cuenta, este incidente es un buen disparador para endurecer el flujo de tokens en tu equipo. Tres acciones concretas que puedes aplicar hoy sin esperar a que termine una investigación:

# 1. Migrar tus pipelines a Docker Hub Organization Access Tokens
#    Settings de la organización -> Access tokens -> Create
#    Asignar scope de read-only para jobs de pull, read+write
#    para jobs de push, pero solo en los repos que corresponda.
# 2. Rotar los PATs personales que ya estén en uso
#    Account settings -> Personal access tokens -> Revoke + recrear
#    Actualizar el secreto en tu CI (GitHub Actions secret, GitLab
#    CI variable, Jenkins credentials, etc.) y redeploy.
# 3. Habilitar 2FA en la cuenta y exigirla en la organización
#    Si bien el bug GhostPAT no esquiva 2FA, mantener 2FA activo
#    sigue siendo el piso mínimo de defensa contra el credential
#    reuse clásico.
Validar el organization access token recién emitido

Una vez creado el OAT en Docker Hub, lo correcto es validar que autentica contra los repos correctos antes de migrar el secreto de tu CI. La validación mínima es un docker pull de prueba contra una imagen que exista en la organización y un docker push sobre una imagen de scratch sin impacto.

# 1. Login con el OAT nuevo (no con tu PAT personal)
echo "$OAT_SECRET" | docker login hub.docker.com -u "rogmoreno-org" --password-stdin
# 2. Validar pull contra una imagen inmutable de la organización
docker pull rogmoreno-org/busybox-test:ci-canary
# 3. Validar push con un tag efímero (no toca imágenes de release)
docker tag alpine:3.20 rogmoreno-org/alpine:oat-validate-$(date +%s)
docker push rogmoreno-org/alpine:oat-validate-$(date +%s)
docker rmi rogmoreno-org/alpine:oat-validate-*  # limpiar inmediatamente
# 4. Si ambos comandos retornan exit 0, el OAT está bien. Reemplaza el PAT
#    personal en tu CI y revoca el viejo.

Troubleshooting: si el push retorna denied: requested access to the resource is denied, el OAT no tiene el scope read, write para ese repo. revisa la matriz de permisos del OAT en Organization → Settings → Access tokens → Manage scopes. Si retorna unauthorized: authentication required, el token venció o quedó mal copiado en la variable OAT_SECRET; regeneralo.

>

Si tu pipeline está en Kubernetes o usa un registry privado mirror, vale la pena revisar también cómo está inyectado el secreto. Un PAT personal en un Secret de Kubernetes cuyo dueño rotó el token fuera de la rotación del cluster queda huerfano. Lo correcto es que el secret sea propiedad del namespace y rote de forma sincronizada con el PAT.

Por qué importa más allá de Docker Hub

El patrón detrás de GhostPAT es general y Docker no es ni será el último en tenerlo. La lección no es "Docker tuvo un bug grave". La lección es que cualquier transferencia de propiedad de credencial sin consentimiento explícito del nuevo dueño es un patrón de falla que se repite, en todos los proveedores, en todas las nubes. Cinco puntos que aplican al estado del ecosistema en 2026:

1. Los servicios que no son SaaS puros también tienen estados heredados. Docker Hub es SaaS, sí, pero la función de conversión vino heredada de cuando Hub era un repositorio de pago con planes de equipo. El código que valida la conversión creció orgánicamente y nadie actualizó la lógica de tokens porque "no era el flujo principal". Esto se repite: en cualquier sistema con un feature antiguo, auditar el feature y no el feature popular.

2. Las credenciales de larga duración son siempre una bomba de tiempo. Los PATs de Docker Hub (y sus equivalentes en GitHub, GitLab, NPM, PyPI) son tokens de larga duración. Cuando un vendor finalmente rota hacia tokens de corta duración + OIDC federation (como ya hicieron GitHub Actions y GitLab), se cierra una clase entera de bugs de reasignación. Si tu organización todavía depende de PATs, acelera la migración a OIDC.

3. La auditoría de "qué tokens están vivos en mi organización" es un control escaso. Pocas organizaciones mantienen un inventario vivo de tokens emitidos, último uso, dueño y propósito. Después de GhostPAT, tener ese inventario debería ser tabla. No solo para Docker Hub, para cada registry, cada cloud, cada servicio.

4. La notificación al afectado es regulación que llegó tarde. Cuando un sistema cambia de dueño una credencial sin avisar al nuevo dueño, está cargando la responsabilidad sobre el usuario. Después de GhostPAT, y de incidentes similares en GitHub y AWS IAM en años recientes, la expectativa razonable es que cualquier transferencia de propiedad de credencial requiera consentimiento explícito.

5. El software supply chain vive o muere en la registry. La registry de Docker es el eslabón donde se firma la confianza entre autor y consumidor. Cualquier falla que permita impersonar al autor es, por definición, una falla de cadena de suministro. Las defensas tienen que estar a la altura: tokens de corta duración, attestations firmadas (Sigstore / cosign), y revisión obligatoria de origen de las imágenes que se despliegan.

Verificar imágenes publicadas durante la ventana expuesta

Si mantienes imágenes públicas y quieres descartar que alguna versión publicada entre agosto de 2024 y septiembre de 2026 haya sido reemplazada por un push de un atacante, hay tres controles cruzados que aplican: el historial de tags en el registry, los digest inmutables de las capas, y la firma cosign si firmás tus releases.

# 1. Listar todos los tags públicos de una imagen con su fecha de update
skopeo inspect --raw docker://rogmoreno-org/app-oficial 2>/dev/null 
  | jq -r '.[] | "(.Name)t(.UpdatedAt)"'
# 2. Comparar contra el log interno de tu pipeline: tags cuya fecha
#    NO corresponda con un build en CI son candidatos para auditoría.
# 3. Verificar firma cosign si la usas
cosign verify --certificate-identity-regexp ".*" 
  --certificate-oidc-issuer "https://accounts.google.com" 
  rogmoreno-org/app-oficial:latest
# 4. Si una imagen carece de firma y fue publicada durante la ventana,
#    rebuildala, firmala, y republica con digest explícito.

Troubleshooting: si skopeo no está instalado, crane de go-containerregistry es un reemplazo directo. Si cosign verify falla por falta de --certificate-identity, viajá sin la restricción la primera pasada para listar qué tags tienen firma y cuáles no; después afinás.

Conclusión para sysadmins y mantenedores

Si usas Docker Hub y tienes una cuenta personal, pasa cinco minutos hoy mismo por la sección Personal access tokens y revoca lo que no reconozcas. Si mantienes imágenes públicas, este incidente es un recordatorio de por qué organization access tokens + pipelines firmados con cosign son mejores que PATs personales con scopes amplios. Si administras una flota de builders que usan Docker Hub, la auditoría de credenciales se vuelve un item crítico en tu próxima revisión de seguridad.

El fix ya está desplegado y, según la investigación de Docker, no hubo explotación observable. Aún así, el hecho de que el bug haya estado abierto dos años sin que nadie lo reportara antes dice algo sobre lo difícil que es detectar, sin herramientas externas, transferencias invisibles dentro de un SaaS cerrado. Hasta que los vendors adopten tokens de corta duración por defecto, la única defensa constante es la hygiene local.

Para cerrar: si encuentras un comportamiento raro en cualquier proveedor de tokens (Docker Hub, GitHub, GitLab, Quay, GHCR, ECR público), reportalo a security@<vendor>.com. Disclosure responsable funciona porque Peter y Krisztian lo demostraron acá, y porque Docker respondió en menos de cuatro horas.

Preguntas frecuentes

¿Qué es CVE-2026-105445 (GhostPAT) y por qué es importante para sysadmins?

Es una falla de tipo privilege assignment en Docker Hub que, entre agosto de 2024 y septiembre de 2026, permitía reasignar un Personal Access Token a un tercero durante la conversión de cuenta a organización, sin avisar al receptor. Para sysadmins importa porque cualquier imagen publicada bajo tu identidad es, por definición, cadena de suministro.

¿Cómo sé si estoy afectado por la vulnerabilidad?

Docker envió el 2 de octubre de 2026 un correo a los usuarios expuestos. Si no lo recibiste, no estás en la lista directa de afectados. Aun así, se recomienda revisar Personal access tokens en Docker Hub y revocar los que no reconozcas.

¿Qué debería usar en lugar de un Personal Access Token?

Para pipelines de CI/CD, organization access tokens (OAT) con scope mínimo. Para integraciones modernas, OIDC federation con tu proveedor de CI (GitHub Actions, GitLab CI, CircleCI) para evitar tokens de larga duración por completo.

¿Cómo migro de un PAT personal a un organization access token sin romper mis pipelines?

Primero emites el OAT en la organización con el mismo scope que el PAT actual. Después actualizas la variable de secreto en tu CI sin hacer push aún. haz una corrida de prueba con un job manual. Si todo funciona, revoca el PAT personal. Mantén el OAT rotando cada 90 días aunque el vendor no lo exija.

¿Qué son los 304.000 pushes analizados por Docker?

Docker cruzó todas las conversiones de cuenta ocurridas durante la ventana expuesta con el log del registry, identificó ~304.000 pushes hechos con tokens transferidos y concluyó que todos cayeron en repos del propio dueño o de su organización, sin evidencia de abuso externo.

¿Necesito actualizar algo del lado del cliente para estar protegido?

No. Docker Hub es SaaS y el fix ya está desplegado. Lo único del lado del cliente es la auditoría manual de tokens y la migración a organization access tokens.

Lee también

Alfonso Moreno | Sysadmin Senior

Soy Alfonso, sysadmin senior con RHCE y más de 10 años administrando sistemas Linux para empresas y proyectos personales. Me especializo en infraestructura, redes, virtualización, monitoring, automation y self-hosting. Sobre SoyLinux.com SoyLinux.com es un blog técnico en español sobre Linux, software libre y self-hosting. Lo mantengo en mi tiempo libre. Áreas de expertise - Linux server administration: RHEL, CentOS, Rocky, Alma, Fedora, Debian, Ubuntu - Network services: Postfix, BIND, Samba, NFS, Squid - Virtualización y containers: KVM, Proxmox, Docker, Podman - Monitoring: Prometheus, Grafana, Zabbix - Automation: Ansible, Bash scripting - Self-hosting: Vaultwarden, Nextcloud, Jellyfin, Immich, Homelab Contacto - Email: [email protected]

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *