Un token de GitHub de hace 3 años, una imagen de Docker publica y 25 minutos: la cadena que casi abre Baseten

Un token de GitHub de hace 3 años, una imagen de Docker publica y 25 minutos: la cadena que casi abre Baseten

Un token de GitHub de hace 3 años, una imagen de Docker publica y 25 minutos: la cadena que casi abre Baseten

La historia es casi de manual, y por eso mismo vale la pena contarla. Los de Strix, una empresa de seguridad, querían usar Baseten para inferencia. Antes de meterles sus datos, decidieron escanearlos. Veinticinco minutos después tenían un token de GitHub vivo, con permisos de administrador sobre repositorios internos de Baseten. Sin cero-day, sin phishing, sin nada exótico.

La cadena, paso a paso

Todo partió por un registro Harbor en un subdominio: gcp-us-east4-zlw.registry.baseten.co. Uno de los proyectos era público y cualquiera podía listar repositorios, pedir tokens anónimos de pull y bajarse las imágenes completas. Hasta ahí es común: mucha gente publica imágenes a propósito y un scanner bien hecho no reporta eso como hallazgo todavía.

Lo interesante vino después. Bajaron la imagen baseten/baseten-app, encontraron un par de llaves AWS (muertas, ok), y en vez de rendirse miraron el historial de build de la imagen. Ahí estaba el detalle que casi nadie revisa: Docker guarda la metadata del build, incluidos los argumentos que se le pasaron. Y en un paso de build del 3 de marzo de 2023, alguien había pasado un token de GitHub como build argument para bajar dependencias privadas. Borrar el archivo de credenciales no sirvió de nada si el historial tenía otra copia del token.

El token seguía vivo más de tres años después, con scope repo y permisos de admin en repositorios de producto y deployment. Ese es el punto que me duele: el token solo necesitaba lectura de dependencias, pero tenía acceso a todo. El blast radius de una fuga es un reflejo directo de cuántos permisos le diste al token, no de dónde se fugó.

Lo que me parece relevante acá

Primero, la humildad de reconocer que el hallazgo está en un lugar que nadie mira. Escaneás la aplicación, escaneás el código fuente, y se te olvida una imagen de contenedor vieja. Y si escaneás los archivos de la imagen, igual tenés que revisar el historial de build con docker history –no-trunc, porque la config de la imagen se baja junto con ella.

Segundo, el arreglo es conocido y gratis: BuildKit secret mounts en vez de build arguments, tokens con permisos mínimos y fecha de expiración. Nada de esto es nuevo. Docker incluso lo advierte en su documentación. Es la diferencia entre saber algo y operarlo todos los días, que es donde se cae la mayoría.

Tercero, y acá va mi opinión: Baseten se portó bien. Confirmaron como crítico, bloquearon el proyecto y rotaron el token al día siguiente. Eso es lo mínimo esperable, pero en la práctica es más de lo que muchos logran. El problema no era falta de herramientas —usaban tooling de seguridad con IA—, era un token viejo en un rincón que nadie auditaba.

Mi takeaway

Si trabajás con contenedores y GitHub, hazte este ejercicio hoy: mira qué puede bajar alguien sin loguearse, revisa el historial de build de tus imágenes, y pregunta qué puede hacer cada token que usas para autenticarte contra GitHub. Fetching una dependencia requiere lectura de esa dependencia, nada más. Si tu token de build tiene admin sobre los repos de producción, un leak cualquiera se convierte en un incidente serio.

Y si a los 25 minutos un agente de IA puede encontrar un token admin vivo en una imagen vieja, asume que los atacantes van a encontrar lo mismo. La pregunta no es si tus secretos están en algún lugar que olvidaste, es cuánto van a esperar ahí hasta que alguien los encuentre.

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 *