Program)を設けており、脆弱性の診断行為を許可しています。本記事は、当該制度を通して報告された脆弱性をCloudflareセキュリティチームの許可を得た上で公開しているものであり、無許可の脆弱性診断行為を推奨することを意図したものではありません。Cloudflareが提供する製品に脆弱性を発見した場合は、Cloudflareの脆弱性開示制度へ報告してください。要約cdnjsのライブラリ更新用サーバーに任意のコードを実行することが可能な脆弱性が存在し、結果としてcdnjsを完全に侵害することが出来る状態だった。これにより、インターネット上のウェブサイトの内12.7%1を改竄することが可能となっていた。cdnjsとはcdnjsは、Cloudflareによって運営されている無料のJavaScript/CSSライブラリ用CDNであり、記事公開時点でインターネット上のウェブサイトの12.7%に使用されている。Librariesの12.8%2に次いで2番目に広く使われているライブラリ用CDNであり、現在の使用率の推移を鑑みるに、近いうちに最も使われているJavaScriptライブラリ用CDNになると考えられる。前回の「Homebrewにおける任意コード実行」に関する調査を行う数週間前に、サプライチェーン攻撃に関する調査を行っていた。多数のアプリケーションが依存するシステムであり、脆弱性調査を許可しているという条件で絞り込んだ際に、cdnjsが対象に入ったため調査を行うことにした。初期調査cdnjsのウェブサイトを眺めている際に、以下のような記述を発見した。Organizationのリポジトリを確認した。結果として、以下のような構成になっていることがわかった。Organizationに集約されている。その中で、cdnjs/bot-ansibleとcdnjs/toolsに興味を惹かれた。この2つのリポジトリのコードを読んだ所、cdnjs/bot-ansibleが定期的にcdnjs/toolsのautoupdateコマンドをライブラリ更新用サーバー内で実行し、cdnjs/packagesにおいて指定されているGitリポジトリ/npmパッケージを用いて更新を確認していることがわかった。自動更新機能の調査この自動更新機能は、ユーザーが管理するGitリポジトリ/npmパッケージをダウンロードし、対象のファイルをコピーすることによってライブラリを更新していた。npmレジストリは、各ライブラリを.tgzファイルとして圧縮した上でダウンロードができるようにしている。この自動更新用のツールがGoで記述されていることから、Goのcompress/gzip及びarchive/tarを用いて解凍しているのではないかと推測した。Goのarchive/tarはアーカイブ内に含まれるファイルパスをサニタイズせずに返す3ため、もし仮にarchive/tarから返されたファイル名を元にしてディスク上に書き込んでいる場合、../../../../../../../../../tmp/testのようなファイル名を持つファイルを.tgzに含めることにより、任意のファイルを上書きできる。4cdnjs/bot-ansibleの情報から、いくつかのスクリプトが定期的に実行されており、それらに対して書き込み権限があることがわかっていたため、パストラバーサルを介したファイルの上書きを重点的に確認することにした。パストラバーサルパストラバーサルを探すために、autoupdateコマンドのmain関数を読み始めた。pckg)}[...]}上記のコードから分かるように、npmをベースとした自動更新が指定されていた場合は、updateNpm関数へとパッケージ情報を渡している。[...]}そして、新しいライブラリバージョンの情報と共にdoUpdateNpm関数を呼び出す。[...]}次に、npm.DownloadTar関数へ新しいバージョンの.tgzファイルのURLを渡す。dest}最後に、http.Getを使用して取得した.tgzファイルを、Untar関数へと渡す。err}}}}}予想通り、Untar関数内においてcompress/gzipとarchive/tarを使用して解凍を行っていた。最初はremovePackageDirにおいてパスのサニタイズを行っているのだと考えていたのだが、関数の内容を確認した所、単純にpackage/という文字列をパスから削除するだけの関数だった。これにより、npmへ公開した.tgzファイルからパストラバーサルを行い、サーバー上で定期的に実行されるスクリプトを上書きした上で任意のコードが実行できるということがわかった。脆弱性のデモンストレーションCloudflareはHackerOne上に脆弱性開示制度を持っているため、脆弱性が実際に攻撃可能であることを示さなければ、HackerOneのトリアージチームがCloudflare側にレポートを転送しない可能性が高い。そのため、脆弱性が実際に攻撃出来ることを示すためにデモンストレーションを行うことにした。攻撃手順としては、以下のとおりとなる。細工したファイル名を含む.tgzファイルをnpm上で公開する。cdnjsの自動更新サーバーがファイルを処理するのを待つ。細工した.tgzファイルの中身が定期実行されているスクリプトファイルへと書き込まれ、任意のコードが実行される。...と、ここまで考えた所でGitリポジトリをベースとした自動更新機能が気になってきた。そのため、脆弱性のデモンストレーションを行う前にコードを流し読みした所、以下のコードのように、Gitリポジトリからファイルをコピーする際に、シンボリックリンクの存在が考慮されていないように見受けられた。nil}Gitはシンボリックリンクを扱うことが可能なため、Gitリポジトリにシンボリックリンクを含め、cdnjsに処理させることによりcdnjsシステム上の任意のファイルを読み取れる可能性がある。適切に動作しなくなってしまう可能性があったため、シンボリックリンクによる任意ファイル読み取りを先に検証/報告し、パストラバーサルに関してはそのレポート内でCloudflareのセキュリティチームに確認後、デモンストレーションを行おうと考えた。これに伴い、攻撃手順を以下のように変更した。cdnjsに登録されたGitリポジトリに、無害なファイル(ここでは/proc/self/mapsを想定)へとリンクさせたシンボリックリンクを追加する。当該のGitリポジトリ上で、新しいバージョンを公開する。cdnjsの自動更新サーバーがファイルを処理するのを待つ。指定したファイルが読み取られ、公開される。この時点で20時頃だったのだが、シンボリックリンクを作成するだけであれば直ぐに済ませられると考え、シンボリックリンクを作成/プッシュしてから夕飯を食べることにした。test.jsインシデント夕飯を済ませ、PCの前に戻ってきた所、cdnjsがシンボリックリンクを含むバージョンを公開していることが確認できた。その後、レポートを送信するためにファイル内容を確認して驚愕した。なんと、GITHUB_REPO_API_KEYや、WORKERS_KV_API_TOKENといった明らかに機微な情報が表示されていたのである。一瞬何が起こったのか理解できず、コマンドのログを確かめた所、誤って/proc/self/mapsではなく/proc/self/environへのリンクを貼っていたことが確認できた。Organizationが侵害された場合、cdnjsの大部分を侵害することが可能となる。対応する必要があったため、レポートには現在の状況がわかるリンクとトークン類を全て取り消して欲しいという旨のみを記載し、送信した。この時点ではとても焦っており確認していなかったが、実はレポートを送信する前にこれらのトークンは無効化されていた。これは後からわかったことだが、GITHUB_REPO_API_KEY(GitHubのAPIキー)が含まれていたことにより、即座にGitHubが自動で通知を行い、それを受け取ったCloudflareはインシデントレスポンスを開始していたらしい。cdnjsが細工されたパッケージを処理してから数分も立たない内に各種認証情報の無効化を行っていたらしく、流石Cloudflareだな、という印象を受けた。影響調査その後、詳細な影響範囲の調査を行った。Organizationに所属しているrobocdnjsというアカウントのアクセストークンであり、cdnjsの各リポジトリに対する書き込み権限を持っていた。cdnjs上でホストされている任意のライブラリの改竄や、ウェブサイトの改竄などをこのトークンを用いて行うことができた。WorkersのKVに対する書き込み権限を持っており、KVにキャッシュされたライブラリ情報を改竄することが可能だった。これらの権限を組み合わせることにより、cdnjsのオリジンデータからKVキャッシュ、更にはcdnjsのウェブサイトといったcdnjsのコア部分を完全に侵害することができたと考えられる。まとめ今回の記事では、cdnjsに存在した脆弱性について解説しました。脆弱性自体は特殊なスキル無しで悪用可能なものでしたが、それによって潜在的に生じる影響が非常に大きいものでした。世の中にはこの脆弱性のような、悪用が簡単なのにも関わらず、影響が非常に大きい脆弱性がサプライチェーンの中にあると考えると、非常に恐ろしいものだと感じます。本記事に関する質問/感想はTwitter(@ryotkak)へメッセージを送信してください。本記事の公開W3Techsより7月15日の情報を引用。SRI/キャッシュの存在により、即座に改竄可能だったウェブサイトはこの数字よりも少なかった。↩︎W3Techsより7月15日時点の情報を引用。↩︎このようなアーカイブファイルは、evilarcといったようなツールを用いることにより作成することが出来る。記憶が定かではないが、この日の夕食は冷凍餃子だったと思う。仕事で疲れていたのと、空腹だった事が重なり、ろくに確認もせずに補完されたコマンドを実行してしまっていた。