macOS Library Injection Analysis
A skill for analyzing macOS binaries for library injection vulnerabilities and understanding dyld behavior.
When to Use This Skill
Use this skill when:
- Analyzing macOS binaries for library injection vulnerabilities
- Checking if
DYLD_INSERT_LIBRARIESwill work on a binary - Finding weak linked libraries that could be hijacked
- Examining rpath configurations in Mach-O binaries
- Testing for privilege escalation via library loading
- Understanding macOS dyld restrictions and protections
- Auditing binaries for hardened runtime and library validation
Core Concepts
DYLD_INSERT_LIBRARIES
Similar to LD_PRELOAD on Linux, this environment variable allows loading a custom library into a process. However, Apple has significantly restricted this since 2012.
Restrictions that block DYLD_INSERT_LIBRARIES:
- Binary is setuid/setgid
- Binary has
__RESTRICT/__restrictsection - Binary has hardened runtime without
com.apple.security.cs.allow-dyld-environment-variablesentitlement
Library Validation
Even if DYLD_INSERT_LIBRARIES is allowed, the binary may check library signatures. To load a custom library, the binary needs:
com.apple.security.cs.disable-library-validationentitlementcom.apple.private.security.clear-library-validationentitlement- OR no hardened runtime flag
- OR the library is signed with the same certificate as the binary
Quick Checks
Check Binary Restrictions
# Check for hardened runtime
codesign --display --verbose <binary>
# Look for flags=0x10000(runtime) in CodeDirectory
# Check entitlements
codesign -dv --entitlements :- <binary>
# Check for setuid/setgid
ls -l <binary>
# Look for 's' in permissions
# Check runtime flags at execution time
csops -status <pid>
# Flag 0x800 indicates CS_RESTRICT
Find Weak Linked Libraries
# Find LC_LOAD_WEAK_DYLIB entries
otool -l <binary> | grep LC_LOAD_WEAK_DYLIB -A 5
# These libraries can be hijacked if they don't exist
# and the attacker can write to the expected path
Find RPATH Configurations
# Find rpath and load commands
otool -l <binary> | grep -E "LC_RPATH|LC_LOAD_DYLIB" -A 5
# Look for @rpath references that could be hijacked
# @executable_path = directory containing main executable
# @loader_path = directory containing the Mach-O binary
Attack Vectors
1. DYLD_INSERT_LIBRARIES Injection
Works when:
- Binary is NOT setuid/setgid
- Binary has NO
__RESTRICTsection - Binary has
com.apple.security.cs.allow-dyld-environment-variablesentitlement OR no hardened runtime - Library validation is disabled OR library is properly signed
Test:
DYLD_INSERT_LIBRARIES=/path/to/inject.dylib ./binary
2. Weak Linked Library Hijacking
Works when:
- Binary has
LC_LOAD_WEAK_DYLIBfor a missing library - Attacker can write to the expected library path
- Library validation restrictions are satisfied
Find targets:
otool -l <binary> | grep LC_LOAD_WEAK_DYLIB -A 5
3. RPATH Hijacking
Works when:
- Binary uses
@rpathinLC_LOAD_DYLIB - One of the rpath directories is writable by attacker
- Library validation restrictions are satisfied
Note: macOS searches rpath directories in order. If a library exists in multiple paths, the first match wins.
4. Dlopen Hijacking
Search order for dlopen (no slash in name):
$DYLD_LIBRARY_PATHLC_RPATHdirectories- Current working directory (if unrestricted)
$DYLD_FALLBACK_LIBRARY_PATH/usr/local/lib/(if unrestricted)/usr/lib/
Works when:
- Binary is unrestricted (no setuid, no CS_RESTRICT)
- Attacker can write to one of the search paths
- Library validation restrictions are satisfied
5. Relative Path Hijacking
Works when:
- Privileged binary loads library via relative path (
@executable_path,@loader_path) - Library validation is disabled
- Attacker can move the binary or write to the relative path
Testing Scripts
Use the bundled scripts for automated analysis:
scripts/check_binary_restrictions.sh- Check all restriction flagsscripts/find_weak_libraries.sh- Find weak linked librariesscripts/find_rpath_configs.sh- Find rpath configurationsscripts/test_dyld_injection.sh- Test DYLD_INSERT_LIBRARIES
Common Patterns
SUID Binary Testing
# Make binary setuid
sudo chown root <binary>
sudo chmod +s <binary>
# Test injection (won't work on setuid)
DYLD_INSERT_LIBRARIES=inject.dylib ./binary
# Remove setuid
sudo chmod -s <binary>
Hardened Runtime Testing
# Apply runtime protection
codesign -s <cert-name> --option=runtime ./binary
# Apply library validation
codesign -f -s <cert-name> --option=library ./binary
# Sign library with same certificate
codesign -f -s <cert-name> inject.dylib
# Apply CS_RESTRICT
codesign -f -s <cert-name> --option=restrict ./binary
Creating Test Binary with __RESTRICT Section
gcc -sectcreate __RESTRICT __restrict /dev/null hello.c -o hello-restrict
Important Notes
Environment Variable Pruning:
DYLD_*andLD_LIBRARY_PATHare removed for restricted binaries bypruneEnvironmentVariables()in dyldLibrary Validation: Even if injection is allowed, library signatures may be checked. The library must be signed with the same certificate as the binary, or the binary must have library validation disabled
Universal Binaries: macOS uses universal files combining 32-bit and 64-bit libraries. There are no separate search paths for different architectures
Dyld Cache: Most OS dylibs are combined into the dyld cache and don't exist on disk. Use
dlopen_preflight()instead ofstat()to check if a dylib existsSetuid/SGID: These binaries always have environment variables pruned and are highly restricted
CS_RESTRICT Flag: Can be applied dynamically at execution time even if not present in the binary's signature