macOS Launch/Environment Constraints & Trust Cache
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Basic Information
Launch constraints in macOS were introduced to enhance security by regulating how, who, and from where a process can be initiated. Initiated in macOS Ventura, they provide a framework that categorizes each system binary into distinct constraint categories, which are defined within the trust cache, a list containing system binaries and their respective hashes. These constraints extend to every executable binary within the system, entailing a set of rules delineating the requirements for launching a particular binary. The rules encompass self constraints that a binary must satisfy, parent constraints required to be met by its parent process, and responsible constraints to be adhered to by other relevant entities.
The mechanism extends to third-party apps through Environment Constraints, beginning from macOS Sonoma, allowing developers to protect their apps by specifying a set of keys and values for environment constraints.
You define launch environment and library constraints in constraint dictionaries that you either save in launchd
property list files, or in separate property list files that you use in code signing.
There are 4 types of constraints:
Self Constraints: Constrains applied to the running binary.
Parent Process: Constraints applied to the parent of the process (for example
launchd
running a XP service)Responsible Constraints: Constraints applied to the process calling the service in a XPC communication
Library load constraints: Use library load constraints to selectively describe code that can be loaded
So when a process tries to launch another process — by calling execve(_:_:_:)
or posix_spawn(_:_:_:_:_:_:)
— the operating system checks that the executable file satisfies its own self constraint. It also checks that the parent process’s executable satisfies the executable’s parent constraint, and that the responsible process’s executable satisfies the executable’s responsible process constraint. If any of these launch constraints aren’t satisfied, the operating system doesn’t run the program.
If when loading a library any part of the library constraint isn’t true, your process doesn’t load the library.
LC Categories
A LC as composed by facts and logical operations (and, or..) that combines facts.
The facts that a LC can use are documented. For example:
is-init-proc: A Boolean value that indicates whether the executable must be the operating system’s initialization process (
launchd
).is-sip-protected: A Boolean value that indicates whether the executable must be a file protected by System Integrity Protection (SIP).
on-authorized-authapfs-volume:
A Boolean value that indicates whether the operating system loaded the executable from an authorized, authenticated APFS volume.on-authorized-authapfs-volume
: A Boolean value that indicates whether the operating system loaded the executable from an authorized, authenticated APFS volume.Cryptexes volume
on-system-volume:
A Boolean value that indicates whether the operating system loaded the executable from the currently-booted system volume.Inside /System...
...
When an Apple binary is signed it assigns it to a LC category inside the trust cache.
iOS 16 LC categories were reversed and documented in here.
Current LC categories (macOS 14 - Somona) have been reversed and their descriptions can be found here.
For example Category 1 is:
(on-authorized-authapfs-volume || on-system-volume)
: Must be in System or Cryptexes volume.launch-type == 1
: Must be a system service (plist in LaunchDaemons).validation-category == 1
: An operating system executable.is-init-proc
: Launchd
Reversing LC Categories
You have more information about it in here, but basically, They are defined in AMFI (AppleMobileFileIntegrity), so you need to download the Kernel Development Kit to get the KEXT. The symbols starting with kConstraintCategory
are the interesting ones. Extracting them you will get a DER (ASN.1) encoded stream that you will need to decode with ASN.1 Decoder or the python-asn1 library and its dump.py
script, andrivet/python-asn1 which will give you a more understandable string.
Environment Constraints
These are the Launch Constraints set configured in third party applications. The developer can select the facts and logical operands to use in his application to restrict the access to itself.
It's possible to enumerate the Environment Constraints of an application with:
Trust Caches
In macOS there are a few trust caches:
/System/Volumes/Preboot/*/boot/*/usr/standalone/firmware/FUD/BaseSystemTrustCache.img4
/System/Volumes/Preboot/*/boot/*/usr/standalone/firmware/FUD/StaticTrustCache.img4
/System/Library/Security/OSLaunchPolicyData
And in iOS it looks like it's in /usr/standalone/firmware/FUD/StaticTrustCache.img4
.
On macOS running on Apple Silicon devices, if an Apple signed binary is not in the trust cache, AMFI will refuse to load it.
Enumerating Trust Caches
The previous trust cache files are in format IMG4 and IM4P, being IM4P the payload section of a IMG4 format.
You can use pyimg4 to extract the payload of databases:
(Another option could be to use the tool img4tool, which will run even in M1 even if the release is old and for x86_64 if you install it in the proper locations).
Now you can use the tool trustcache to get the information in a readable format:
The trust cache follows the following structure, so The LC category is the 4th column
Then, you could use a script such as this one to extract data.
From that data you can check the Apps with a launch constraints value of 0
, which are the ones that aren't constrained (check here for what each value is).
Attack Mitigations
Launch Constrains would have mitigated several old attacks by making sure that the process won't be executed in unexpected conditions: For example from unexpected locations or being invoked by an unexpected parent process (if only launchd should be launching it)
Moreover, Launch Constraints also mitigates downgrade attacks.
However, they don't mitigate common XPC abuses, Electron code injections or dylib injections without library validation (unless the team IDs that can load libraries are known).
XPC Daemon Protection
In the Sonoma release, a notable point is the daemon XPC service's responsibility configuration. The XPC service is accountable for itself, as opposed to the connecting client being responsible. This is documented in the feedback report FB13206884. This setup might seem flawed, as it allows certain interactions with the XPC service:
Launching the XPC Service: If assumed to be a bug, this setup does not permit initiating the XPC service through attacker code.
Connecting to an Active Service: If the XPC service is already running (possibly activated by its original application), there are no barriers to connecting to it.
While implementing constraints on the XPC service might be beneficial by narrowing the window for potential attacks, it doesn't address the primary concern. Ensuring the security of the XPC service fundamentally requires validating the connecting client effectively. This remains the sole method to fortify the service's security. Also, it's worth noting that the mentioned responsibility configuration is currently operational, which might not align with the intended design.
Electron Protection
Even if it's required that the application has to be opened by LaunchService (in the parents constraints). This can be achieved using open
(which can set env variables) or using the Launch Services API (where env variables can be indicated).
References
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Last updated