GH Actions - Cache Poisoning
キャッシュポイズニング
CIのどこかでaction/cache Gitアクションを使用すると、2つのステップが実行されます。1つは実行プロセス中に呼び出されたとき、もう1つはワークフロー後に行われます(実行アクションがキャッシュミスを返した場合)。
実行アクション – キャッシュを検索して取得するために使用されます。検索はキャッシュキーを使用して行われ、結果はキャッシュヒット(成功、キャッシュ内のデータが見つかった)またはキャッシュミスのいずれかです。見つかった場合、ファイルとディレクトリはアクティブな使用のためにキャッシュから取得されます。キャッシュミスの場合、望ましいファイルとディレクトリは、初めて呼び出されたかのようにダウンロードされます。
ワークフロー後のアクション – キャッシュを保存するために使用されます。実行アクションでのキャッシュ呼び出しの結果がキャッシュミスであった場合、このアクションはキャッシュしたいディレクトリの現在の状態を提供されたキーで保存します。このアクションは自動的に行われ、明示的に呼び出す必要はありません。
アクセス制限は、異なるブランチ間の論理的な境界を作成することでキャッシュの分離とセキュリティを提供します (例: ブランチFeature-A [ベースはmain]のために作成されたキャッシュは、ブランチFeature-B [ベースはmain]のプルリクエストからはアクセスできません)。
キャッシュアクションは最初にキーのキャッシュヒットを検索し、ワークフロー実行を含むブランチのキーを復元します。現在のブランチでヒットがない場合、キャッシュアクションは親ブランチと上流ブランチでキーを検索し、キーを復元します。
キャッシュへのアクセスはブランチ(現在のブランチと親ブランチ)によってスコープされており、該当ブランチのワークフロー全体にわたる実行へのアクセスが提供されます。
もう一つ重要な点は、GitHubはエントリがプッシュされた後の変更を許可しないことです – キャッシュエントリは読み取り専用のレコードです。
私たちは、低権限のワークフローから高権限のものへ攻撃がピボットする方法を示す2つのワークフローを含むCIの例を使用しました。
ユニットテスト ワークフローはユニットテストとコードカバレッジツールを実行します。ツールの1つが悪意のあるものであるか、リモートコード実行に脆弱であると仮定します。ワークフローはaction/cache Gitアクションを使用する必要はありません。任意のワークフローがキャッシュにアクセスできます。
リリース ワークフローはアプリケーションアーティファクトをビルドしてリリースします。このワークフローは、Golangの依存関係を使用する際の最適化のためにキャッシュを使用します。
ユニットテスト ワークフローは、悪意のあるアクションを使用して、Golangのロギングライブラリ(go.uber.org/zap@v1)を変更し、「BAD library」という文字列をアプリケーションアーティファクトの説明に追加するキャッシュエントリを追加します。
次に、リリース ワークフローはこの毒されたキャッシュエントリを使用します。その結果、悪意のあるコードがビルドされたGolangバイナリとイメージに注入されます。キャッシュエントリのキーが破棄されるまで(通常は依存関係の更新によってトリガーされます)、キャッシュは毒されたままです。同じキャッシュキーを使用する他のワークフロー、実行、子ブランチにも同じ毒されたキャッシュが影響します。
私たちが実施したテストでは、イメージの説明に「BAD library」という文字列を注入することに成功しました:
これはバージョン0.4.1でした。次に、タグを更新してイメージを何度か再ビルドし、「Bad library」が説明に残っていることを観察しました。
この技術はhttps://scribesecurity.com/blog/github-cache-poisoning/から取られました。
Last updated