2022 APFS Advent Challenge Day 14 - Sealed Volumes
With the release of macOS 11, Apple added a security feature to APFS called sealed volumes. Sealed volumes can be used to cryptographically verify the contents of the read-only system volume as an additional layer of protection against rootkits and other malware that may attempt to replace critical components of the operating system. Sealed volumes have subtle differences from some of the properties of file systems that we’ve discussed so far.
Identifying a Sealed Volume
Sealed volumes can be identified by checking for the APFS_INCOMPAT_SEALED_VOLUME
flag in the apfs_incompatible_features
field of their Volume Superblock. In addition, the apfs_integrity_meta_oid
and apfs_fext_tree_oid
fields must have non-zero values.
An Integrity Metadata Object stores information about the sealed volume. This is a virtual object that is owned by the volume’s Object Map and whose object identifier can be found in the apfs_integrity_meta_oid
field of the Volume Superblock. On disk, it is stored as an integrity_meta_phys_t
structure.
typedef struct integrity_meta_phys {
obj_phys_t im_o; // 0x00
uint32_t im_version; // 0x20
uint32_t im_flags; // 0x24
apfs_hash_type_t im_hash_type; // 0x28
uint32_t im_root_hash_offset; // 0x2C
xid_t im_broken_xid; // 0x30
uint64_t im_reserved[9]; // 0x38
} integrity_meta_phys_t; // 0x80
im_o
: The object’s headerim_version
: The version of the data structureim_flags
: The configuration flagsim_hash_type
: The hash algorithm that is usedim_root_hash_offset
: The offset (in bytes) of the root hash relative to the start of the objectim_broken_xid
: The identifier of the transaction that unsealed the volumeim_reserved
: reserved (only in version 2 or above)
Integrity Metadata Flags
Name | Value | Description |
---|---|---|
APFS_SEAL_BROKEN | 0x00000001 | The volume was modified after being sealed, breaking its seal |
Hash Types
Name | Value | Description |
---|---|---|
APFS_HASH_INVALID | 0 | An invalid hash algorithm |
APFS_HASH_SHA256 | 0x1 | The SHA-256 variant of Secure Hash Algorithm 2 |
APFS_HASH_SHA512_256 | 0x2 | The SHA-512/256 variant of Secure Hash Algorithm 2 |
APFS_HASH_SHA384 | 0x3 | The SHA-384 variant of Secure Hash Algorithm 2 |
APFS_HASH_SHA512 | 0x4 | The SHA-512 variant of Secure Hash Algorithm 2 |
File System Tree
Sealed Volumes can ensure integrity by hashing the contents of their File System Trees. This hashing necessitates some slight differences to the B-Tree. These modified B-Trees can be identified by the BTREE_HASHED
and BTREE_NOHEADER
flags being set in their B-Tree Info.
In standard B-Trees, non-leaf nodes store the object identifier of their children in the value-half of their entries. “Hashed” B-Trees instead use btn_index_node_val_t
structures for this purpose, which store the cryptographic hash of the child node’s contents along with its identifier. Hashed nodes are also stored as headerless objects, with their 32-byte header being zeroed out.
#define BTREE_NODE_HASH_SIZE_MAX 64
typedef struct btn_index_node_val {
oid_t binv_child_oid; // 0x00
uint8_t binv_child_hash[BTREE_NODE_HASH_SIZE_MAX]; // 0x08
} btn_index_node_val_t; // 0x48
binv_child_oid
: The object identifier of the child nodebinv_child_hash
: The hash of the child node
Data Stream Extents
As we discussed yesterday, Data Streams store their extents as file system records in the File System Tree. Sealed Volumes store extents in a separate File Extent Tree, whose virtual object identifier is stored in the apfs_fext_tree_oid
of the Volume Superblock.
The key-half of the File Extent Tree entries are fext_tree_key_t
structures and are sorted first by private_id
and then by logical_addr
.
typedef struct fext_tree_key {
uint64_t private_id; // 0x00
uint64_t logical_addr; // 0x08
} fext_tree_key_t; // 0x10
private_id
: The object identifier of the filelogical_addr
: The offset (in bytes) within the file’s data for the data stored in this extent
The value-half takes the form of a fext_tree_val_t
structure. Its fields are interpreted in the same way as the j_file_extent_val
fields. There is no crypto_id
because sealed system volumes are never encrypted.
typedef struct fext_tree_val {
uint64_t len_and_flags; // 0x00
uint64_t phys_block_num; // 0x08
} fext_tree_val_t; // 0x10
len_and_flags
: A bit field that contains the length of the extent and its flagsphys_block_num
: The starting physical block address of the extent
Conclusion
Sealed Volumes in APFS provide an extra layer of security by allowing macOS to verify its system volume cryptographically. This post described some of the subtle differences in analyzing sealed volumes.
Find an issue or technical inaccuracy in this post? Please file an issue so that it may be corrected.