セキュリティ
BasetenのプロダクションGitHubへの管理権限を25分で取得
We got admin access to Baseten's production GitHub in 25 minutes (strix.ai)
要約
セキュリティ企業Strixは、自身が利用を検討していたAIプラットフォームBasetenのセキュリティ評価を実施したところ、25分で同社のプロダクションGitHubリポジトリに対する管理権限を持つトークンを発見しました。このトークンは、公開されたDockerイメージのビルド履歴に含まれており、主要なソースコードリポジトリやインフラストラクチャを管理するリポジトリへの管理者権限を有していました。Baseten側は迅速に対応し、問題は修正されました。
全文翻訳
私たちは、Basetenに私たち自身と顧客のデータを預けることを検討していました。安全を期すため、まず彼らが安全であることを確認するためにStrixを実行しました。約25分後、Basetenの内部リポジトリに対するリポジトリレベルの管理者権限を持つライブGitHubトークンを取得しました。
私たちはStrix、自律型ハッキングエージェントを開発しています。もちろん、そのためには(安価で高速な)推論が必要です。私たちは選択肢を検討しており、Basetenは明白な選択肢の一つです。素晴らしい製品であり、130億ドルの評価額を持ち、多くの真剣な企業が彼らに依存しています。しかし…私たちはセキュリティ企業です。
サードパーティにデータ、モデル、またはコードを提供する前に、私たちはそれをスキャンします。そのサービスに依存し始める前に問題を発見し、修正を支援する方がはるかに良いと考えています(私たちはほとんどすべてのベンダーに対してこれを行っており、深刻な問題を発見する率が高いです)。
そこで…私たちは*.baseten.coにStrixを向け、認証情報やソースコードなしで実行させました。結果として、basetenbot用のアクティブなGitHubパーソナルアクセストークンが得られました。そのトークンは、Basetenのメイン製品リポジトリ、クラスターを駆動するGitOpsリポジトリ、およびHomebrew tapに対する管理者およびプッシュアクセス、さらに顧客ごとの特定のレポを含む他のプライベートリポジトリへの読み取り/書き込みアクセスを持っていました。イメージのビルドは2023年3月の日付でしたが、2026年7月に発見したとき、トークンはまだ有効でした。
しかし、詳細に入る前に、Basetenのセキュリティチームに称賛を送りたいと思います。彼らは問題をクリティカルと確認し、レジストリプロジェクトをロックダウンし、翌日午後にはトークンをローテーションしました。彼らはプロフェッショナルで、この状況(多くの場合そうではない)に非常に迅速に対処しました。
Strixがどのようにしてそれを見つけたか
Strixは、優れたペネトレーションテストと同様に、偵察から始まります。多くの場合、インフラストラクチャ上の最も深刻な脆弱性は、あなたが忘れているサブドメイン上のサービスにある可能性があります(だからこそ、コードアクセスを伴うテストと並行してブラックボックステストを常に推奨するのです!)。
そのため、Strixはホストを列挙し、証明書ログを調べ、完全なサーフェスをマッピングしました。最終的に、gcp-us-east4-zlw.registry.baseten.coにあるHarborレジストリを発見しました。Harborはコンテナイメージを保存し、リポジトリをプロジェクトにグループ化します。これらのプロジェクトの一つが公開されていました。
トークンや認証なしで、Strixはリポジトリをリストし、匿名プルトークンを取得し、実際のイメージマニフェストとブロブをダウンロードすることができました。これにはbaseten/baseten-appというイメージが含まれていました。
この時点で、公開されたレジストリを報告して次に進むのは簡単です。しかし、企業は意図的にイメージを公開しており、Strixは偽陽性を報告したくありません。重要なのは、イメージが公開されていることがどのような影響を与えるかということです。
Strixはイメージをプルして、中身を確認することにしました。以下は、Strixに表示されるその部分の実行ログです(はい、エージェント自身の記述はAIのように聞こえます。明白な理由があります):
Harbor Exposure Impact Review
Thinking
If those Harbor projects are not meant to be public, this is a real exposure, not metadata noise. The chain is what matters: enumerate public projects, read artifact metadata, mint an anonymous service/token scoped to repository:baseten/baseten-app:pull, pull the manifest, then fetch the blob through a signed storage redirect. That proves real anonymous pull access. The way to make triage take it seriously is to prove one thing from a pulled image: proprietary code, internal binaries, hardcoded credentials, or internal hostnames.
最初の手がかりは、baseten/baseten-appにあった一組のAWSキーでした。Strixは読み取り専用のsts:GetCallerIdentity呼び出しを試みましたが、これは認証情報がどのカウントに属するかを教えてくれます。応答はInvalidClientTokenIdでした。そのキーは無効だったので、Strixは探し続けました。
そして、実際に機能するトークンが見つかりました。
レイヤーをプルし、TruffleHog(オープンソースセキュリティの友人たちに感謝!)を実行し、イメージ設定を直接検査しました。そして、そこにありました:クラシックなGitHubパーソナルアクセストークンが、history[].created_byにありました。
私はDockerランタイムの専門家ではありませんが、幸いStrixは(ほぼすべての人間の知識を利用できるため)専門家です。そのため、そのフィールドがビルドステップがどのように作成されたかを記録していることを知っていました。この場合、それはGITHUB_TOKENの値が直接展開されたRUNコマンドを含んでいました。
Strixは、そのトークンを使用してGitHubに対して読み取り専用のGET /userリクエストを実行しました…そして、VOILÀ。200 OK、アカウント名はbasetenbotでした。
Dockerビルド履歴内のトークン、そしてGitHubがそれをbasetenbotとして識別したこと。認証情報は編集されています。完全なサイズで画像を開いてください。トークンが見つかった場所をよく見てください。
私が学んだように、Dockerイメージはファイルシステムレイヤーを持っていますが、イメージとそのビルド履歴に関する情報を含む設定も持っています。その設定は、イメージと一緒にダウンロードできます。ビルド履歴にトークンの別のコピーが残っている場合、認証情報ファイルをクリーンアップしても意味がありません。そして、このトークンは3年以上経過しても有効でした。
さて、basetenbotは何ができるのでしょうか?ライブトークンは興味深いですが、もちろん権限が重要です。このトークンは権限がゼロで、したがって影響もゼロである可能性があります。そこでStrixはアカウントとその組織メンバーシップを確認しました。
GitHubはX-OAuth-Scopes: repoを返し、アカウントはbasetenlabsに属していました。GitHubはbasetenbotにrepoスコープを返し、basetenlabsをその組織としてリストしました。
完全なサイズで画像を開いてください。
次に、読み取り専用リクエストを使用して、個々のリポジトリの権限を確認しました。
RepositoryAccess
basetenlabs/basetenadmin: true, push: true
basetenlabs/flux-cdadmin: true, push: true
basetenlabs/homebrew-tapadmin: true, push: true
basetenlabs/release-platformPrivate, read/write
basetenlabs/basevibePrivate, read/write
basetenlabs/trainersPrivate, read/write
basetenlabs/baseten-dbtPrivate, read/write
公開ダウンロード可能なイメージにこれほど多くのアクセス権を残しておくのは異常です。
basetenlabs/basetenは製品です。このトークンを持つ者は、推論プラットフォームのメインソースコードリポジトリに対する管理者およびプッシュ権限を持っていました。彼らは、他の企業がモデルを実行するために依存しているコードを改ざんできた可能性があります。私たちは自身のコードやモデルをこの会社に送ることを検討しており、まさにそのためにこれらのチェックを行っているのです。
basetenlabs/flux-cdは、おそらくさらに恐ろしいものです。FluxはGitOpsです。リポジトリにはクラスターの望ましい状態が含まれており、Fluxはその状態をインフラストラクチャに適用します。ここでの管理者アクセスは、漏洩したビルドトークンから本番インフラストラクチャへの変更へのルートを作成します。
basetenlabs/homebrew-tapは、彼らのCLIが開発者のマシンにインストールされる方法です。配布チャネルを改ざんすると、Basetenのツールをインストールする人々に対するサプライチェーン攻撃になる可能性があります。
そして、basetenlabs/fdeがありました。そのプライベートリポジトリのリストには、トップレベルのcustomers/ディレクトリが表示され、その下にBasetenの顧客の名前が付いたサブディレクトリが次々と続きました。
その時点で、報告するのに十分な情報があり、これが偽陽性ではないと確信できました。私たちは顧客リポジトリをクローンしたり、何かをプッシュしたり、設定を変更したりしませんでした。そこで停止し、すぐに開示メールを作成しました。
トークンがそこにどのように配置されたのか?
ビルド履歴にはタイムスタンプが付いていました。トークンを含むステップは2023年3月3日に実行されました。これは古いビルド認証情報であり、2026年7月にテストしたときにも、そのすべてのアクセス権を保持していました。
根本的な間違いは非常に一般的です。ビルドがGitHubからプライベート依存関係を取得する必要があったため、誰かがビルド引数としてトークンを渡しました。関連するパターンは次のようでした。
1ARG GITHUB_TOKEN
2RUN GITHUB_TOKEN=${GITHUB_TOKEN} bash -c '\3 if [[ "${GITHUB_TOKEN}" != "" ]]; then \4 git config --global --add \5 url."https://${GITHUB_TOKEN}@github.com/".insteadOf "git@github.com:"; \6 fi'
誰かがこれを書くことになるのは理解できます。プライベート依存関係が必要で、トークンを渡すと、Gitが認証され、ビルドが機能します。しかし、Dockerはそのビルド引数をイメージのメタデータと履歴に記録できます。この場合、実際のトークン値が記録されました。
Dockerはこのことについて明確に警告しています。このパターンには第二の問題もあります。git config --globalは、認証されたURLをGitの設定ファイルに書き込みます。トークンがどのようにビルドに渡されるかを変えたとしても…