An approach to Mac OS Thick Client Pen Test.

Dhanishtha Awasthi
5 min readSep 21, 2023

An approach to not so easy pen test.

I’m here today with a mind map to approach your Mac OS thick client pen test.

Identify the battle ground.

Architecture of a normal Mac OS application. MacOS Applications are suppose to work on MacOS (OSX) platform. They are bundled into “Application Bundles” with the “.app” as the extension. These applications consist of bundled hierarchy that contains actual executable and the resources required by the same. Below is architecture “Books” application bundle on MacOS

Bundle contains some important files to look into.

The Info.plist is a property list file stored at a location that identifies a directory hierarchy as a bundle. For a list of Info.plist keys, see Information Property List. This is used to analyze what all permissions does the application have. What all resources can it access.

Code content is either executable code, like a helper tool, or another bundle that contains executable code, like an app extension. In this context, executable code means a Mach-O image. It doesn’t include things like shell scripts, Python scripts, and AppleScripts (unless you save the AppleScript as an application). Although you execute a script, it has no place to hold a code signature, so you treat it as a resource.

Resources are everything that’s not code.

Security Protections which are already in place.

  1. SIP : System Integrity Protection. System Integrity Protection (SIP) is a security technology in macOS that safeguards certain system directories from unauthorized access, even for the root user. It prevents modifications to these directories, including creation, alteration, or deletion of files. The main directories that SIP protects are:
  • /System
  • /bin
  • /sbin
  • /usr

SIP needs to be either bypassed (tough task) or disabled.

# To check status of SIP 
csrutil status
# To disable SIP
csrutil disable
#To keep SIP enabled, but just not prevent debugging.
csrutil enable --without debug

2. Notarization: Developers need to submit the application. Then application is sent to Apple Notary Service, where rigorous tests are made to scrutinize the software against malicious content. Also against code-signing. Notarization ticket is created, which is attached to the software. Gatekeeper looks for this ticket.

3. Gatekeeper: Checks if user runs only trusted software. Prevents running application directly as an application, by first opening it as installer package. It checks if the downloaded software is signed by the recognized developer. It checks if it is already Notarized by the Apple. It prompts user to approve before running the application.

4. Quarantine files: Files from non trusted source have Quarantine flag set.

5. Sandbox: Limits running application inside a Sandboxed environment, where only access to resources mentioned in Info.plist is given. Sandbox profile will contain access to the resources. App runs with a sandbox profile.

6. TCC (Transparency, Consent, and Control): is a mechanism in macOS to limit and control application access to certain features, usually from a privacy perspective. This can include things such as location services, contacts, photos, microphone, camera, accessibility, full disk access, and a bunch more.

What to look for. From here starts our approach.

Logs: All the applications log their data somewhere. These may be debug logs, error logs, system logs, access logs etc. A lot of information can be gained looking into the logs. The debug logs also can define, what is the flow of application, as in what was tried to be accessed after and before.

The Logs can be UNIFIED Logs and can be hidden. This is when a <private> tag is attached to the logs.

Static Analysis

2. Entitlements: Key-value pairs that grant an executable permission to use a service or technology.

#To look for the entitlements on the app.
codesign -d --entitlements :- /System/Applications/

3. Verify if the content of app is modified.

Codesign --verify --verbose /Application/

4. Get the real signer of the binary. You can also resign the binary. This is required when you want to copy the binary from expected path to intended path.

# To get signer details 
codesign --v -d /bin/ls 2?&1 | grep -E "Authority|TeamIdentifier"
#To sign a binary
codesign -s <cert-keychain-name> abcd
#"Where abcd is app'

5. You need to see if (lib) and (bin) are both signed by same certs. In this case and entitlements can be bypassed

# Check if the signature is valid
spctl --assess --verbose /Applications/

6. If the language used is Objective-C you need to look for “objc_msgsend()” function. And play with parameters. Similarly if the language is Swift, you need to look for __Swift prefix in the module.

7. ObjDump : You can list the dynamically linked libraries using tool. You can get the header of the binary. You can see if Symbol Table exists. You can disassemble the binary.

De compile/Disassemble the Application

  1. Hopper : UI tool to de compile, disassemble the Mac Os Application.
  2. Otool: De compile the application. Dynamically linked libraries can be listed as well.


  1. Process Analysis: Process Monitor
  2. File Analysis: File Monitor
  3. Crescendo : Procmon Mac OS equivalent. GUI Tool for Event monitoring. This can be files, process, network.
  4. Task Analysis: Task Explorer

Process Abuse. In this section we will mention the ways in which processes on mac Os can be manipulated to execute our code.

  1. Library Injection :

DyLib Hijacking

Dyld Hijacking

2. Function Hooking

Frida Dynamic Instrumentation

Dyld injection via Environment Variables

Dylib process injection

Debugger — lldb

3. IPC Communication Abuse

Reuse PID

Thread injection via task port

XPC Authorization

XPC Connecting process check

Approach for the two Hijacking techniques

Dylib Hijacking

What not like windows on Mac OS :

a. MacOS Binaries indicate full path. So no NAME NOT FOUND type.

b. Mac OS never searches the folder of $PATH Variable.

Different commands used to load libraries and loopholes. Look into Imageloader.cpp inside ImageLoader::recursiveLoadLibraries

a. LC_LOAD_DYLIB: contains path for specific libraries to load. Look for several @rpaths . Abuse the paths with missing libraries.

b. LC_LOAD_WEAK_DYLIB: Look for missing weak linked libraries. Place dylib where expected. For this lib -> required is false and LC_LOAD_WEAK_DYLIB is true .

You can do a quick check using “Dylib Hijack Scanner”

Dyld Hijacking

To do this, different search order paths are followed, which you need to trace down. One example is as follows.

  3. CWD (if unrestricted process)
  5. /usr/local/lib
  6. /usr/lib


Look at the architecture of Application first.

  • Universal Mach O Binary

Approach as follows.

  1. Of course SIP disabled or bypassed is step numro uno.

2. Look for PT_DENY_ATTACH to be not present.

If it is present. We need to disable it. For that we need to do ptrace detection. If ptrace is present. You need to potentially inspect and undo ptrace call. It would look like ptrace(PT_DENY_ATTACH,0,0,0) You need to find this, p_lflag and change the value to disable PT_DENY_ATTACH.

3. Finally attach (LLDB) — and run command. You can find list of commands here.

Few more key take aways

  1. VM detection can be ON. hw.model is used to check if mac or not.
  2. Malwares may also detect VM using MAC Address pattern.
  3. We need to see if debugging is disabled.
if(P_TRACED == (info.kp_proc_p_flag & P_TRACED)