This research has been presented at:
- DEF CON 31 - ELECTRONizing macOS privacy
- Objective By the Sea - ELECTRONizing macOS Privacy - a New Weapon in Your Red Teaming Armory
In 2019 I wrote a blog post about injecting code to Electron apps to impersonate their TCC permissions. The trick was really simple because at that time the only thing an attacker had to do was to modify one of the Electron app’s HTML files or the whole ASAR. In macOS Ventura, this trick stopped working as the app protection mechanism has been introduced.
This mechanism broke my simple Electron injection technique so it was time to figure out something new…
TCC inheritance problem
TCC is now a much bigger mechanism than it was before. As we all know, complicity usually indicates vulnerabilities. One such example is the inheritance of privacy privileges (look at: CVE-2020-10008). What happens when an entitled process spawns a child that wants to access something? Or if a process has a completely separate privileged helper that wants to access your desktop for example? Who should be then responsible for that action - the parent or the child?
As I’m not going to update this post in the future, please keep in mind that the TCC inheritance may change in the future. On macOS Sonoma, generally speaking, situation looks as follows:
- When an app has private TCC entitlements – its permissions are not inherited by other apps they spawn
- When an app has TCC permission granted by the user (User clicked “OK” in the prompt) - its permissions are inherited
Electron apps are in the second group so if we make our target Electron app spawn a malicious process - we would be able to inherit its TCC permissions.
--inspect flag for the rescue
Turns out that Electron has a debugging feature that we may trigger by setting a
--inspect flag. Before we do so it’s important to verify if the targeted app respects it:
$ npx @electron/fuses read --app /Applications/Test.app
Analyzing app: Test.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
EnableNodeCliInspectArguments is enabled it means that we can use the
const exec = require('child_process').exec;
You may ask what if
EnableNodeCliInspectArguments is disabled? - Well, injecting to old vulnerable versions still works.
Take a look at the demos below.
Teams vs electroniz3r
Visual Studio Code vs electroniz3r