Consul Template の writeToFile ヘルパーは、オペレーターが指定した宛先を直接開き、リンクされたパスコンポーネントをたどるため、レンダリングされた出力が意図したディレクトリの外に逃れ、既存のファイルを上書きする可能性がありました。
この問題は、HashiCorp Consul Template をレビューしているときに、直接的なファイルシステムのセキュリティ問題を念頭に置いて見つけました。
writeToFile が意図したディレクトリ内にあるように見えるパスを受け取った場合、ファイルシステムが実際にデータを書き込む場所を検証するでしょうか?
この場合、答えはノーでした。
writeToFile テンプレートヘルパーは、ユーザーが指定した最終パスを os.Create() または os.OpenFile() で直接開きました。
これらの操作は、宛先パスに既に存在するシンボリックリンク、ディレクトリジャンクション、および同等のファイルシステムリダイレクションをたどりました。
つまり、パス文字列はオペレーターが意図したルートの下に留まりながら、実際の書き込みは別の場所に到達する可能性がありました。
私が管理した概念実証では、リンクされた親ディレクトリがレンダリングされた出力を意図したツリーの外にリダイレクトし、既存のターゲットファイルを上書きさせました。
この問題は CVE-2026-14361 になりました。
HashiCorp セキュリティ情報: HCSEC-2026-20
IBM セキュリティ情報: CVE-2026-14361 セキュリティ情報
CVE: CVE-2026-14361
修正バージョン: 0.42.1
photo0
operator-supplied destination appears inside intended root -> attacker pre-positions linked parent or final path component -> writeToFile opens the path directly -> filesystem resolves the write outside the intended directory -> rendered secret is redirected -> preexisting target may be overwritten
Consul Template は、Consul や Vault などのソースからデータをレンダリングします。
writeToFile ヘルパーを使用すると、テンプレートは選択したコンテンツを、要求された所有者、グループ、パーミッションモードを適用しながら、別のローカルファイルに書き込むことができます。
HashiCorp のドキュメントでは、PKI マテリアルを使ったヘルパーを具体的に示しています:
private key -> writeToFile /my/path/to/cert.key
certificate authority -> writeToFile /my/path/to/cert.pem
certificate -> append to /my/path/to/cert.pem
これにより、これは単なる通常の出力ヘルパーではありません。
この境界を越えるコンテンツには、次のものが含まれる可能性があります:
重要な疑問は、writeToFile が要求されたファイル名を作成できるかどうかではありませんでした。
本当の疑問は次のとおりです:
プロセスは、オペレーターが意図したファイルシステムの場所に書き込むのでしょうか、それとも単にパスが開かれた時点で解決されるオブジェクトに書き込むのでしょうか?
脆弱なバージョンでは、後者を信頼していました。
書き込みヘルパーは、アプリケーションデータからファイルシステムの変更にまたがるため、セキュリティ上価値の高い対象領域です。
興味深い障害は、多くの場合、古典的な ../ トラバーサルではありません。
それらは解決の失敗です:
これは、プロセスが攻撃者よりも多くのファイルシステム権限で実行される場合に特に重要です。
低権限のローカル攻撃者は、機密ファイルを直接上書きできない可能性があります。
しかし、意図した書き込みディレクトリ配下のパスコンポーネントに影響を与えることができれば、より特権のある Consul Template プロセスが代わりに書き込みを実行する可能性があります。
それが私が焦点を当てた境界です。
私はこれを一般的なパストラバーサルのレビューとして扱いませんでした。
指定されたパスは .. セグメントを必要としませんでした。
パスは、その間ずっと字句的に意図したルート内に留まることができました。
より強い疑問は次のとおりです:
機密コンテンツが書き込まれる前に、リンクされた宛先コンポーネントは拒否されますか?
この疑問は次の両方にとって重要です:
2 番目のケースは、ログと設定に期待されるディレクトリ配下の無害に見えるパスが依然として表示されるため、特に有用です。
ファイルシステムはそれを別の場所に解決します。
根本原因は、リンクを考慮した検証を行わずに、パスベースで直接ファイルを作成したことでした。
テストしたリビジョンでは、writeToFile() は 2 つのオープンパスのいずれかを選択しました。
追記モードでは次のものを使用:
f, err = os.OpenFile(
path,
os.O_APPEND|os.O_WRONLY|os.O_CREATE,
perm,
)
通常の書き込みモードでは次のものを使用:
dirPath := filepath.Dir(path)
if _, err := os.Stat(dirPath); err != nil {
err := os.MkdirAll(dirPath, os.ModePerm)
if err != nil {
return "", err
}
}
f, err = os.Create(path)
どちらのパスも、ファイルが開かれる前にリンクされた宛先コンポーネントを拒否しませんでした。
これは次の理由で重要です:
os.Create(path) は既存のファイルシステムリダイレクションをたどり、解決されたファイルを切り詰めますos.OpenFile(path, ...) は追記モード中にリンクされたパスコンポーネントをたどります書き込み後も、同じパスベースの前提が続きました。
所有者と権限は、再びパスを使用して適用されました:
err = os.Chown(path, uid, gid)
err = os.Chmod(path, perm)
つまり、メタデータ操作も、すでに開いているファイル記述子ではなく、変更可能なパス名に結び付けられていました。
攻撃者は writeToFile が実行される前にリダイレクションを準備できるためです。
基本的な攻撃には、確率的な競合条件は必要ありません。
その順序は単純です:
writeToFile がそれを直接開くこれが脆弱性の全体です。
オペレーティングシステムがパスベースのファイルオープン中に通常シンボリックリンクをたどることは事実です。
しかし、それがこのアプリケーションの動作を安全にするわけではありません。
セキュリティ上の疑問は次のものではありません:
「Go はドキュメントどおりに動作しましたか?」
本当の疑問は次のとおりです:
機密性の高いレンダリングデータを書き込むヘルパーは、解決された宛先がオペレーターの意図した場所と一致することを検証しましたか?
脆弱なバージョンでは、検証されていませんでした。
この区別が重要なのは、Consul Template が次のように実行される可能性があるためです:
そのような状況で攻撃者が仕掛けたファイルシステムリダイレクションをたどると、現実の権限と信頼境界の問題が発生します。
テストしたコミットにおける正確な writeToFile の動作を中心に、スタンドアロンの再現プログラムを作成しました。
管理されたセットアップでは、以下を使用しました:
writeToFile に渡される管理されたシークレットコンテンツ再現の流れは次のとおりです:
観察された動作は次のとおりです:
writeToFile がリダイレクションをたどったこれにより、主張の両方の部分が実証されました:
最終コンポーネントのシンボリックリンクだけでも、安全でないリンク追従を実証できます。
しかし、リンクされた親コンポーネントは、より強力な運用上のポイントを証明します:
既存のターゲットも重要でした。
それがなければ、PoC は予期しないファイル作成のみを示すことになります。
既存のファイルから開始し、その最終ハッシュを検証することで、再現プログラムは上書き動作を直接証明しました。
これにより、ソースのみの主張よりも結果が具体的になりました。
この問題には、ローカルファイルシステムへの影響力が必要です。
攻撃者は、意図した書き込み場所の中または配下に、シンボリックリンク、ディレクトリジャンクション、または同等のリダイレクションを作成または変更するための十分なアクセス権を必要とします。
その後、Consul Template プロセスがそのパスを通じて書き込む必要があります。
影響は、プロセスの権限とレンダリングされるコンテンツに大きく依存します。
実際に起こり得る結果は次のとおりです:
これは、デフォルトインストールからのリモートの未認証の任意書き込みではありませんでした。
しかし、特権環境や共有ファイルシステム環境において重大な影響を伴う、明確なローカル信頼境界の失敗でした。
HashiCorp はこの問題を次のように分類しました:
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N
公開されている基本スコアは次のとおりです:
4.7 / Medium
このベクターは以下を反映しています:
公式のセキュリティ情報も、リダイレクトされた書き込みが既存のファイルを上書きする可能性があることを明示的に文書化しています。
スコアが中程度なのは、ファイルシステムへの影響が理論上のものだからではなく、悪用がローカルのパス制御条件に依存するためです。
HashiCorp のセキュリティ情報には次のように記載されています:
Affected: consul-template up to and including 0.42.0
Fixed: consul-template 0.42.1
修正は 2026年7月8日 の 0.42.1 リリースで提供されました。
0.42.1 パッチは、書き込みパスを複数の層で強化しました。
パッチ適用後のヘルパーは、os.Lstat() を使用して以下を検査します:
そして、それらのコンポーネントがリンクである場合に拒否します。
これにより、レポートと回帰テストでカバーされる、直接の親リダイレクションと最終ファイルシンボリックリンクのケースが塞がれます。
サポートされている Unix プラットフォームでは、宛先は O_NOFOLLOW で開かれます。
これにより、事前チェックとオープンの間に最終コンポーネントがシンボリックリンクになった場合、オープン自体が失敗します。
これは、事前チェックだけでは別の TOCTOU ウィンドウが生じる可能性があるため重要です。
プラットフォーム固有の実装は Windows では no-op であり、O_NOFOLLOW は同じメカニズムでは利用できません。
パッチは、パスベースの所有者とモードの操作を、記述子ベースの操作に置き換えました:
f.Chown(uid, gid)
f.Chmod(perm)
これにより、メタデータの変更が、後でパスを再解決する代わりに、実際に開かれたファイルに結び付けられます。
ヘルパーは、stat の失敗が本当に os.IsNotExist である場合にのみ親ディレクトリを作成するようになりました。
権限エラーなどの他のエラーは、書き込みパスに進まずに返されます。
パッチは、以下に焦点を当てたテストを追加しました:
公開されたパッチの議論では、重要な制限が明確に文書化されています。
新しい検証は以下をチェックします:
それより上位のすべての祖先コンポーネントを走査して拒否するわけではありません。
メンテナーがその境界を選択したのは、writeToFile には完全な封じ込めチェックを固定する設定済みサンドボックスルートがなく、一般的なオペレーティングシステムでは macOS の /var -> /private/var のように、パスプレフィックスに正当な管理リンクが含まれる可能性があるためです。
O_NOFOLLOW も、すべての祖先ディレクトリではなく、最終コンポーネントを保護します。
これは、報告された脆弱性のステータスや公式の修正バージョンを変更するものではありません。
ただし、パッチによって提供される正確なセキュリティ特性は明確になります:
その区別は、技術的な文書では維持する価値があります。
私はこの問題を、2 番目の独立した Consul Template の発見として、2026年3月21日 に HashiCorp Security に非公開で報告しました。
レポートには以下が含まれていました:
最初のフォローアップメールは、セキュリティチームのレポートキューに存在せず、おそらくメーリングリストのスパムフィルタに捕捉されたものと思われます。
私が完全なレポートを再転送した後、HashiCorp はエンジニアリングチームと連絡を取り、最初の Consul Template の問題とは別に調査しました。
HashiCorp は 0.42.1 で脆弱性を修正し、2026年7月8日 に HCSEC-2026-20 を公開しました。
IBM は同じ CVE に対応するセキュリティ情報を公開しました。
両方の公式セキュリティ情報は、報告者として次のように謝辞を記載しました:
Mohamed Abdelaal (0xmrma)
重要な教訓は単純です:
パス文字列は、それが指し示すファイルシステムオブジェクトと同じではありません
この区別は、特権コードが攻撃者の影響を受けたパスに書き込むときは常に重要です。
文字列が意図したディレクトリで始まることを確認するだけでは不十分です。
トラバーサルシーケンスのないクリーンなパスでも、次のものを介して別の場所に解決される可能性があります:
機密性の高い操作は、適切な境界でその ID が検証された宛先に結び付ける必要があります。
この問題は、より広いルールも強化します:
すでにファイル記述子を開いている場合は、パスを再解決する代わりに、その記述子を通じてセキュリティに敏感な操作を適用してください
まさにそれが、記述子ベースの Chown と Chmod の変更が重要である理由です。
writeToFile は、証明書や秘密鍵などの機密性の高いマテリアル向けにドキュメント化されているos.Create または os.OpenFile で宛先を直接開いたChown と Chmod は、変更可能なパスに対する追加の信頼を導入した0.42.1 は、リンクチェック、サポートされている環境での O_NOFOLLOW、記述子ベースのメタデータ操作、および回帰テストを追加したこの脆弱性は ../ トラバーサルに関するものではありませんでした。
パス文字列は正しく見えました。
ファイルシステムの宛先は正しくありませんでした。
Consul Template はオペレーターの意図したパスを受け入れ、攻撃者が仕掛けたリダイレクションをたどり、レンダリングされたコンテンツを別のファイルに書き込みました。
それが、これが CVE-2026-14361 になった理由です。
consul-template 0.42.1 で修正されました。