Mach-O 权限提取与 IPSW 索引
Reading time: 9 minutes
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
概述
本页介绍如何通过遍历 LC_CODE_SIGNATURE 并解析 code signing SuperBlob,编程式地从 Mach-O 二进制中提取 entitlements,以及如何通过挂载并索引 Apple IPSW 固件的内容,以便进行取证搜索/差异比较,从而实现规模化处理。
如果你需要复习 Mach-O 格式和 code signing,可以参考:macOS code signing 和 SuperBlob 内部结构。
- Check macOS code signing details (SuperBlob, Code Directory, special slots): macOS Code Signing
- Check general Mach-O structures/load commands: Universal binaries & Mach-O Format
Entitlements in Mach-O: where they live
Entitlements 存储在由 LC_CODE_SIGNATURE 加载命令引用的 code signature 数据中,并放置在 __LINKEDIT 段。该签名是一个 CS_SuperBlob,包含多个 blob(code directory、requirements、entitlements、CMS 等)。entitlements blob 是一个 CS_GenericBlob,其数据是一个 Apple Binary Property List (bplist00),将 entitlement 键映射到值。
关键结构(来自 xnu):
/* 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;
重要常量:
- 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
注意:多架构(fat)二进制包含多个 Mach-O slices。你必须选择要检查的架构对应的 slice,然后遍历其 load commands。
Extraction steps (generic, lossless-enough)
- Parse Mach-O header; iterate ncmds worth of load_command records.
- Locate LC_CODE_SIGNATURE; read linkedit_data_command.dataoff/datasize to map the Code Signing SuperBlob placed in __LINKEDIT.
- Validate CS_SuperBlob.magic == 0xfade0cc0; iterate count entries of CS_BlobIndex.
- 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.
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:
- 要处理 fat binaries,首先读取 struct fat_header/fat_arch,选择所需的架构切片,然后将子范围传递给 parse_entitlements。
- 在 macOS 上可以使用以下命令验证结果:codesign -d --entitlements :- /path/to/binary
示例发现
具有特权的平台二进制通常会请求如下敏感 entitlements:
- 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"]
在大规模固件镜像中搜索这些条目对于 attack surface mapping 和跨发布/设备的 diffing 非常有价值。
Scaling across IPSWs (mounting and indexing)
要在不存储完整镜像的情况下,对可执行文件进行枚举并大规模提取 entitlements:
- 使用 @blacktop 的 ipsw 工具下载并挂载固件文件系统。挂载使用 apfs-fuse,因此可以在不完全提取的情况下遍历 APFS 卷。
# 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>
- 遍历已挂载卷以定位 Mach-O 文件(检查 magic 和/或使用 file/otool),然后解析 entitlements 和导入的 frameworks。
- 将标准化视图持久化到关系型数据库,以避免在成千上万个 IPSWs 中线性增长:
- executables, operating_system_versions, entitlements, frameworks
- many-to-many: executable↔OS version, executable↔entitlement, executable↔framework
示例查询:列出包含给定 executable 名称的所有 OS versions:
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";
数据库可移植性说明(如果你实现自己的索引器):
- 使用 ORM/抽象层(例如 SeaORM)以保持代码与数据库无关(SQLite/PostgreSQL)。
- SQLite 仅在 INTEGER PRIMARY KEY 上需要 AUTOINCREMENT;如果在 Rust 中希望使用 i64 作为主键,可生成实体为 i32 然后转换类型,SQLite 在内部将 INTEGER 以 8 字节有符号整数存储。
Open-source tooling and references for entitlement hunting
- 固件 挂载/下载: https://github.com/blacktop/ipsw
- Entitlement 数据库与参考:
- Jonathan Levin’s entitlement DB: https://newosxbook.com/ent.php
- entdb: https://github.com/ChiChou/entdb
- 大规模索引器(Rust,self-hosted Web UI + OpenAPI): https://github.com/synacktiv/appledb_rs
- Apple 头文件(用于结构体和常量):
- loader.h (Mach-O headers, load commands)
- cs_blobs.h (SuperBlob, GenericBlob, CodeDirectory)
有关代码签名内部机制(Code Directory、special slots、DER entitlements)的更多信息,请参见: macOS Code Signing
References
- appledb_rs: a research support tool for Apple platforms
- synacktiv/appledb_rs
- blacktop/ipsw
- Jonathan Levin’s entitlement DB
- ChiChou/entdb
- XNU cs_blobs.h
- XNU mach-o/loader.h
- SQLite Datatypes
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。