
AWS SAM CLI の脆弱性 (CVE-2025-3047, CVE-2025-3048)
この README では、AWS Serverless Application Model CLI (AWS SAM CLI) で見つかった 2 つのセキュリティ脆弱性(CVE-2025-3047 および CVE-2025-3048)について、コードレベルの問題点と修正を含む詳細な分析を提供します。どちらの脆弱性も、Docker コンテナを使用したビルドプロセス中のシンボリックリンク(シンボリックリンク)の不適切な処理に関するものです。以下の各セクションでは、各 CVE について、要約、影響を受けるコード(SAM CLI ソースへの参照付き)、パッチとその問題の解決方法、および修復のガイダンスを説明します。
これらの脆弱性は、sam build --use-container プロセス中のシンボリックリンクの不適切な処理に関するものです。どちらの問題もローカル開発環境に影響を及ぼします(デプロイされた AWS サービスやリソースには影響しません)が、AWS SAM CLI がシンボリックリンクを処理する方法を悪用することで、ホストマシン上のファイルへの不正アクセスを許可する可能性があります。AWS SAM CLI をパッチ適用済みバージョン(CVE-2025-3047 の場合は 1.133.0 以降、CVE-2025-3048 の場合は 1.134.0 以降)にアップグレードすることを強くお勧めします。
GHSA-px37-jpqx-97q9 は、AWS SAM CLI <= v1.132.0 におけるパストラバーサル脆弱性であり、sam build --use-container 中にホストマシン上のファイルへの不正アクセスを許可していました。Docker コンテナ内でサーバーレスアプリケーションをビルドする際、SAM CLI はデフォルトでプロジェクト内のシンボリックリンクを辿っていました。悪意のあるシンボリックリンク(機密性の高いホストファイルを指すもの)をプロジェクトに仕掛けることができた攻撃者は、Docker コンテナの昇格された権限を利用して、そのファイルをコンテナ内にマウントし、コンテナ内でアクセス可能な場所にコピーさせることができました。実際には、これにより特権的なホストファイル(プロジェクトディレクトリ外のもの)がビルドコンテナを介して読み取られ、外部に持ち出される可能性がありました。この問題は v1.133.0 で修正されました。(正当なケースの後方互換性を維持するために、SAM CLI v1.133.0 では、必要に応じて以前の動作を再び有効にするオプトインフラグ --mount-symlinks が導入されました。)
根本原因と影響を受けるコンポーネント: 核心の問題は、AWS SAM CLI がプロジェクトディレクトリとそのシンボリックリンクをビルドに使用する Docker コンテナにマウントする方法にあります。AWS SAM CLI のコード(samcli.local.docker.container モジュール)では、修正前は、プロジェクトディレクトリ内のすべてのトップレベルのシンボリックリンクが自動的に解決され、コンテナプロセスと同じ昇格された権限でコンテナにバインドマウントされていました。コンテナはデフォルトで root として実行されるため、機密性の高いホストパスを指すシンボリックリンクを解決してマウントすると、通常は特権のないホストユーザーがアクセスできないファイルにコンテナがアクセスできるようになりました。脆弱なコードは、どのシンボリックリンクを辿るか/マウントするかを十分に制限していませんでした。
具体的には、ビルド用の Docker ボリュームマウントを作成する関数で、SAM CLI はシンボリックリンクを無条件に実際のファイル/ディレクトリとして扱い、マウントしていました。この脆弱性は SAM CLI のコンテナオーケストレーションロジック、具体的には samcli/local/docker/container.py の Container.create メソッドに存在していました。脆弱性のあるバージョンでは、このメソッドは常にプロジェクトから Docker コンテナへのシンボリックリンクターゲットを解決してマウントしようとしていました。問題のあるコードを以下に示します(SAM CLI v1.132.0)。
# samcli/local/docker/container.py (v1.132.0 - 脆弱なスニペット)
if self._host_dir:
mount_mode = "rw,delegated" if self._mount_with_write else "ro,delegated"
LOG.info("Mounting %s as %s:%s, inside runtime container", self._host_dir, self._working_dir, mount_mode)
_volumes = {
self._host_dir: {
"bind": self._working_dir,
"mode": mount_mode,
},
**self._create_mapped_symlink_files(), # 常にシンボリックリンクを解決してマウントする(脆弱)
}
上記のコードでは、_create_mapped_symlink_files() がプロジェクトディレクトリ以下のシンボリックリンクをスキャンし、マウント用に準備します。これが無条件に含まれていたため、すべてのシンボリックリンク(プロジェクト外を指すものを含む)がコンテナにマウントされていました。(aws/aws-sam-cli#7865 参照)
ここでの問題は、sam build --use-container 中に、SAM CLI がシンボリックリンクをコンテナにマウントするファイルとして扱うことです。シンボリックリンクがホスト上の /etc/shadow などを指している場合、Docker コンテナ(昇格された権限で実行される可能性があります)はそのファイルをバインドマウントすることになります。これは古典的なシンボリックリンクベースのパストラバーサルであり、結果として権限昇格を引き起こします。つまり、ユーザーが通常アクセスできないホストファイルがコンテナによって読み取られ、ビルド出力に含まれる可能性があります。
パッチ(v1.133.0 で修正されたコード): 修正では、ビルドコンテキストの概念が導入され、コンテナビルド中のシンボリックリンク解決が無効化されました。パッチ適用バージョンでは、Container.create がコンテキスト(BUILD と INVOKE)を示す追加パラメータを受け取り、シンボリックリンクを解決するのはコンテキストが呼び出し(関数をローカルで実行する場合)の場合のみで、ビルド中は解決しません。以下は、パッチ適用バージョンの修正されたコードです。
# samcli/local/docker/container.py (v1.133.0+ - パッチ適用済みスニペット)
if self._host_dir:
mount_mode = "rw,delegated" if self._mount_with_write else "ro,delegated"
LOG.info("Mounting %s as %s:%s, inside runtime container", self._host_dir, self._working_dir, mount_mode)
mapped_symlinks = self._create_mapped_symlink_files() if self._resolve_symlinks(context) else {}
_volumes = {
self._host_dir: {
"bind": self._working_dir,
"mode": mount_mode,
},
**mapped_symlinks, # コンテキストで明示的に許可された場合のみシンボリックリンクをマウント(ビルド時は不可)
}
パッチ適用コードでは、_create_mapped_symlink_files() がコンテキストチェックの背後にラップされています。新しい ContainerContext 列挙型は BUILD や INVOKE などのコンテキストを定義し、_resolve_symlinks(context) はビルドコンテキストでは False を返します。したがって、ビルド中は mapped_symlinks は空の辞書になり、デフォルトではシンボリックリンクはコンテナにマウントされません。
コード変更の参照: この修正は プルリクエスト #7865("fix: Resolve symlinks on local invoke only")で実装され、v1.133.0 の一部としてリリースされました。GitHub の差分では、ContainerContext と条件付きマウントロジックの導入が示されています。ビルド中にシンボリックリンクターゲットをマウントしないことで、コンテナはプロジェクトディレクトリ外のファイルにアクセスできなくなります。(ユーザーがホストパスへのシンボリックリンクを許可したい場合は、この修正に続いて追加された --mount-symlinks フラグを利用してオプトインする必要があります。)
パッチが問題を解決する方法: パッチ適用後、プロジェクト内のシンボリックリンクはビルドフェーズ中に辿られなくなります。それらは単にコンテナ内のシンボリックリンクとして残るか(マウントされないパスを指す)、無視されるだけであり、ターゲットの内容に置き換えられることはありません。これにより、攻撃者がビルドプロセスを騙して機密ホストファイルをコピーさせる穴が塞がれます。要約すると、ビルドコンテナのビューはプロジェクトディレクトリ自体(および明示的に許可されたボリューム)に限定され、意図しない権限昇格が排除されます。
修復: すべてのユーザーは、この修正を入手するために AWS SAM CLI v1.133.0 以降にアップグレードする必要があります。アップグレード後、デフォルトの動作は安全です。プロジェクトを明示的に信頼し、以前の動作が必要な場合にのみ、sam build --use-container --mount-symlinks を使用してください。ほとんどの開発者にとっては、このフラグをオフ(デフォルト)のままにしておくことをお勧めします。これにより、シンボリックリンクがワークスペース外にトラバースできなくなります。また、プロジェクト内のシンボリックリンクを確認し、機密性の高い場所を指していないことを確認することも良い習慣です。
GHSA-pp64-wj43-xqcr は、AWS SAM CLI <= v1.133.0 に影響する関連脆弱性です(v1.134.0 で修正)。AWS SAM CLI のビルドアーティファクトキャッシュの脆弱性により、ビルド後に機密ファイルがコンテナからホストワークスペースに漏洩する可能性がありました。プロジェクトにシンボリックリンクが含まれている場合、sam build --use-container を実行した後、シンボリックリンクターゲットの内容が通常のファイルまたはフォルダとしてローカルのビルドキャッシュディレクトリにコピーされていました。つまり、特定のホストファイルにアクセスできない開発者でも、それらのファイルの内容がホスト上の .aws-sam ビルド出力に含まれるため、アクセス可能になる可能性がありました。例えば、プロジェクト内の /secret/config を指すシンボリックリンクがあると、ユーザーが直接 /secret/config を読み取れなくても、コンテナビルド後にその実際の内容がプロジェクトの .aws-sam/build フォルダに現れる可能性がありました。
根本原因と影響を受けるコンポーネント: この問題の核心は、SAM CLI がコンテナ(またはビルドプロセス)からローカルプロジェクトのビルドアーティファクトディレクトリにファイルをコピーする方法にありました。ファイルコピーを担当する関数は samcli/lib/utils/osutils.py、具体的にはカスタムの copytree ユーティリティにあります。脆弱性のあるバージョンでは、この関数は Python の shutil.copy2 を follow_symlinks=False を指定せずに使用しており、デフォルトではシンボリックリンクを辿り、ファイルの内容をコピーしていました。以下のスニペット(v1.133.0 より)は、問題のあるロジックを示しています。
# samcli/lib/utils/osutils.py (v1.133.0 - 脆弱なスニペット)
# ... osutils.copytree 内 ...
else:
try:
shutil.copy2(new_source, new_destination) # follow_symlinks はデフォルトで True(脆弱)
except OSError as e:
if e.errno != errno.EINVAL:
raise e
ここで、new_source がシンボリックリンクの場合、shutil.copy2 はシンボリックリンクを解決し、ターゲットファイルを new_destination にコピーします。シンボリックリンクを保持するように指示するフラグはありませんでした。そのため、機密ファイルへのシンボリックリンクは、そのファイルの内容が宛先ディレクトリに現れる結果になりました。(aws/aws-sam-cli#7890 参照)
実際のシナリオでは、ビルドプロセスが config -> /etc/secret-config のようなシンボリックリンクを作成したとします(おそらく依存関係のレイヤリングの一部として、または CVE-2025-3047 のシナリオの残骸として)。上記のコードは /etc/secret-config の内容を config としてローカルのビルド出力にコピーします。直接 /etc/secret-config を読み取ることができなかったローカルユーザーは、.aws-sam/build/.../config 下のファイルを開くだけでその内容を見ることができました。
パッチ(v1.134.0 で修正されたコード): 修正は簡単でした。コピー時にシンボリックリンクを辿るのではなく、保持するようにしました。Python の shutil では、follow_symlinks=False を渡すことで実現できます。パッチ適用コード(v1.134.0)では、コピー呼び出しが以下のように変更されています。
# samcli/lib/utils/osutils.py (v1.134.0 - パッチ適用済みスニペット)
else:
try:
shutil.copy2(new_source, new_destination, follow_symlinks=False) # シンボリックリンクを辿らない(修正)
except OSError as e:
if e.errno != errno.EINVAL:
raise e
follow_symlinks=False を指定すると、new_source がシンボリックリンクの場合、関数はそれが指すファイルではなく、シンボリックリンク自体をコピーします。つまり、ビルド出力にはターゲットの内容を持つ実ファイルではなく、同じターゲットを指すシンボリックリンクが含まれることになります。
コード変更の参照: この変更は プルリクエスト #7890("fix: Keep symlinks when copying files after build")で行われ、v1.134.0 でリリースされました。この PR の GitHub 差分では、shutil.copy2 に follow_symlinks=False パラメータが追加されたこと、およびシンボリックリンクが保持されることを確認するための更新された単体テストが確認できます。PR の説明には明示的に次のように書かれています。"Symlinks will stop being transformed into copies of the files and keep their symlink status." これは、ビルドアーティファクトにファイルデータの不正なコピーではなく、元のファイルパスを参照するシンボリックリンクが含まれることを意味します。
パッチが問題を解決する方法: この修正後、SAM CLI のビルド後ファイルコピーはファイルの内容を漏洩しなくなります。ビルド中にシンボリックリンクが作成された場合、出力でもシンボリックリンクのままになります。ローカルユーザーがターゲットの内容への読み取りアクセスを魔法のように得ることはなく、元のパスを指すシンボリックリンクが表示されるだけです。ユーザーがターゲットファイルの読み取り権限を既に持っていない限り、出力内のシンボリックリンクは無害です(適切なアクセスなしで逆参照しようとするとエラーになります)。基本的に、機密性への影響は軽減されます。機密ファイルが誤ってユーザーのワークスペースに具体化されることはありません。この修正は CVE-2025-3047 のパッチを補完するものです。CVE-2025-3047 のパッチはコンテナがシンボリックリンクを介してホストファイルを取得するのを防ぎ、CVE-2025-3048 の修正は、たとえ取得されたもの(または正当に存在するもの)がプレーンファイルとして出力に永続化されるのを防ぎます。
修復: ユーザーはこのパッチを入手するために AWS SAM CLI v1.134.0 以降にアップグレードする必要があります。v1.134.0 以降では、ビルドプロセスはデフォルトでシンボリックリンクを保持し、この脆弱性を修正します。アップグレード後は、SAM アプリケーションをクリーンして再ビルドし、キャッシュされたビルドアーティファクトが新しい安全な動作で再生成されることをお勧めします(AWS セキュリティ速報では、アップグレード後に新しい sam build --use-container を実行するよう推奨しています)。古いバージョンでは、手動で機密性の高いシンボリックリンクを削除するか、コンテナビルドを使用しない以外に回避策はないため、アップグレードが唯一の堅牢な解決策です。一般的に、ローカルの SAM CLI ビルドフォルダは機密性のある出力として扱います。パッチ適用後は予期しない秘密が含まれるべきではありませんが、ビルドアーティファクトに何が含まれるかに注意を払うことは良い習慣です。
CVE-2025-3047 と CVE-2025-3048 はどちらも、シンボリックリンクの処理の脆弱性に関連しており、ローカルビルド中に機密ファイルが露出する可能性があります。CVE-2025-3047 はファイルが Docker コンテナ内で読み取られ(そして潜在的にコピーされる)可能性があり、CVE-2025-3048 はそれらのファイルがローカル出力に含まれ、後でユーザーや攻撃者が読み取れる可能性があります。これらの脆弱性は中程度の深刻度と評価されました。悪意のあるプロジェクトでビルドを実行するというユーザーの操作が必要ですが、機密性への影響は大きい可能性があります。連携したパッチにより、デフォルトでは SAM CLI がコンテナ化ビルド中にシンボリックリンクを辿らず、ビルド出力にシンボリックリンクターゲットをコピーしないことが保証されます。
SAM CLI を使用する開発者および DevSecOps 担当者は、CLI が最新(v1.134.0 以降)であることを確認し、予期しないシンボリックリンクを含むプロジェクトには注意を払う必要があります。SAM CLI のフォーク版やカスタマイズ版を保守している場合は、これらの修正を組み込む必要があります。これらのコード変更を理解することで、条件文や関数パラメータの追加といった小さなロジックの調整が、深刻なセキュリティホールをどのように閉じることができるかを理解できます。ファイル処理、特にシンボリックリンクとコンテナの相互作用のセキュリティを常に考慮し、自身のプロジェクトで同様のパストラバーサルの問題を防いでください。
参考文献:
container.py および osutils.py の関連抜粋