GH Actions - Cache Poisoning
Cache Poisoning
Utiliser l'action Git action/cache dans le CI exécutera deux étapes : une étape aura lieu pendant le processus run lorsqu'elle est appelée et l'autre après le workflow (si l'action run a retourné un cache-miss).
Action run – est utilisée pour rechercher et récupérer le cache. La recherche est effectuée en utilisant la clé de cache, avec comme résultat soit un cache-hit (succès, données trouvées dans le cache) soit un cache-miss. Si trouvé, les fichiers et répertoires sont récupérés du cache pour une utilisation active. Si le résultat est un cache-miss, les fichiers et répertoires désirés sont téléchargés comme s'il s'agissait de leur premier appel.
Action post workflow – utilisée pour sauvegarder le cache. Si le résultat de l'appel de cache dans l'action run retourne un cache-miss, cette action sauvegardera l'état actuel des répertoires que nous voulons mettre en cache avec la clé fournie. Cette action se produit automatiquement et n'a pas besoin d'être explicitement appelée.
Les restrictions d'accès fournissent une isolation du cache et une sécurité en créant une frontière logique entre différentes branches (par exemple : un cache créé pour la branche Feature-A [avec la base principale] ne serait pas accessible à une pull request pour la branche Feature-B [avec la base principale]).
L'action de cache recherche d'abord les hits de cache pour une clé et restaure les clés dans la branche contenant le workflow run. S'il n'y a pas de hits dans la branche actuelle, l'action de cache recherche la clé et restaure les clés dans la branche parente et les branches en amont.
L'accès à un cache est limité par branche (actuelle et parente), ce qui signifie que l'accès est fourni à tous les workflows à travers les runs de ladite branche.
Un autre point important est que GitHub ne permet pas de modifications une fois que les entrées sont poussées – les entrées de cache sont des enregistrements en lecture seule.
Nous avons utilisé un exemple de CI qui comprenait deux workflows. Cet exemple montre comment une attaque peut pivoter d'un workflow à faibles permissions à un workflow à permissions élevées.
Workflow unit-test exécutant des tests unitaires et des outils de couverture de code. Nous supposons que l'un des outils est malveillant ou vulnérable à l'exécution de code à distance. Le workflow n'a pas besoin d'utiliser l'action Git action/cache. N'importe quel workflow peut accéder au cache.
Workflow release construit et publie l'artefact de l'application. Ce workflow utilise un cache pour optimiser l'utilisation des dépendances Golang.
Le workflow unit-test utilise une action malveillante qui ajoute une entrée de cache avec un contenu malveillant en changeant une bibliothèque de journalisation Golang (go.uber.org/zap@v1) pour ajouter la chaîne, 'BAD library' à la description de l'artefact de l'application.
Ensuite, le workflow release utilise cette entrée de cache empoisonnée. En conséquence, le code malveillant est injecté dans le binaire Golang construit et l'image. Le cache reste empoisonné jusqu'à ce que la clé d'entrée soit écartée (généralement déclenchée par des mises à jour de dépendances). Le même cache empoisonné affectera tout autre workflow, run, et branche enfant utilisant la même clé de cache.
Dans le test que nous avons effectué, nous avons réussi à injecter la chaîne 'BAD library' dans la description de l'image :
C'était dans la version 0.4.1. Ensuite, nous avons mis à jour le tag et reconstruit l'image plusieurs fois, et observé que 'Bad library' restait dans la description.
Cette technique a été prise de https://scribesecurity.com/blog/github-cache-poisoning/
Last updated