Mach IPC (Inter-Process Communication)
Mach IPC là backbone của Darwin. Mọi communication giữa processes, giữa userspace và kernel, và giữa kernel subsystems đều qua Mach messages và ports. Đây là thành phần quan trọng nhất cho kernel exploitation.
Tại Sao Đây Là Thành Phần Quan Trọng Nhất
- Mach messages cấp phát trên kernel heap → dùng cho heap spraying/grooming
- Port objects là kernel objects → target cho type confusion, UAF
- OOL (out-of-line) descriptors → controlled kernel allocations với data tùy ý
- Task ports → ai có send right tới kernel task = full kernel r/w
- MIG-generated handlers → large attack surface, historically buggy
Mach Ports
Khái Niệm
Mach port = kernel-managed endpoint cho message queue. Conceptually tương tự file descriptor nhưng mạnh hơn.
// Port trong kernel
struct ipc_port {
struct ipc_object ip_object; // Header (type, refs, lock)
struct ipc_mqueue ip_messages; // Message queue
natural_t ip_mscount; // Make-send count
natural_t ip_srights; // Send rights count
mach_port_rights_t ip_sorights; // Send-once rights count
union {
struct ipc_kmsg *imq_messages; // Message list
struct task *task; // Cho IKOT_TASK ports
struct thread *thread; // Cho IKOT_THREAD ports
struct semaphore *semaphore; // Cho IKOT_SEMAPHORE
IOUserClient *iokit_object; // Cho IKOT_IOKIT_CONNECT
} kdata;
struct ipc_port *ip_nsrequest; // No-senders notification
struct ipc_port *ip_pdrequest; // Port-destroyed notification
// ...
};
Port Rights
| Right | Ý nghĩa | Giới hạn |
|---|---|---|
| Receive right | Cho phép nhận messages từ port | Chỉ 1 holder trong toàn system |
| Send right | Cho phép gửi messages | Nhiều holders, reference counted |
| Send-once right | Gửi đúng 1 message rồi tự hủy | Dùng cho reply ports |
Process A ─── send right ───→ Port ←── receive right ─── Process B
Process C ─── send right ──╱ (chỉ B nhận messages)
Capability model:
- Có right = có capability
- Không có mechanism nào khác để kiểm soát access (không phải ACL)
- Transfer right = transfer capability (qua Mach messages)
Port Lifecycle
// 1. Allocate port (get receive right)
mach_port_t port;
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
// 2. Insert send right
mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
// 3. Send message
mach_msg_header_t msg = {
.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0),
.msgh_size = sizeof(msg),
.msgh_remote_port = port, // destination
.msgh_local_port = MACH_PORT_NULL,
.msgh_id = 0x1234
};
mach_msg(&msg, MACH_SEND_MSG, sizeof(msg), 0, 0, 0, 0);
// 4. Receive message
mach_msg(&msg, MACH_RCV_MSG, 0, sizeof(msg), port, 0, 0);
// 5. Destroy port
mach_port_destroy(mach_task_self(), port);
Mach Messages
Message Structure
// Simple message (no ports or OOL data)
struct {
mach_msg_header_t header; // 24 bytes
// optional: inline data
char data[256];
} simple_msg;
// Complex message (contains ports or OOL data)
struct {
mach_msg_header_t header;
mach_msg_body_t body; // descriptor count
// Descriptors:
mach_msg_port_descriptor_t port_desc; // Send port right
mach_msg_ool_descriptor_t ool_desc; // OOL memory pointer
// Trailing data
} complex_msg;
Header
typedef struct {
mach_msg_bits_t msgh_bits; // Type bits (complex, send/receive types)
mach_msg_size_t msgh_size; // Total message size
mach_port_t msgh_remote_port; // Destination port
mach_port_t msgh_local_port; // Reply port (optional)
mach_port_name_t msgh_voucher_port;// Voucher port
mach_msg_id_t msgh_id; // Message ID (for MIG dispatch)
} mach_msg_header_t;
msgh_bits Encoding
// Remote port type (destination)
MACH_MSG_TYPE_COPY_SEND // Copy send right
MACH_MSG_TYPE_MOVE_SEND // Move send right (sender loses it)
MACH_MSG_TYPE_MAKE_SEND // Make send from receive right
MACH_MSG_TYPE_MOVE_SEND_ONCE // Move send-once right
// Complex bit
MACH_MSGH_BITS_COMPLEX // Message contains descriptors
// Helper macro
MACH_MSGH_BITS(remote, local) // Combine remote + local types
Out-of-Line (OOL) Data — Exploitation Primitive
OOL Memory Descriptor
Khi message data quá lớn, dùng OOL: kernel allocate kernel memory, copy data vào.
mach_msg_ool_descriptor_t ool = {
.address = user_buffer, // Userspace buffer (source)
.count = size, // Size in bytes
.deallocate = FALSE, // Don't free source after copy
.copy = MACH_MSG_VIRTUAL_COPY, // Copy strategy
.type = MACH_MSG_OOL_DESCRIPTOR
};
Tại sao quan trọng cho exploitation:
1. Attacker gửi OOL message với controlled data
2. Kernel allocate buffer trên kernel heap (kalloc zone)
3. Kernel copy attacker's data vào buffer
4. Message enqueued → buffer stays allocated
5. Attacker có controlled data trên kernel heap!
Dùng cho:
- Heap spraying: gửi nhiều OOL messages cùng size → fill zone
- Heap grooming: spray → free selected → allocate target object
- Fake object creation: OOL data = fake kernel struct
OOL Ports Descriptor
Gửi array of port rights:
mach_msg_ool_ports_descriptor_t ports_desc = {
.address = port_array, // Array of mach_port_t
.count = num_ports, // Number of ports
.deallocate = FALSE,
.disposition = MACH_MSG_TYPE_COPY_SEND,
.type = MACH_MSG_OOL_PORTS_DESCRIPTOR
};
Exploitation: Mỗi port right trong OOL → kernel allocate ipc_port pointer entry. Controlling port array content = controlling kernel heap layout.
Task Ports — The Ultimate Goal
Task Port Concept
struct task {
// ...
struct ipc_port *itk_self; // Task's self port
struct ipc_port *itk_nself; // Task's notify port
struct vm_map *map; // Address space
// ...
};
task_self()→ send right tới own task porttask_for_pid(target_task, pid, &port)→ get send right tới another task
tfp0 (Task-For-Pid 0 = Kernel Task Port)
mach_port_t kernel_task_port;
task_for_pid(mach_task_self(), 0, &kernel_task_port);
// Nếu thành công: kernel_task_port = send right tới kernel task
// → Dùng mach_vm_read/mach_vm_write đọc/ghi kernel memory
Trên iOS hiện đại:
task_for_pid(0)bị chặn bởi entitlement check + sandbox- Phải tạo fake kernel task port sau khi có kernel r/w primitive
- Hoặc dùng
mach_vm_*thông qua alternative primitives
Fake Task Port Attack (Classic)
1. Spray IOSurface objects trên kernel heap
2. Trigger vulnerability → kernel write primitive
3. Craft fake ipc_port structure trong controlled memory
4. Overwrite port pointer → point to fake port
5. Fake port's kdata.task → pointer to fake task struct
6. Fake task's map → pointer to kernel_map
7. mach_vm_read(fake_port, kernel_addr, ...) → reads kernel memory!
MIG (Mach Interface Generator)
MIG generates C code cho Mach IPC interfaces:
.defs file → MIG compiler → client stubs + server stubs
Ví dụ MIG Definition
routine task_threads(
target_task : task_inspect_t;
out act_list : thread_act_array_t;
out act_listCnt : mach_msg_type_number_t
);
MIG generates:
- Client stub: serialize arguments → mach_msg → deserialize reply
- Server stub: deserialize message → call implementation → serialize reply
Attack surface:
- MIG handlers parse complex message structures
- Historically nhiều bugs trong size validation, port handling
ipc_kmsg_copyin()handles complex descriptor parsing → bugs here = kernel exploitation
Exploitation Patterns Summary
| Pattern | Cơ chế | Ví dụ |
|---|---|---|
| Heap spray | OOL messages cùng size fill kalloc zone | Fill zone trước khi trigger vuln |
| Heap grooming | Spray → free selected → allocate target | Control adjacency on heap |
| Port UAF | Free port nhưng giữ reference | Overwrite freed port memory |
| Fake port | Craft fake ipc_port struct | Redirect port operations |
| Port replacement | Replace kernel object via dangling port | Type confusion |
| Message race | Race between send and receive | TOCTOU on message data |
Tài Nguyên
- Apple Developer — Mach Overview
- Notes on Mach IPC — ulexec
- XNU IPC — Mach Messages — dmcyk
- Damien Deville — IPC on iOS with Mach Messages
- Ali Pourhadi — IPC Mach Message
- XNU source:
osfmk/ipc/,osfmk/kern/ipc_tt.c
Bài Tập
- Viết Mach IPC client/server: Tạo 2 processes communicate qua Mach messages trên macOS
- Spray kernel heap: Gửi 1000 OOL messages kích thước 256 bytes, observe kernel memory usage
- Enumerate ports: Viết tool list mọi Mach ports của target process (dùng
mach_port_names) - Send complex message: Gửi message chứa port right + OOL data, nhận ở process khác
- Trace MIG call: Identify MIG subsystem/routine ID cho
task_threads, trace kernel handling path