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

  1. Mach messages cấp phát trên kernel heap → dùng cho heap spraying/grooming
  2. Port objects là kernel objects → target cho type confusion, UAF
  3. OOL (out-of-line) descriptors → controlled kernel allocations với data tùy ý
  4. Task ports → ai có send right tới kernel task = full kernel r/w
  5. 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;
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 port
  • task_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


Bài Tập

  1. Viết Mach IPC client/server: Tạo 2 processes communicate qua Mach messages trên macOS
  2. Spray kernel heap: Gửi 1000 OOL messages kích thước 256 bytes, observe kernel memory usage
  3. Enumerate ports: Viết tool list mọi Mach ports của target process (dùng mach_port_names)
  4. Send complex message: Gửi message chứa port right + OOL data, nhận ở process khác
  5. Trace MIG call: Identify MIG subsystem/routine ID cho task_threads, trace kernel handling path