For Red Team members who already know Linux, using a Mac can be confusing. Many people assume that macOS and Linux are similar operating systems, and in many ways, they are. Both use Unix-like principles and rely on the command line interface for much of their functionality.
Once you move beyond the Bash/Zsh prompt, macOS becomes a different beast. It has layers of security and proprietary frameworks that Linux doesn’t have. It’s not just “Linux with a better UI.” It’s Darwin, a BSD-derivative with a Mach microkernel, wrapped in proprietary Apple frameworks.
In this guide, we’ll bridge the gap. We will cover the “BSD Trap” that catches Linux users, the nightmare of TCC (Transparency, Consent, and Control), and how to persist and execute code using Apple’s unique “Living off the Land” binaries.
The Core: Darwin and the BSD Heritage#
macOS runs on Darwin, an open-source Unix-like operating system that combines the XNU kernel with a BSD-based userspace (userland). This is the first trap for Linux users: macOS is BSD-based, not GNU-based.
Command Line Differences (GNU vs. BSD)#
As a red team operator, your shell scripts will break if you assume GNU behavior.
sed: On Linux,sed -i 's/foo/bar/g' fileworks. On macOS, you must provide an empty string for the backup extension:sed -i '' 's/foo/bar/g' file.ps: Linux uses standardps aux. macOS prefers BSD flags likeps -ax -o pid,user,comm.netstat: Modern macOS has deprecatednetstatfor process-to-port mapping. Uselsof -iTCP -sTCP:LISTEN -n -Pinstead.grep: The macOS version ofgrepdoes not support-P(Perl-compatible regex) by default. Use-Efor extended regex.base64: macOS usesbase64 -i input -o output. Linux usesbase64 input > output.
The Security Boundaries: TCC and SIP#
On Linux, root is god. On macOS, Apple restricts even root. This is the single most important thing for an offensive operator to understand.
SIP (System Integrity Protection)#
SIP prevents even the root user from modifying certain system directories (like /System, /bin, /sbin). It also prevents “code injection” into system processes via task_for_pid unless Apple signs the binary with specific entitlements.
- Checking SIP:
csrutil status. - Implication: You cannot simply drop a rootkit into
/System/Library/Extensions. Limit yourself to/Library,~/Library, or/usr/local.
TCC (Transparency, Consent, and Control)#
TCC is the “sandbox” that prompts you for permission to access the camera, microphone, or “Full Disk Access” (FDA).
- The Problem: Even if you have a root shell via SSH, if you try to
ls ~/Documents, the command might fail (Operation not permitted) or hang while a GUI prompt appears on the user’s desktop—tipping them off immediately. - The Database: TCC stores permissions in SQLite databases protected by SIP.
- System:
/Library/Application Support/com.apple.TCC/TCC.db - User:
~/Library/Application Support/com.apple.TCC/TCC.db
- System:
- Red Team Strategy: Don’t attack TCC directly. Attack the apps that have TCC rights. If
Terminal.apphas Full Disk Access, running commands inside it inherits those rights.
Keychain Access: The Vault of Secrets#
macOS stores passwords, certificates, and keys in the Keychain. For an offensive operator, this is the primary target for credential harvesting.
Dumping the Keychain via CLI#
If you know the user’s login password (or have a keylogger), you can use the built-in security tool.
# Dump generic passwords (interactive prompt unless unlocked)
security find-generic-password -ga "TargetName"
security to access the key. This is a OpSec risk.The “Dump-All” Strategy#
Tools like Chainbreaker can parse the .keychain-db files directly from ~/Library/Keychains/ if you can extract the master key. This usually requires root privileges to read the system keys.
Persistence: LaunchDaemons and LaunchAgents#
While cron still exists on macOS (for now), the professional way to persist is via launchd.
- LaunchDaemons (
/Library/LaunchDaemons): Run as root at boot. Used for system-wide persistence. - LaunchAgents (
~/Library/LaunchAgents): Run as the user at login. Used for user-land persistence.
Creating a Persistent Agent#
Create a plist file (for example com.apple.update.helper.plist):
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.apple.update.helper</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/python3</string>
<string>/Users/Shared/payload.py</string>
</array>
<key>RunAtLoad</key>
<true />
</dict>
</plist>
Load it:
launchctl load -w ~/Library/LaunchAgents/com.apple.update.helper.plist
Living off the Land: JXA and AppleScript#
macOS has a unique automation layer called OSA (Open Scripting Architecture). You can script almost any application using AppleScript or JavaScript for Automation (JXA).
JXA (JavaScript for Automation)#
JXA allows you to use JavaScript syntax to bridge into native Objective-C (“Cocoa”) APIs. This allows for powerful in-memory execution without dropping binaries.
Example: Reading a file using ObjC bridge
// Run via: osascript -l JavaScript payload.js
var fm = $.NSFileManager.defaultManager;
var data = fm.contentsAtPath("/etc/passwd");
var str = $.NSString.alloc.initWithDataEncoding(data, $.NSUTF8StringEncoding);
console.log(str.js);
AppleScript Phishing#
You can invoke native-looking dialog boxes to steal passwords.
osascript -e 'display dialog "Software Update requires your password to continue:" default answer "" with icon file "System:Library:CoreServices:Software Update.app:Contents:Resources:SoftwareUpdate.icns" with hidden answer'
The “Quarantine” Flag: Gatekeeper#
When you download a file via a browser, macOS adds the com.apple.quarantine extended attribute (xattr). This triggers Gatekeeper and Notarization checks.
If you curl or wget a binary, the flag is often not added. However, unzipping a file via Finder might propagate the flag.
Removing the Mark of the Beast:
# Check attributes (@ sign in ls -la@ indicates xattrs)
ls -la@ payload
# Remove quarantine
xattr -d com.apple.quarantine payload
Always perform this step on any binary you upload before trying to execute it.
macOS Post-Exploitation Tools#
If you find yourself on a Mac and need to escalate or pivot:
- Mythic (Poseidon / Apfell): A robust C2 framework with macOS-specific agents written in Go and JXA.
- SwiftBelt: An enumeration tool inspired by Seatbelt, written in Swift to avoid command-line logging.
- Basic Recon:
system_profiler SPSoftwareDataType: OS Version.dscl . list /Users: List local users.scutil --proxy: Check proxy settings.
Conclusion#
macOS requires a shift in mindset. You are not fighting just file permissions; you are fighting Entitlements, TCC transparency, and a hardened kernel. To succeed, you must stop thinking like a sysadmin and start thinking like a macOS developer who knows how to bend launchd, JXA, and Apple Events to their will.
Happy hunting!