type
status
slug
tags
category
icon
password
Property
Jan 5, 2024 08:18 AM
date
summary
前言
越来越多的桌面应用程序都使用了electron 开发,再加上最近私建企x爆发了xss漏洞,只要向特定用户发送消息就可以直接rce。所以想研究一下electron安全,本文主要是将了一下electron如何开发,和一些不安全配置下的Rce条件。目前最新版本electron的rce也是主要围绕三个安全配置来操作。
Electron 开发
安装
npm 初始化
通过npm安装electron
新建 index.js 文件 里面包含如下内容
并且在package.js 添加start scripts 完整package.js内容如下
然后执行 npm run start 终端应该会输出
欢迎来到 Electron 👋
,正确提示则表示Electron
安装完成基础
窗口创建
将网页装载到BrowserWindow 在src目录新建index.html
然后修改index.js 文件
vscode debug
在主要程序中加载下面代码,可以开启开发者模式类似浏览器F12
新建一个
.vscode
文件夹,然后在其中新建一个 launch.json 配置文件并填写如下内容保存后,当您选择侧边栏的 “运行和调试”,将会出现一个 "Main + renderer" 选项。然后您便可设置断点,并跟踪主进程和渲染器进程中的所有变量。
预加载脚本
Electron 的主进程是一个拥有着完全操作系统访问权限的 Node.js 环境。 On top of Electron modules, you can also access Node.js built-ins, as well as any packages installed via npm. 另一方面,出于安全原因,渲染进程默认跑在网页页面上,而并非 Node.js里。
为了将 Electron 的不同类型的进程桥接在一起,我们需要使用被称为 预加载 的特殊脚本。
从 Electron 20 开始,预加载脚本默认 沙盒化 ,不再拥有完整 Node.js 环境的访问权。 实际上,这意味着你只拥有一个 polyfilled 的
require
函数,这个函数只能访问一组有限的 API。可用的 API | 详细信息 |
Electron 模块 | 渲染进程模块 |
Node.js 模块 | |
Polyfilled 的全局模块 |
index.js
index.html
preload.js
renderer.js
如果输出如下内容,代表预加载成功
IPC进程之间通信
使用 Electron 的
ipcMain
模块和 ipcRenderer
模块来进行进程间通信。 为了从你的网页向主进程发送消息,你可以使用 ipcMain.handle
设置一个主进程处理程序(handler),然后在预处理脚本中暴露一个被称为 ipcRenderer.invoke
的函数来触发该处理程序(handler)。例如如果程序需要打印系统命令
uname -a
首先,在预处理脚本中设置
invoke
调用:然后,在主进程中设置你的
handle
监听器。 我们在 HTML 文件加载之前完成了这些,所以才能保证在你从渲染器发送 invoke
调用之前处理程序能够准备就绪。将发送器与接收器设置完成之后,现在你可以将信息通过刚刚定义的
'uname'
通道从渲染器发送至主进程当中。成功传递到前端
模式 1:渲染器进程到主进程(单向)
要将单向 IPC 消息从渲染器进程发送到主进程,您可以使用
[ipcRenderer.send](<https://www.electronjs.org/zh/docs/latest/api/ipc-renderer>)
API 发送消息,然后使用 [ipcMain.on](<https://www.electronjs.org/zh/docs/latest/api/ipc-main>)
API 接收。通过前端UI 修改标题,实例代码
要将消息发送到上面创建的监听器,您可以使用
ipcRenderer.send
API。 默认情况下,渲染器进程没有权限访问 Node.js 和 Electron 模块。 作为应用开发者,您需要使用 contextBridge
API 来选择要从预加载脚本中暴露哪些 API。在渲染器程序调用api
尝试调用cmd 执行命令
模式 2:渲染器进程到主进程(双向)
双向 IPC 的一个常见应用是从渲染器进程代码调用主进程模块并等待结果。 这可以通过将
[ipcRenderer.invoke](<https://www.electronjs.org/zh/docs/latest/api/ipc-renderer#ipcrendererinvokechannel-args>)
与 [ipcMain.handle](<https://www.electronjs.org/zh/docs/latest/api/ipc-main#ipcmainhandlechannel-listener>)
搭配使用来完成。示例代码写了一个命令执行工具,执行命令并显示在前端
在主程序创建对应函数
预加载,设置导出函数
渲染程序,执行对应程序并将结果返回到前端显示
可以成功执行代码,此时如果该软件存在xss漏洞,哪怕开了上下文、安全模式和沙箱也可以导致Rce
模式 3:主进程到渲染器进程
将消息从主进程发送到渲染器进程时,需要指定是哪一个渲染器接收消息。 消息需要通过其
[WebContents](<https://www.electronjs.org/zh/docs/latest/api/web-contents>)
实例发送到渲染器进程。 此 WebContents 实例包含一个 [send](<https://www.electronjs.org/zh/docs/latest/api/web-contents#contentssendchannel-args>)
方法,其使用方式与 ipcRenderer.send
相同。参考代码https://github.com/electron/electron/tree/v25.2.0/docs/fiddles/ipc/pattern-3
- 使用
webContents
模块发送消息
对于此演示,我们需要首先使用 Electron 的
Menu
模块在主进程中构建一个自定义菜单,该模块使用 webContents.send
API 将 IPC 消息从主进程发送到目标渲染器。index.js (Main Process)
出于本教程的目的,请务必注意,
click
处理函数通过 update-counter
通道向渲染器进程发送消息(1
或 -1
)。INFO
请确保您为以下步骤加载了
index.html
和 preload.js
入口点!- 通过预加载脚本暴露
ipcRenderer.on
与前面的渲染器到主进程的示例一样,我们使用预加载脚本中的
contextBridge
和 ipcRenderer
模块向渲染器进程暴露 IPC 功能:preload.js (Preload Script)
加载预加载脚本后,渲染器进程应有权访问
window.electronAPI.onUpdateCounter()
监听器函数。preload.js (Preload Script)
但是,与通过 context bridge 暴露预加载 API 相比,此方法的灵活性有限,因为监听器无法直接与渲染器代码交互。
- 构建渲染器进程 UI
为了将它们联系在一起,我们将在加载的 HTML 文件中创建一个接口,其中包含一个
#counter
元素,我们将使用该元素来显示值:index.html
最后,为了更新 HTML 文档中的值,我们将添加几行 DOM 操作的代码,以便在每次触发
update-counter
事件时更新 #counter
元素的值。renderer.js (Renderer Process)
在上面的代码中,我们将回调传递给从预加载脚本中暴露的
window.electronAPI.onUpdateCounter
函数。 第二个 value
参数对应于我们传入 webContents.send
函数的 1
或 -1
,该函数是从原生菜单调用的。可选:返回一个回复
对于从主进程到渲染器进程的 IPC,没有与
ipcRenderer.invoke
等效的 API。 不过,您可以从 ipcRenderer.on
回调中将回复发送回主进程。我们可以对前面例子的代码进行略微修改来演示这一点。 在渲染器进程中,使用
event
参数,通过 counter-value
通道将回复发送回主进程。renderer.js (Renderer Process)
在主进程中,监听
counter-value
事件并适当地处理它们。main.js (Main Process)
编译
因为安装Electron 的核心模块中没有捆绑任何用于打包或分发文件的工具,需要安装Electron Forge 打包
然后在执行即可
交叉编译
win打包:平台 --paltform=win32 | 架构 --arch=x64 | 图标 --icon=**.ico
mac打包:平台 --paltform=drawin | 架构 --arch=x64 | 图标 --icon=**.ico
交叉编译参考
反编译
将程序中的app.asar提取到本地 然后执行下面命令
安全风险
常用payload
electron 并不是存在Xss就可以Rce
这里主要关注三个配置选项
20以前版本sandbox默认关闭
12版本以后默认开启contextIsolation
nodeIntegration | contextIsolation | sandbox | ㅤ |
FALSE | FALSE | FALSE | perload可控 = Rce (20以前版本sandbox默认关闭) |
FALSE | FALSE | TRUE | 无法rce |
FALSE | TRUE | FALSE | 考虑IPC |
FALSE | TRUE | TRUE | 考虑IPC |
TRUE | FALSE | FALSE | xss = Rce |
TRUE | FALSE | TRUE | 考虑IPC |
TRUE | TRUE | FALSE | 考虑IPC |
TRUE | TRUE | TRUE | 考虑IPC |
Rce: 开启nodeIntegration,关闭contextIsolation,关闭sanbox
这种情况只要存在xss ,直接就可以rce
Rce: perload覆盖
如果关闭了
contextIsolation
,可以通过覆盖perload中的特定方法 (In preload code or in Electron internal code)通过覆盖perload成功改变逻辑,执行了exec