Kernel Read/Write Primitives
kread/kwrite = the ability to read/write arbitrary kernel virtual addresses. This is the primary goal – having kread/kwrite = controlling the kernel.
Why kread/kwrite Is Sufficient
With kread/kwrite you can:
- Read/write process credentials – get root
- Modify the sandbox slot – unsandbox
- Inject trust cache – bypass code signing
- Read kernel objects – leak additional information
- Modify any kernel data structure
Techniques
1. IOSurface-Based kread/kwrite
Most common in modern exploits.
Concept:
IOSurface objects contain dictionary properties (key-value pairs)
Properties stored as serialized data in the kernel heap
set_value/get_value methods read/write property data
Exploit:
1. Create IOSurface
2. Trigger vulnerability → corrupt IOSurface internal data pointer
3. iosurface_kread: set corrupted pointer → target address
→ get_value reads from target address → returns data to userspace
4. iosurface_kwrite: set corrupted pointer → target address
→ set_value writes attacker data to target address
Functions:
iosurface_kread32(addr) → read 32 bits at kernel addr
iosurface_kread64(addr) → read 64 bits at kernel addr
iosurface_kwrite32(addr, val) → write 32 bits
iosurface_kwrite64(addr, val) → write 64 bits
2. Pipe-Based kread/kwrite
Concept:
pipe() creates a kernel pipe_buffer struct
read(pipe_fd) reads from pipe_buffer
write(pipe_fd) writes to pipe_buffer
Exploit:
1. Create pipe pair
2. Corrupt pipe_buffer struct:
- Change buffer pointer → kernel address
- Change buffer size → large
3. read(pipe_fd) → kernel copies from corrupted address → kread
4. write(pipe_fd) → kernel copies to corrupted address → kwrite
3. Fake Task Port (Classic, pre-iOS 14)
Concept:
task port + mach_vm_read/write = access task's address space
If task port points to kernel_task → access kernel memory
Exploit:
1. Craft a fake ipc_port structure in controlled memory
2. Set kobject type = IKOT_TASK
3. Set kdata.task → fake task structure
4. Fake task's map → kernel_map (or kernel_pmap)
5. Send/receive to get port name for fake port
6. mach_vm_read(fake_port, addr, size) → kread
7. mach_vm_write(fake_port, addr, data, size) → kwrite
Mitigated by:
- PAC on port pointers (iOS 12+)
- zone_require (iOS 14+)
- Lockdown of task port operations
4. kfd Methods
The kfd project implements multiple kread/kwrite methods:
| Method | Mechanism |
|---|---|
kread_kqueue_workloop_ctl |
Abuse kqueue workloop for kernel reads |
kread_sem_open |
Abuse POSIX semaphore for kernel reads |
kwrite_dup |
Abuse dup() file descriptor for kernel writes |
kwrite_sem_open |
Abuse POSIX semaphore for kernel writes |
5. Physical R/W → Virtual R/W
If you have physrw (physical read/write):
1. Read TTBR1_EL1 (kernel page table base) from a known location
2. Walk kernel page tables manually
3. Translate kernel virtual address → physical address
4. Read/write the physical address
→ Equivalent to kread/kwrite but through the physical layer
Stability & Reliability
| Method | Reliability | iOS range | Notes |
|---|---|---|---|
| IOSurface | High | iOS 11+ | Standard in modern exploits |
| Pipe | Medium | iOS 10+ | Simple but has limitations |
| Fake task port | High (when working) | iOS ≤13 | Killed by PAC + zone_require |
| kfd methods | High | iOS 15-16 | Well-tested |
| Physical r/w | High | Depends | Requires PUAF primitive |
Resources
- kfd Project – reference implementation
- Alfie CG – Kernel Exploit Guide – IOSurface kread/kwrite
- Secfault – Kernel Exploit from Scratch