Injection in macOS is different than on Windows - you can’t just open a handle to a remote process and allocate/write memory. There are a couple of commonly successful TCC bypass methods you can look for.

Shared Library Injection

Shared lbraries (Windows .dll or Linux .so) are called “dylib” files on macOS. If an application is compiled without the hardened runtime, you should be able to load an arbitrary dylib into it with the DYLD_INSERT_LIBRARIES environment variable. You can check for this flag using the fowwoling command:

codesign -dv --entitlements :- /Applications/someapp.app/Contents/MacOS/someapp

If the hardened runtime is set, you will see this line:

If the hardened runtime is not set, you will see this line:

The previous screenshots were taken using two different versions of NoMAD. Since the signature is still valid, the older version of the application can be used to access anything NoMAD can.

Once you find an application without the hardened runtime, you can launch use it to lad an arbitrary dylib. To simplify the test, you can start with hijack.dylib and monitor for logs with the following command:

log stream --style syslog --predicate 'eventMessage CONTAINS[c] *BHIS*'

Then, launch the target application with a command such as this:

DYLD_INSERT_LIBRARIES=hijack.dylib ./NoMAD.app/Contents/MacOS/NoMAD

You can automate this search using Dylib Hijack Scanner.

Electron Injection

We can often inject into Electron applications like Slack, Teams, and VSCode - even if they have the hardened runtime flag. There are a couple of methods we can attempt.

First, we can execute each Electron app with the ELECTRON_RUN_AS_NODE environment variable. This doesn’t work for all apps/versions, but it is quick and easy to check.

This can be done over C2 as well, by creating electron_run_as_node.plist and executing launchctl load /tmp/test.plist.

Second, we can attept to launch an Electron app with remote debugging enabled. You can check if an app will launch this way by launching it with the --inspect flag.

Once you have an application that supports remote debugging, you can attempt to inject arbitrary JavaScript into it, for example with electron_remote_debug.py.

Additional Resources