Envenenamiento de caché de GitHub

Todos los Artículos

¿Sabes lo que sucede bajo el capó de tu CI? Sin un conocimiento profundo, usted podría ser vulnerable a ataques innovadores a la cadena de suministro. Este artículo describe tal ataque.

El almacenamiento en caché se utiliza para acelerar los procesos. En lugar de crear o descargar, una y otra vez, un paquete, guarda de forma sencilla y automática los artefactos para su reutilización. Sin embargo, los cachés pueden estar envenenados. Por ejemplo, una herramienta maliciosa utilizada en un flujo de trabajo de prueba puede envenenar su caché. Más adelante, es posible que otro flujo de trabajo que utilice el mismo caché se vea afectado. Si este flujo de trabajo tiene privilegios más altos, este es de hecho un método para realizar un ataque. En esta publicación, informamos sobre un ataque experimental en una canalización de CI de GitHub, pero existe una vulnerabilidad lógica similar en otros productos de CI.

El ataque se desarrolla de la siguiente manera: 

  1. Un atacante publica una herramienta maliciosa o una acción de Github que es detectada por un flujo de trabajo desprevenido en Github.
  2. El flujo de trabajo está diseñado con un caché.
  3. La carga útil maliciosa modifica el caché para incluir datos maliciosos.
  4. Otros flujos de trabajo que invocan este caché desde este punto podrían verse afectados.

En respuesta a nuestra divulgación, GitHub dijo que no tienen un plan para reforzar la función de caché contra este tipo de ataque. 

Proponemos una mitigación firmando criptográficamente el valor hash del contenido de la caché y verificando la firma antes de su uso. Para obtener más información sobre el ataque y las mitigaciones, siga leyendo.

Ataque de envenenamiento de caché

Caché de GitHub

a menudo reutiliza las mismas salidas o dependencias descargadas de una ejecución a otra (por ejemplo, los paquetes descargados mediante herramientas de administración de paquetes y dependencias como Maven, Gradle, npm y Yarn. Estos paquetes generalmente se guardan en un caché local de las dependencias descargadas).

Para optimizar las ejecuciones de CI, GitHub otorga acceso a un recurso de caché que se puede usar en toda la canalización a través de una API. Las entradas en el caché son una combinación clave-valor, donde las claves se basan en cadenas y los valores son archivos o directorios que uno desea almacenar en caché.

Usando el acción/caché La acción de Git en cualquier parte del CI ejecutará dos pasos: un paso se llevará a cabo durante el corrida proceso cuando se llama y el otro se llevará a cabo después flujo de trabajo (si la acción de ejecución devolvió un error de caché).

  • Ejecutar acción – se utiliza para buscar y recuperar el caché. La búsqueda se realiza utilizando la clave de caché, y el resultado es un acierto de caché (éxito, datos encontrados en la caché) o un error de caché. Si se encuentran, los archivos y directorios se recuperan del caché para su uso activo. Si el resultado es falta de caché, los archivos y directorios deseados se descargan como si fuera la primera vez que se llaman.
  • Acción posterior al flujo de trabajo – utilizado para guardar el caché. Si el resultado de la llamada de caché en la acción de ejecución devuelve un error de caché, esta acción guardará el estado actual de los directorios que queremos almacenar en caché con la clave proporcionada. Esta acción ocurre automáticamente y no es necesario llamarla explícitamente.

Permisos de caché de GitHub

Las restricciones de acceso proporcionan aislamiento de caché y seguridad al crear un límite lógico entre diferentes ramas (por ejemplo: un caché creado para la sucursal Característica-A [con la base principal] no sería accesible para una solicitud de extracción para la sucursal Característica-B [con la base principal]).

La acción de caché primero busca en los aciertos de caché una clave y restaura las claves en la rama que contiene la ejecución del flujo de trabajo. Si no hay coincidencias en la rama actual, la acción de caché busca la clave y restaura las claves en la rama principal y en las ramas ascendentes.

El acceso a un caché está limitado por rama (actual y principal), lo que significa que el acceso se proporciona a todos flujos de trabajo a través de corre de dicha sucursal.

Otra nota importante es que GitHub no permite modificaciones una vez que se envían las entradas: las entradas de la caché son registros de solo lectura.

Alcance de CI de GitHub

GitHub alcances permiten especificar exactamente qué tipo de acceso se necesita para diversas tareas. La CI de GitHub tiene diferentes alcances, cada una con su propio conjunto de valores y características:

  • Máquina virtual (VM) para cada trabajo
  • Permisos de trabajo
  • Alcances del flujo de trabajo
  • Ejecuciones de flujo de trabajo
  • Ramas de Git
  • Tokens de identidad del flujo de trabajo
  • y otros

El alcance de la caché de GitHub se ha definido de una manera que puede romper algunas de las otras restricciones del alcance. (por ejemplo: el caché de GitHub puede afectar múltiples flujos de trabajo).

El ataque

Usamos un CI de ejemplo que incluía dos flujos de trabajo. Este ejemplo muestra cómo un ataque puede pasar de un flujo de trabajo con permisos bajos a uno con permisos altos.

  • Prueba de unidad flujo de trabajo que ejecuta pruebas unitarias y herramientas de cobertura de código. Suponemos que una de las herramientas es maliciosa o vulnerable a la ejecución remota de código. El flujo de trabajo necesita utilizar el acción/caché Gire la acción. Cualquier flujo de trabajo puede acceder al caché.
  • tortugitas El flujo de trabajo crea y libera el artefacto de la aplicación. Este flujo de trabajo utiliza un caché para optimizar el uso de las dependencias de Golang.

El prueba de unidad El flujo de trabajo utiliza una acción maliciosa que agrega una entrada de caché con contenido malicioso al cambiar una biblioteca de registro de Golang. (go.uber.org/zap@v1) para agregar la cadena 'Biblioteca MALA' ​​a la descripción del artefacto de la aplicación.

A continuación, el , El flujo de trabajo utiliza esta entrada de caché envenenada. Como resultado, el código malicioso se inyecta en la imagen y el binario integrados de Golang. La caché permanece envenenada hasta que se descarta la clave de entrada (normalmente provocada por actualizaciones de dependencia). El mismo caché envenenado afectará a cualquier otro. flujo de trabajo, corriday rama infantil usando la misma clave de caché.

En la prueba que realizamos, logramos inyectar la cadena 'Biblioteca MALA' ​​en la descripción de la imagen:

mala biblioteca

Esto fue en la versión 0.4.1. A continuación, actualizamos la etiqueta y reconstruimos la imagen varias veces, y observamos que "Biblioteca incorrecta" permanecía en la descripción.

Divulgación de GitHub

La respuesta de Github a nuestra divulgación fue que la acción de Git,  acción/caché se comporta como se esperaba y no tienen planes de ajustar el alcance del caché.
Aunque el equipo de GitHub no considera que este comportamiento sea problemático, recomendamos a los profesionales de DevSecOps que tengan cuidado con este vector de ataque.

Mitigaciones

  1. No utilice cachés en versiones ni en flujos de trabajo importantes.
  2. Ejecute sus flujos de trabajo de forma secuencial, ejecutándose primero el flujo de trabajo confiable para asegurarse de que su caché se cree en un flujo de trabajo confiable.
  3. Venda sus dependencias: vender en GoLang es un método para garantizar que todos los paquetes de terceros utilizados en el proyecto Go sean consistentes para todos los que desarrollan para esa aplicación. De esa forma, las dependencias almacenadas en caché seguirán siendo válidas para todas las ramas del proyecto. Es posible que otros idiomas no admitan este método.
  4. Firme el valor de la caché criptográficamente y verifique la firma antes de su uso.
    Scribe mitiga dichos ataques firmando de forma granular cualquier objeto o directorio en un flujo de trabajo, como un caché. Antes del lanzamiento, puede utilizar Scribe para validar que solo se utilizó un caché generado por un flujo de trabajo confiable para crear el lanzamiento.

Resumen 

En esta publicación, describimos un ataque de envenenamiento de caché en flujos de trabajo de CI, que está oculto a los ojos del equipo de DevSecOps, y discutimos las mitigaciones.

Este contenido es presentado por Scribe Security, un proveedor líder de soluciones de seguridad de la cadena de suministro de software de extremo a extremo, que ofrece seguridad de última generación para artefactos de código y procesos de desarrollo y entrega de código en todas las cadenas de suministro de software. Más información.