mirror of
				https://github.com/HackTricks-wiki/hacktricks.git
				synced 2025-10-10 18:36:50 +00:00 
			
		
		
		
	Merge pull request #1436 from HackTricks-wiki/update_appledb_rs__a_research_support_tool_for_Apple_plat_20250925_125609
appledb_rs a research support tool for Apple platforms
This commit is contained in:
		
						commit
						8966a41427
					
				| @ -61,6 +61,7 @@ | ||||
|     - [Deofuscation vbs (cscript.exe)](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/desofuscation-vbs-cscript.exe.md) | ||||
|     - [Discord Cache Forensics](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/discord-cache-forensics.md) | ||||
|     - [Local Cloud Storage](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/local-cloud-storage.md) | ||||
|     - [Mach O Entitlements And Ipsw Indexing](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/mach-o-entitlements-and-ipsw-indexing.md) | ||||
|     - [Office file analysis](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/office-file-analysis.md) | ||||
|     - [PDF File analysis](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/pdf-file-analysis.md) | ||||
|     - [PNG tricks](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/png-tricks.md) | ||||
|  | ||||
| @ -54,4 +54,9 @@ video-and-audio-file-analysis.md | ||||
| zips-tricks.md | ||||
| {{#endref}} | ||||
| 
 | ||||
| {{#include ../../../banners/hacktricks-training.md}} | ||||
| 
 | ||||
| {{#ref}} | ||||
| mach-o-entitlements-and-ipsw-indexing.md | ||||
| {{#endref}} | ||||
| 
 | ||||
| {{#include ../../../banners/hacktricks-training.md}} | ||||
| @ -0,0 +1,221 @@ | ||||
| # Mach-O Entitlements Extraction & IPSW Indexing | ||||
| 
 | ||||
| {{#include ../../../banners/hacktricks-training.md}} | ||||
| 
 | ||||
| ## Overview | ||||
| 
 | ||||
| This page covers how to extract entitlements from Mach-O binaries programmatically by walking LC_CODE_SIGNATURE and parsing the code signing SuperBlob, and how to scale this across Apple IPSW firmwares by mounting and indexing their contents for forensic search/diff. | ||||
| 
 | ||||
| If you need a refresher on Mach-O format and code signing, see also: macOS code signing and SuperBlob internals. | ||||
| - Check macOS code signing details (SuperBlob, Code Directory, special slots): [macOS Code Signing](../../../macos-hardening/macos-security-and-privilege-escalation/macos-security-protections/macos-code-signing.md) | ||||
| - Check general Mach-O structures/load commands: [Universal binaries & Mach-O Format](../../../macos-hardening/macos-security-and-privilege-escalation/macos-files-folders-and-binaries/universal-binaries-and-mach-o-format.md) | ||||
| 
 | ||||
| 
 | ||||
| ## Entitlements in Mach-O: where they live | ||||
| 
 | ||||
| Entitlements are stored inside the code signature data referenced by the LC_CODE_SIGNATURE load command and placed in the __LINKEDIT segment. The signature is a CS_SuperBlob containing multiple blobs (code directory, requirements, entitlements, CMS, etc.). The entitlements blob is a CS_GenericBlob whose data is an Apple Binary Property List (bplist00) mapping entitlement keys to values. | ||||
| 
 | ||||
| Key structures (from xnu): | ||||
| 
 | ||||
| ```c | ||||
| /* mach-o/loader.h */ | ||||
| struct mach_header_64 { | ||||
|     uint32_t magic; | ||||
|     cpu_type_t cputype; | ||||
|     cpu_subtype_t cpusubtype; | ||||
|     uint32_t filetype; | ||||
|     uint32_t ncmds; | ||||
|     uint32_t sizeofcmds; | ||||
|     uint32_t flags; | ||||
|     uint32_t reserved; | ||||
| }; | ||||
| 
 | ||||
| struct load_command { | ||||
|     uint32_t cmd; | ||||
|     uint32_t cmdsize; | ||||
| }; | ||||
| 
 | ||||
| /* Entitlements live behind LC_CODE_SIGNATURE (cmd=0x1d) */ | ||||
| struct linkedit_data_command { | ||||
|     uint32_t cmd;        /* LC_CODE_SIGNATURE */ | ||||
|     uint32_t cmdsize;    /* sizeof(struct linkedit_data_command) */ | ||||
|     uint32_t dataoff;    /* file offset of data in __LINKEDIT */ | ||||
|     uint32_t datasize;   /* file size of data in __LINKEDIT */ | ||||
| }; | ||||
| 
 | ||||
| /* osfmk/kern/cs_blobs.h */ | ||||
| typedef struct __SC_SuperBlob { | ||||
|     uint32_t magic;   /* CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0 */ | ||||
|     uint32_t length; | ||||
|     uint32_t count; | ||||
|     CS_BlobIndex index[]; | ||||
| } CS_SuperBlob; | ||||
| 
 | ||||
| typedef struct __BlobIndex { | ||||
|     uint32_t type;    /* e.g., CSMAGIC_EMBEDDED_ENTITLEMENTS = 0xfade7171 */ | ||||
|     uint32_t offset;  /* offset of entry */ | ||||
| } CS_BlobIndex; | ||||
| 
 | ||||
| typedef struct __SC_GenericBlob { | ||||
|     uint32_t magic;   /* same as type when standalone */ | ||||
|     uint32_t length; | ||||
|     char data[];      /* Apple Binary Plist containing entitlements */ | ||||
| } CS_GenericBlob; | ||||
| ``` | ||||
| 
 | ||||
| Important constants: | ||||
| - LC_CODE_SIGNATURE cmd = 0x1d | ||||
| - CS SuperBlob magic = 0xfade0cc0 | ||||
| - Entitlements blob type (CSMAGIC_EMBEDDED_ENTITLEMENTS) = 0xfade7171 | ||||
| - DER entitlements may be present via special slot (e.g., -7), see the macOS Code Signing page for special slots and DER entitlements notes | ||||
| 
 | ||||
| Note: Multi-arch (fat) binaries contain multiple Mach-O slices. You must pick the slice for the architecture you want to inspect and then walk its load commands. | ||||
| 
 | ||||
| 
 | ||||
| ## Extraction steps (generic, lossless-enough) | ||||
| 
 | ||||
| 1) Parse Mach-O header; iterate ncmds worth of load_command records. | ||||
| 2) Locate LC_CODE_SIGNATURE; read linkedit_data_command.dataoff/datasize to map the Code Signing SuperBlob placed in __LINKEDIT. | ||||
| 3) Validate CS_SuperBlob.magic == 0xfade0cc0; iterate count entries of CS_BlobIndex. | ||||
| 4) Locate index.type == 0xfade7171 (embedded entitlements). Read the pointed CS_GenericBlob and parse its data as an Apple binary plist (bplist00) to key/value entitlements. | ||||
| 
 | ||||
| Implementation notes: | ||||
| - Code signature structures use big-endian fields; swap byte order when parsing on little-endian hosts. | ||||
| - The entitlements GenericBlob data itself is a binary plist (handled by standard plist libraries). | ||||
| - Some iOS binaries may carry DER entitlements; also some stores/slots differ across platforms/versions. Cross-check both standard and DER entitlements as needed. | ||||
| - For fat binaries, use the fat headers (FAT_MAGIC/FAT_MAGIC_64) to locate the correct slice and offset before walking Mach-O load commands. | ||||
| 
 | ||||
| 
 | ||||
| ## Minimal parsing outline (Python) | ||||
| 
 | ||||
| The following is a compact outline showing the control flow to find and decode entitlements. It intentionally omits robust bounds checks and full fat binary support for brevity. | ||||
| 
 | ||||
| ```python | ||||
| import plistlib, struct | ||||
| 
 | ||||
| LC_CODE_SIGNATURE = 0x1d | ||||
| CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0 | ||||
| CSMAGIC_EMBEDDED_ENTITLEMENTS = 0xfade7171 | ||||
| 
 | ||||
| # all code-signing integers are big-endian per cs_blobs.h | ||||
| be32 = lambda b, off: struct.unpack_from(">I", b, off)[0] | ||||
| 
 | ||||
| def parse_entitlements(macho_bytes): | ||||
|     # assume already positioned at a single-arch Mach-O slice | ||||
|     magic, = struct.unpack_from("<I", macho_bytes, 0) | ||||
|     is64 = magic in (0xfeedfacf,) | ||||
|     if is64: | ||||
|         ncmds = struct.unpack_from("<I", macho_bytes, 0x10)[0] | ||||
|         sizeofcmds = struct.unpack_from("<I", macho_bytes, 0x14)[0] | ||||
|         off = 0x20 | ||||
|     else: | ||||
|         # 32-bit not shown | ||||
|         return None | ||||
| 
 | ||||
|     code_sig_off = code_sig_size = None | ||||
|     for _ in range(ncmds): | ||||
|         cmd, cmdsize = struct.unpack_from("<II", macho_bytes, off) | ||||
|         if cmd == LC_CODE_SIGNATURE: | ||||
|             # struct linkedit_data_command is little-endian in file | ||||
|             _, _, dataoff, datasize = struct.unpack_from("<IIII", macho_bytes, off) | ||||
|             code_sig_off, code_sig_size = dataoff, datasize | ||||
|         off += cmdsize | ||||
| 
 | ||||
|     if code_sig_off is None: | ||||
|         return None | ||||
| 
 | ||||
|     blob = macho_bytes[code_sig_off: code_sig_off + code_sig_size] | ||||
|     if be32(blob, 0x0) != CSMAGIC_EMBEDDED_SIGNATURE: | ||||
|         return None | ||||
| 
 | ||||
|     count = be32(blob, 0x8) | ||||
|     # iterate BlobIndex entries (8 bytes each after 12-byte header) | ||||
|     for i in range(count): | ||||
|         idx_off = 12 + i*8 | ||||
|         btype = be32(blob, idx_off) | ||||
|         boff  = be32(blob, idx_off+4) | ||||
|         if btype == CSMAGIC_EMBEDDED_ENTITLEMENTS: | ||||
|             # GenericBlob is big-endian header followed by bplist | ||||
|             glen = be32(blob, boff+4) | ||||
|             data = blob[boff+8: boff+glen] | ||||
|             return plistlib.loads(data) | ||||
|     return None | ||||
| ``` | ||||
| 
 | ||||
| Usage tips: | ||||
| - To handle fat binaries, first read struct fat_header/fat_arch, choose the desired architecture slice, then pass the subrange to parse_entitlements. | ||||
| - On macOS you can validate results with: codesign -d --entitlements :- /path/to/binary | ||||
| 
 | ||||
| 
 | ||||
| ## Example findings | ||||
| 
 | ||||
| Privileged platform binaries often request sensitive entitlements such as: | ||||
| - com.apple.security.network.server = true | ||||
| - com.apple.rootless.storage.early_boot_mount = true | ||||
| - com.apple.private.kernel.system-override = true | ||||
| - com.apple.private.pmap.load-trust-cache = ["cryptex1.boot.os", "cryptex1.boot.app", "cryptex1.safari-downlevel"] | ||||
| 
 | ||||
| Searching these at scale across firmware images is extremely valuable for attack surface mapping and diffing across releases/devices. | ||||
| 
 | ||||
| 
 | ||||
| ## Scaling across IPSWs (mounting and indexing) | ||||
| 
 | ||||
| To enumerate executables and extract entitlements at scale without storing full images: | ||||
| 
 | ||||
| - Use the ipsw tool by @blacktop to download and mount firmware filesystems. Mounting leverages apfs-fuse, so you can traverse APFS volumes without full extraction. | ||||
| 
 | ||||
| ```bash | ||||
| # Download latest IPSW for iPhone11,2 (iPhone XS) | ||||
| ipsw download ipsw -y --device iPhone11,2 --latest | ||||
| 
 | ||||
| # Mount IPSW filesystem (uses underlying apfs-fuse) | ||||
| ipsw mount fs <IPSW_FILE> | ||||
| ``` | ||||
| 
 | ||||
| - Walk mounted volumes to locate Mach-O files (check magic and/or use file/otool), then parse entitlements and imported frameworks. | ||||
| - Persist a normalized view into a relational database to avoid linear growth across thousands of IPSWs: | ||||
|   - executables, operating_system_versions, entitlements, frameworks | ||||
|   - many-to-many: executable↔OS version, executable↔entitlement, executable↔framework | ||||
| 
 | ||||
| Example query to list all OS versions containing a given executable name: | ||||
| 
 | ||||
| ```sql | ||||
| SELECT osv.version AS "Versions" | ||||
| FROM device d | ||||
| LEFT JOIN operating_system_version osv ON osv.device_id = d.id | ||||
| LEFT JOIN executable_operating_system_version eosv ON eosv.operating_system_version_id = osv.id | ||||
| LEFT JOIN executable e ON e.id = eosv.executable_id | ||||
| WHERE e.name = "launchd"; | ||||
| ``` | ||||
| 
 | ||||
| Notes on DB portability (if you implement your own indexer): | ||||
| - Use an ORM/abstraction (e.g., SeaORM) to keep code DB-agnostic (SQLite/PostgreSQL). | ||||
| - SQLite requires AUTOINCREMENT only on an INTEGER PRIMARY KEY; if you want i64 PKs in Rust, generate entities as i32 and convert types, SQLite stores INTEGER as 8-byte signed internally. | ||||
| 
 | ||||
| 
 | ||||
| ## Open-source tooling and references for entitlement hunting | ||||
| 
 | ||||
| - Firmware mount/download: https://github.com/blacktop/ipsw | ||||
| - Entitlement databases and references: | ||||
|   - Jonathan Levin’s entitlement DB: https://newosxbook.com/ent.php | ||||
|   - entdb: https://github.com/ChiChou/entdb | ||||
| - Large-scale indexer (Rust, self-hosted Web UI + OpenAPI): https://github.com/synacktiv/appledb_rs | ||||
| - Apple headers for structures and constants: | ||||
|   - loader.h (Mach-O headers, load commands) | ||||
|   - cs_blobs.h (SuperBlob, GenericBlob, CodeDirectory) | ||||
| 
 | ||||
| For more on code signing internals (Code Directory, special slots, DER entitlements), see: [macOS Code Signing](../../../macos-hardening/macos-security-and-privilege-escalation/macos-security-protections/macos-code-signing.md) | ||||
| 
 | ||||
| 
 | ||||
| ## References | ||||
| 
 | ||||
| - [appledb_rs: a research support tool for Apple platforms](https://www.synacktiv.com/publications/appledbrs-un-outil-daide-a-la-recherche-sur-plateformes-apple.html) | ||||
| - [synacktiv/appledb_rs](https://github.com/synacktiv/appledb_rs) | ||||
| - [blacktop/ipsw](https://github.com/blacktop/ipsw) | ||||
| - [Jonathan Levin’s entitlement DB](https://newosxbook.com/ent.php) | ||||
| - [ChiChou/entdb](https://github.com/ChiChou/entdb) | ||||
| - [XNU cs_blobs.h](https://github.com/apple-oss-distributions/xnu/blob/main/osfmk/kern/cs_blobs.h) | ||||
| - [XNU mach-o/loader.h](https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/loader.h) | ||||
| - [SQLite Datatypes](https://sqlite.org/datatype3.html) | ||||
| 
 | ||||
| {{#include ../../../banners/hacktricks-training.md}} | ||||
| @ -298,6 +298,11 @@ Load command 13 | ||||
| 
 | ||||
| ### **`LC_CODE_SIGNATURE`** | ||||
| 
 | ||||
| {{#ref}} | ||||
| ../../../generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/mach-o-entitlements-and-ipsw-indexing.md | ||||
| {{#endref}} | ||||
| 
 | ||||
| 
 | ||||
| Contains information about the **code signature of the Macho-O file**. It only contains an **offset** that **points** to the **signature blob**. This is typically at the very end of the file.\ | ||||
| However, you can find some information about this section in [**this blog post**](https://davedelong.com/blog/2018/01/10/reading-your-own-entitlements/) and this [**gists**](https://gist.github.com/carlospolop/ef26f8eb9fafd4bc22e69e1a32b81da4). | ||||
| 
 | ||||
|  | ||||
| @ -4,6 +4,11 @@ | ||||
| 
 | ||||
| ## Basic Information | ||||
| 
 | ||||
| {{#ref}} | ||||
| ../../../generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/mach-o-entitlements-and-ipsw-indexing.md | ||||
| {{#endref}} | ||||
| 
 | ||||
| 
 | ||||
| Mach-o binaries contains a load command called **`LC_CODE_SIGNATURE`** that indicates the **offset** and **size** of the signatures inside the binary. Actually, using the GUI tool MachOView, it's possible to find at the end of the binary a section called **Code Signature** with this information: | ||||
| 
 | ||||
| <figure><img src="../../../images/image (1) (1) (1) (1).png" alt="" width="431"><figcaption></figcaption></figure> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user