(Lazily) Making our own entitlements dumper

Goals

Introduction: The XNU Entitlements System

Apple’s Systems are known for being locked down & (literally) sandboxed, where no mere process can simply access the filesystem outside of that process’ own container directory which is created by the System (See: Sandbox.kext), or do stuff like accessing Camera/Microphone functions without user permissions, etc etc. One of the many ways this system of restrictions is enforced by is called ‘Entitlements’.

In simple terms, Entitlements describe the capabilities of a binary to the System by embedding a Property List (A serializable format, often being XML) into the binary itself. This concept is familiar to iOS & macOS developers who sometimes have to deal with it due to certain developer functionalities being locked being entitlements, and those entitlements are often (but not always) locked to paid Apple Developer accounts (ie, if you want to use Push Notifications in your iOS app, you must add an Entitlement through the Xcode IDE, available only if you have a paid Apple Developer Account).

The following image shows a list of Entitlements available to add to an application through through Xcode List of Entitlements available to add to an application through through Xcode Each Entitlement is named in Reverse domain name notation (ie, com.apple.whatever), and can have a value of a String, Bool, Integer, Float, Dictionary, or an Array. For example, here are the Entitlements of macOS’ TextEdit.app, macOS’ built in text editor app, as a basic example:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.application-identifier</key>
	<string>com.apple.TextEdit</string>
	<key>com.apple.developer.ubiquity-container-identifiers</key>
	<array>
		<string>com.apple.TextEdit</string>
	</array>
	<key>com.apple.private.hid.client.event-dispatch.internal</key>
	<true/>
	<key>com.apple.security.app-sandbox</key>
	<true/>
	<key>com.apple.security.files.user-selected.executable</key>
	<true/>
	<key>com.apple.security.files.user-selected.read-write</key>
	<true/>
	<key>com.apple.security.print</key>
	<true/>
</dict>
</plist>

Just to go over some Entitlements here:

Some Developer accounts are more (Entitled) than others

As mentioned above, some Entitlements are restricted only to Apple Developer paid accounts, which you must pay 100$ a year for to get access to, meaning that ultimately some API are just gated by paid Apple Developer Account (Apple MusicKit, Push Notification APIs).

Developers can (and have to) also apply for certain Entitlements, for example, creating a CarPlay application requires you submit an application form to Apple to get the required entitlements to create your CarPlay app.

However, above all others lies the one who has (and creates) all Entitlements: Apple themselves, as they can and do sign their applications with their undocumented Entitlements freely, their applications have the ability to do just about anything without the same restrictions that other apps face (ie, being unsandboxed & contacting System Daemons), something that no normal app on iOS could ever dream of doing.

The Dumper

Okay, we understand what Entitlements are and their purpose, now lets make our dumper that takes in a path to an executable and prints out the entitlements of that executable. First, let’s understand how where Entitlements even are in the first place

First, we need to understand that the Mach-O executable format is divided into 3 parts:

To understand where entitlements are located, we only need to understand the header and load commands, specifically, Entitlements are located in a specific load command, which we will go over.

Understanding (some of) the Mach-O Format

The beginning of a Mach-O starts with the header. The header consists of basic metadata necessary for dyld to process the executable, the layout can be seen here:

struct mach_header {
	uint32_t	magic;		/* mach magic number identifier */
	cpu_type_t	cputype;	/* cpu specifier */
	cpu_subtype_t	cpusubtype;	/* machine specifier */
	uint32_t	filetype;	/* type of file */
	uint32_t	ncmds;		/* number of load commands */
	uint32_t	sizeofcmds;	/* the size of all the load commands */
	uint32_t	flags;		/* flags */
};

The header has the necessary information for us to cycle through the load commands of a Mach-O, of which one of them contains the Entitlements of the binary, being LC_CODE_SIGNATURE

After the header comes the Load Commands, which describe various metadata about the Mach-O executable, such as:

A Load Command is garaunteed to have 2 properties:

struct load_command {
	uint32_t cmd; /* type of load command */
	uint32_t cmdsize; /* total size of command in bytes */
};

Entitlements are located in the code signing Load Command, LC_CODE_SIGNATURE, meaning that we’d have to iterate over all Load Commands of the Mach-O till we find the one marked as LC_CODE_SIGNATURE (by checking for if the cmd property is equal to LC_CODE_SIGNATURE), and find the Entitlement there by going through that Load Command’s layout.

Creating the dumper

Usually, here we would write a parser for Mach-O to go over the Mach-O header and Load Commands etc etc. However, as a wise Chinese man once said:

If there’s a will, there is most probably a private API to the way. (~Sun Tzu, The Art of War)

Sun Tzu was correct. One day I happened to be scrolling past the PrivateFrameworks folder on macOS (/System/Library/PrivateFrameworks), where all private system frameworks are listed, and came across one by the name of AppSandbox.framework, which caught my eye and later dropped it into IDA to check that it had various classes, but the one that caught my eye the most was AppSandboxEntitlements, with an interface of:

// Reverse engineered from Apple's private AppSandbox.framework
@interface AppSandboxEntitlements : NSObject
+ (AppSandboxEntitlements *)entitlementsForCodeAtURL:(NSURL *)appURL error:(NSError **)error; // Constructor
- (NSDictionary *)allEntitlements;
@end

So, one would think that you could just create an instance of AppSandboxEntitlements with the entitlementsForCodeAtURL method and then use the allEntitlements method to get the entitlements, right? Right?

Well, yes! As a matter of fact, this hidden API makes it as easy as just:

// lets pick a random system application which contains some special entitlements..
// say, Safari
NSURL *url = [NSURL fileURLWithPath: @"/Applications/Safari.app"];
AppSandboxEntitlements *grabber = [AppSandboxEntitlements entitlementsForCodeAtURL: url error: nil];
NSDictionary *dict = [grabber allEntitlements];

And that’s it! Now we have the Entitlements in the form of a dictionary and can dump the Entitlements of any binary.

A demonstration project is available on Github .