Un token de GitHub dormido tres años en una imagen Docker: la filtración de Baseten

Un token de GitHub dormido tres años en una imagen Docker: la filtración de Baseten

Un token de GitHub dormido tres años en una imagen Docker: la filtración de Baseten

La semana pasada me crucé con una historia que debería leer todo el que construye imágenes Docker para vivir. El equipo de Strix, una empresa que hace un agente de hacking autónomo, estaba evaluando a Baseten —plataforma de inferencia valuada en 13 mil millones de dólares— antes de mandarle sus datos. Decidieron escanearla primero. En 25 minutos obtuvieron un token de GitHub con permisos de admin sobre repositorios internos de Baseten. Y lo peor: el token era de marzo de 2023 y seguía funcionando en julio de 2026.

La cadena del desastre

El camino fue bastante simple de seguir. Primero, un registro de contenedores Harbor en un subdominio de Baseten tenía un proyecto público. Sin ningún token, cualquiera podía listar repositorios, pedir tokens de pull anónimos y descargar los manifiestos y blobs de las imágenes. Uno de esos blobs era la imagen baseten-app.

Aquí viene el detalle técnico que me parece la lección central de todo esto: la credencial no estaba en un archivo dentro del contenedor. Estaba en la historia de build de la imagen, en el campo history[].created_by del config. Alguien había pasado un token de GitHub como build argument para descargar dependencias privadas, y Docker grabó el valor expandido directamente en los metadatos. Borren el archivo de credenciales del Dockerfile lo que quieran: si el token quedó en la historia, la imagen lo sigue filtrando para siempre.

El alcance era grave de verdad

El token pertenecía a la cuenta basetenbot y tenía scope repo con permisos de admin y push sobre tres repositorios críticos: el código fuente principal del producto, el repositorio de GitOps que define el estado de los clusters de producción, y la distribución de su CLI (por donde los desarrolladores instalan su tooling). Además acceso de lectura/escritura a repos privados que incluían un directorio con subcarpetas por cada cliente.

Dicho en cristiano: un token viejo y olvidado en una imagen descargable públicamente daba ruta directa para un ataque de supply chain contra una plataforma de inferencia que usa gente seria. Podías haber alterado el código que otras empresas ejecutan para correr sus modelos. Eso no es una vulnerabilidad teórica, eso es un mal día para todos.

Mi lectura

Lo primero que pienso es que esto no pasa por mala suerte, pasa por hipótesis falsas sobre qué contiene una imagen. Todos escaneamos las capas del filesystem buscando claves hardcodeadas, pero casi nadie revisa el config y su historia de build. Es metadatos, no código, y por eso se escapa de los escanners habituales y de la intuición. Yo mismo he hecho builds con ARG GITHUB_TOKEN en la pega, y ver este caso escrito en negro sobre blanco te cambia la manera de mirar tus propias imágenes.

Lo segundo: el archivo del token del grupo de seguridad de Baseten fue digno. Confirmaron como crítico, bloquearon el proyecto del registro y rotaron el token al día siguiente de la tarde. Eso merece reconocerse, porque en el mundo real muchos equipos responden con silencio y abogados.

Y tercero, la parte que más me incomoda: quién encontró esto fue un agente autónomo de seguridad. No un pentester humano con coffee y checklist, sino software que enumeró hosts, revisó logs de certificados, bajó una imagen, probó una credencial muerta, siguió buscando, encontró otra viva, y verificó sus permisos con requests de solo lectura. La automatización ofensiva ya no es un experimento; está encontrando cosas que llevan tres años ahí.

Qué hacer esta semana

Si construyes imágenes, tres acciones concretas. Uno: usa secret mounts de BuildKit en vez de build args para cualquier credencial, y jamás pases tokens por ARG. Dos: audita el history de tus imágenes publicadas, no solo las capas — herramientas como TruffleHog sobre los blobs y el config encuentran lo que un escaneo de archivos se salta. Tres: rota tokens viejos aunque el Dockerfile ya esté corregido; una imagen que alguien ya descargó no se puede arreglar editando código.

El costo de esta wea no fue un exploit sofisticado. Fue un build normal de 2023, un subdominio olvidado y tres años de nadie revisando metadatos. Las filtraciones rara vez son exóticas; casi siempre son aburridas. Y eso, al final, es la mejor noticia: porque lo aburrido se arregla con hábitos, no con milagros.

Fuente de inspiración: We wanted to use Baseten for inference. We ended up with admin access to Baseten GitHub repos

Comentarios

Aún no hay comentarios. ¿Por qué no comienzas el debate?

Deja una respuesta

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