Kernel exploit analysis
Static reverse engineering of the kernel exploitation stage - a 2MB ARM64 DYLIB with 649 functions targeting IOSurfaceRoot (CVE-2023-41974). Recovered from a live iPhone X by researcher matteyeux via lldb memory dump of the powerd daemon.
NadSec Research
Kernel exploit binary - recovered from memory.
Binary size
2.00 MB
ARM64 DYLIB · Mach-O 64-bit
Functions
649
9 exceed 2KB · median 180B
Imported symbols
265
IOKit · Mach/VM · CoreFoundation
Target
IOSurfaceRoot
iPhone X · iOS 16.6 · A11
GitHub Repos
Coruna Blog Post
Full exploit chain overview
Download Coruna Dump
Full recovered module archive (.zip)
dump.bin - The kernel exploitation stage of the CORUNA iOS attack chain
Mach-O 64-bit ARM64 DYLIB | 2,097,152 bytes (2.00 MB)
| Field | Value |
|---|---|
| Analyst | @Nadsec11 / nadsec.online |
| Binary Source | matteyeux/coruna - kernel_exploit/dump.bin |
| Dump Method | lldb memory dump from powerd daemon process |
| Target Device | iPhone X (A11 Bionic / T8015) |
| Target OS | iOS 16.6 |
| File SHA-256 | 3b52e3b489948ae491a44faf24a9634e4c959408b321b9c36c367324874a05dc |
| File SHA-1 | fee91ff66deebea8708e6453f527833c95b67cd4 |
| File MD5 | ae3885437016750cb6b9367402fa3ac6 |
| Related CVE | CVE-2023-41974 (added to CISA KEV 2025-01-29) |
dump.bin is the kernel exploitation stage of the CORUNA attack chain - a sophisticated multi-stage iOS exploit delivered through compromised websites via the b27.icu infrastructure.
The binary was recovered by researcher matteyeux, who identified the powerd system daemon behaving anomalously on an iPhone X running iOS 16.6. Using lldb, matteyeux dumped the injected DYLIB from the running process's memory, capturing the exploit mid-execution. This is a runtime memory dump, not an on-disk binary - which explains certain artifacts such as resolved GOT entries containing live kernel/framework addresses and CFString ISA pointers referencing runtime class objects.
The kernel exploit is not embedded in the browser-side JavaScript modules. It is fetched at runtime through the following chain:
yAerzw, KRfmo6, Fq2t1Q, YGPUu7) achieve arbitrary read/write in the WebKit rendererfinal_payload_A/B) deployeddump.bin from the serverpowerd daemon (a long-lived system process with elevated privileges)_driver function executes the kernel exploit| Finding | Detail |
|---|---|
| Single export | _driver at offset 0x7514 - 29,972 bytes, the main exploit entry point |
| 265 imported symbols | Spanning IOKit, Mach/VM, CoreFoundation, CommonCrypto, ObjC runtime, and libSystem |
| Primary attack surface | IOSurfaceRoot - IOKit user client for GPU surface management |
| Privilege escalation | Disk image entitlements + IOServiceSetAuthorizationID for kernel object manipulation |
| Sandbox escape | References to com.apple.security.sandbox, com.apple.driver.AppleMobileFileIntegrity (AMFI) |
| Filesystem remount | APFS snapshot manipulation via /dev/disk0s1s1, mount_apfs, newfs_hfs |
| Kernel memory ops | Full suite: mach_vm_read_overwrite, mach_vm_write, mach_vm_wire, vm_remap |
| 649 functions | Median size 180 bytes; 9 functions exceed 2KB indicating complex logic |
| Corellium awareness | Anti-analysis check for /usr/libexec/corelliumd and CORELLIUM string |
| SoC targeting | Strings for T8020 (A12) and T8120 (A16) - multi-generation support |
| Integrity hashing | CommonCrypto SHA-1, SHA-256, and SHA-384 imported - likely for code signing or KTRR bypass verification |
Magic: 0xFEEDFACF (MH_MAGIC_64)
CPU Type: ARM64
File Type: DYLIB (6)
Load Commands: 21 (1936 bytes)
Flags: MH_NOUNDEFS | MH_DYLDLINK | MH_TWOLEVEL | MH_NO_REEXPORTED_DYLIBS
File Size: 2,097,152 bytes (2.00 MB)
The binary is a 64-bit ARM64 dynamic library - the standard format for code injection into a running process via dlopen or direct memory mapping. The MH_TWOLEVEL flag indicates two-level namespace bindings (symbols are bound per-library, not flat), consistent with modern iOS toolchain output.
| Field | Value |
|---|---|
| Platform | iOS |
| Minimum OS | 14.0.0 |
| SDK | 14.0.0 |
| Linker | LD 820.1.0 |
| UUID | 018f0aea-e228-3440-ba05-14d21d036ce1 |
The minimum deployment target of iOS 14.0 with SDK 14.0 indicates this exploit was compiled to support devices as far back as iOS 14, despite being deployed against iOS 16.6. This broad compatibility window suggests the exploit was maintained across multiple iOS generations.
SoC Discrepancy Note: The dump source is an iPhone X (A11 Bionic / T8015), yet the binary's C-strings only contain offset table identifiers for
T8020(A12 Bionic) andT8120(A16 Bionic) - not T8015. The exploit was running successfully on a device whose SoC is absent from its own identifier strings. This implies either: (a) a default/fallback code path handles unlisted SoCs, (b) additional SoC identifiers are encoded in the__constdata section (not readable as C-strings), or (c) the A11-specific offsets are hardcoded without a string-based lookup. The exploit clearly works on A11 - matteyeux dumped it mid-execution - but the offset selection mechanism for unlisted SoCs cannot be determined from static analysis alone.
| Library | Version | Purpose |
|---|---|---|
/usr/lib/libc++.1.dylib |
1300.36.0 | C++ standard library |
Foundation.framework |
1953.255.0 | ObjC foundation classes |
CoreFoundation.framework |
1953.255.0 | Core CF types (dictionaries, data, strings) |
IOKit.framework |
275.0.0 | Hardware/driver interaction - primary attack surface |
/usr/lib/libSystem.B.dylib |
1319.0.0 | System calls, threading, memory |
The combination of IOKit + CoreFoundation + libSystem is the classic fingerprint of an iOS kernel exploit - IOKit provides the attack surface (user clients to kernel drivers), CoreFoundation handles the serialized data structures needed to communicate with IOKit, and libSystem provides the Mach primitives for kernel memory manipulation.
Address Range Segment Size Prot Contents
──────────────────────────────── ──────────────── ───────── ────── ─────────────────────
0x000000 - 0x044000 __TEXT 278,528 r-x Code + read-only data
0x044000 - 0x048000 __DATA_CONST 16,384 rw- GOT, constants, CFStrings
0x048000 - 0x04C000 __DATA 16,384 rw- BSS (zero-initialized globals)
0x04C000 - 0x058000 __LINKEDIT 49,152 r-- Symbol tables, string tables
Total mapped size: 352 KB (0x58000) - compact for a kernel exploit of this sophistication.
| Segment | Section | Address | Size | Purpose |
|---|---|---|---|---|
__TEXT |
__text |
0x7514 | 237,176 | Executable code - 587 of 649 functions (remaining 62 in stubs, __DATA_CONST, __DATA) |
__TEXT |
__stubs |
0x4138C | 2,928 | Lazy symbol stubs (244 × 12 bytes) |
__TEXT |
__cstring |
0x41EFC | 3,460 | C string literals (153 strings) |
__TEXT |
__const |
0x42C80 | 2,892 | Read-only constants |
__TEXT |
__objc_classname |
0x437CC | 1 | Minimal ObjC metadata |
__TEXT |
__unwind_info |
0x437D0 | 2,028 | Exception unwind tables |
__TEXT |
__eh_frame |
0x43FC0 | 52 | DWARF exception handling frame |
__DATA_CONST |
__got |
0x44000 | 2,112 | 264 GOT entries (resolved at runtime) |
__DATA_CONST |
__const |
0x44840 | 232 | Constant data |
__DATA_CONST |
__cfstring |
0x44928 | 128 | 4 CFString objects |
__DATA_CONST |
__objc_imageinfo |
0x449A8 | 8 | ObjC image metadata |
__DATA |
__common |
0x48000 | 256 | Uninitialized global variables |
Because this binary was dumped from a running process, the LINKEDIT segment has a slide between its on-disk file offset and its in-memory virtual address:
LINKEDIT vmaddr = 0x4C000
LINKEDIT fileoff = 0x48000
Slide = 0x4000
All symbol table offsets, string table offsets, and indirect symbol table offsets in the raw dump must be adjusted by +0x4000 to read correctly. This slide was applied during our parsing of Steps 1-8.
__DATA segment has filesize=0: The __common section (BSS) is zero-initialized at load time and was not written to disk - standard for uninitialized globals, but means any global state the exploit used at runtime is lost in the dump.__stub_helper section: Despite having 244 stubs, there is no lazy binding helper. All symbols were resolved eagerly via the GOT (non-lazy symbol pointers) - this is consistent with dyld having fully resolved all bindings before the dump was taken.__cfstring entries contain ISA pointer 0x1EE30AF00 and string pointers like 0x102A724D8 - these are live runtime addresses from the process's memory space, confirming this is a memory dump rather than an on-disk binary.__objc_imageinfo flags = 0x40 (IS_SIMULATED): This flag is unusual for a real device dump. It may indicate the exploit was compiled with simulator-compatible headers, or it may be a deliberate obfuscation choice.The binary exports a single function and imports 265 symbols from system frameworks. This section categorizes every import by subsystem to map the exploit's capabilities.
_driver offset: 0x7514 size: 29,972 bytes [EXTERNAL]
The sole export is _driver - a 29,972-byte function that serves as the kernel exploit's entry point. The name is deliberately generic. At nearly 30KB of ARM64 machine code, this single function contains the core exploit logic including IOSurface interaction, kernel memory corruption, and post-exploitation. Its size alone (the largest of 649 functions by a factor of ~2.6x) marks it as the monolithic exploit driver.
| Subsystem | Count | Purpose in Exploit |
|---|---|---|
| Mach/VM | 82 | Kernel memory read/write/remap, Mach ports, threading |
| CoreFoundation | 41 | Dictionary/data/string manipulation for IOKit serialization |
| IOKit/Kernel | 30 | Driver interaction - IOSurface, IOConnect, IORegistry |
| FileSystem | 30 | Mount/unmount, file I/O, dlopen/dlsym |
| String/Data | 18 | memcpy, strcmp, snprintf - data manipulation |
| Process/Thread | 12 | posix_spawn, getpid, seteuid - process management |
| Security/Crypto | 10 | SHA-1, SHA-256, SHA-384, arc4random |
| Memory | 5 | malloc, free, calloc, mmap, realloc |
| libSystem | 4 | sysctl, dlsym, usleep |
| Networking | 3 | socket, connect, setsockopt |
| Other | 30 | Sandbox checks, processor info, NECP, misc |
| Total | 265 |
This is the largest category and reveals the exploit's kernel interaction model:
Kernel Memory Manipulation:
mach_vm_read_overwrite / mach_vm_write - arbitrary kernel read/write once exploit achieves kernel task portmach_vm_allocate / mach_vm_deallocate - kernel heap shapingmach_vm_wire - wire pages to prevent paging (keep exploit data resident)vm_remap - remap kernel memory into userspace (alternative read primitive)vm_protect - modify page protectionsvm_copy - intra-address-space copymach_vm_machine_attribute - cache attribute manipulation (ARM cache coherency)Mach Port Manipulation:
mach_port_allocate / mach_port_deallocate / mach_port_destroy - port lifecyclemach_port_insert_right - insert send rights (used in port replacement attacks)mach_port_mod_refs - reference count manipulationmach_port_request_notification - dead-name notifications (trigger timing)mach_port_set_attributes / mach_port_get_attributes - port attribute manipulationmach_port_space_info - enumerate port space (reconnaissance)mach_ports_register - register port set with taskTask/Thread Control:
task_get_special_port / task_set_special_port - critical - used to replace task ports (e.g., set kernel task port)task_threads / task_suspend2 / task_resume2 - thread enumeration and controltask_info - query task metadatatask_map_corpse_info_64 - map crash info (likely used for info leak)thread_create / thread_set_state / thread_get_state - direct thread manipulationthread_set_exception_ports - exception port takeover (common exploit primitive)thread_suspend / thread_resume / thread_terminate - thread lifecycleHost Ports:
host_get_special_port - retrieve host-level portshost_get_io_master - get IO master port (alternative to kIOMasterPortDefault)host_create_mach_voucher - Mach voucher creation (used in various exploits for type confusion)host_security_set_task_token - highly privileged - set security token on a taskhost_kernel_version / host_page_size / host_processors - system reconnaissanceOther:
mach_make_memory_entry_64 - create memory entry objects (shared memory primitives)fileport_makefd / fileport_makeport - convert between file descriptors and Mach portspid_for_task - resolve PID from task port (post-exploitation reconnaissance)The IOKit imports map the exploit's driver interaction:
Connection Management:
IOServiceMatching → IOServiceGetMatchingService → IOServiceOpen → IOServiceClose - standard open/close lifecycleIOConnectCallMethod / IOConnectCallScalarMethod / IOConnectCallStructMethod - invoke driver methods (external traps)IOConnectTrap4 / IOConnectTrap6 - direct trap calls - bypass normal method dispatch, often used in exploits to hit specific vulnerable code pathsNotification & Monitoring:
IOServiceAddMatchingNotification - register for service matching (wait for driver load)IONotificationPortCreate / IONotificationPortDestroy / IONotificationPortGetRunLoopSource - async notification channelIOServiceWaitQuiet - wait for IORegistry to settleRegistry Access:
IORegistryEntryFromPath / IORegistryEntryCreateCFProperties / IORegistryEntryCreateCFProperty - read driver propertiesIORegistryEntrySearchCFProperty / IORegistryEntryGetChildIterator - traverse IORegistry treeIOCFSerialize - serialize CF objects for IOKit transportObject Lifecycle:
IOIteratorNext / IOObjectRelease - iterator traversal and reference countingCF Constants (IOKit dependencies):
kCFAllocatorDefault / kCFAllocatorNull / kCFBooleanFalse - CoreFoundation allocators and sentinel valueskCFRunLoopDefaultMode - run loop mode for notification port integrationkCFTypeDictionaryKeyCallBacks / kCFTypeDictionaryValueCallBacks - dictionary creation for IOKit property querieskIOMasterPortDefault - default IOKit master portPrivilege Manipulation:
_IOServiceSetAuthorizationID - private API - set the authorization ID on an IOKit connection, used to impersonate a different process's entitlements when interacting with kernel driversCC_SHA1_Init / CC_SHA1_Update / CC_SHA1_Final
CC_SHA256_Init / CC_SHA256_Update / CC_SHA256_Final
CC_SHA384_Init / CC_SHA384_Update / CC_SHA384_Final
arc4random_buf
Three separate hash algorithms (SHA-1, SHA-256, SHA-384) is unusual for exploit code. Likely purposes:
posix_spawn / posix_spawnattr_* / posix_spawn_file_actions_*
getpid / getppid / getuid / seteuid
The posix_spawn family with full attribute control (setflags, setpgroup, setsigdefault) indicates the exploit spawns child processes post-exploitation. Combined with seteuid, this suggests:
newfs_hfs and mount_apfs strings confirm filesystem tool execution via posix_spawnsocket / connect / setsockopt
Minimal networking - just enough for a raw socket connection. This is likely used for:
necp_open + necp_client_action (also imported)Several imports in the "Other" category (30 total) deserve individual attention:
| Import | Significance |
|---|---|
_ffsctl |
Non-standard filesystem control syscall - used for APFS-specific operations not exposed through ioctl |
_proc_pidinfo |
Process inspection - post-exploitation reconnaissance of running processes |
_getattrlist |
Filesystem attribute queries - enumerate file metadata (permissions, ownership, flags) |
_getmntinfo |
Mount table enumeration - discover all mounted filesystems before remount operations |
_processor_set_default / _processor_set_info |
CPU topology queries - identify processor set configuration, possibly for per-CPU kernel data navigation |
_dyldVersionNumber |
Runtime dyld version check - fingerprints the exact dynamic linker version on the target |
_necp_open / _necp_client_action |
NECP (Network Extension Control Policy) - may manipulate network policy or use NECP as an alternative kernel attack surface |
_pid_for_task |
Resolve PID from Mach task port - post-exploitation process identification |
_sandbox_check |
Query sandbox policy without triggering enforcement (used with SANDBOX_CHECK_NO_REPORT) |
Two ObjC block type encoding strings appear in the C-string table:
v12@?0i8 - block taking an int parameter, returning void (12-byte signature)v8@?0 - block taking no parameters, returning void (8-byte signature)These indicate the exploit uses block-based callbacks - likely as completion handlers for asynchronous IOKit notification APIs (IOServiceAddMatchingNotification with IOServiceMatchedCallback blocks) and RunLoop source callbacks (IONotificationPortGetRunLoopSource → CFRunLoopAddSource).
The C-strings, entitlements XML blobs, and import combinations reveal a multi-phase kernel exploitation strategy. This section reconstructs the exploit's methodology from the static evidence.
IOSurfaceRoot
/System/Library/Frameworks/IOSurface.framework/IOSurface
kIOSurfaceIsGlobal
kIOSurfaceWidth
kIOSurfaceHeight
IOSurfaceRoot is the IOKit user client for managing GPU surfaces (shared memory buffers between the GPU and CPU). It has been a recurring attack surface in iOS kernel exploits, notably:
The exploit loads the IOSurface framework dynamically (dlopen of the framework path), creates a matching dictionary via IOServiceMatching("IOSurfaceRoot"), opens a connection with IOServiceOpen, then triggers the vulnerability through IOConnectCallMethod / IOConnectCallScalarMethod / IOConnectCallStructMethod.
The kIOSurfaceIsGlobal, kIOSurfaceWidth, kIOSurfaceHeight property keys are used to construct the IOSurface creation dictionary - these set up a surface with specific dimensions, likely to control the kernel heap allocation size for heap shaping.
IOConnectTrap4
IOConnectTrap6
These are direct IOKit trap functions that bypass the normal IOConnectCallMethod dispatch. They invoke the driver's externalTrap handler directly with 4 or 6 scalar arguments. Exploits use these to:
externalMethodIOConnectCallMethod serialization is too high_IOServiceSetAuthorizationID
<dict><key>com.apple.private.iokit.IOServiceSetAuthorizationID</key><true/></dict>
IOServiceSetAuthorizationID is a private IOKit API that sets the authorization ID on an IOService connection. The authorization ID determines which entitlements the kernel checks when validating IOKit method calls. By setting a custom authorization ID, the exploit can:
The binary contains several XML property list blobs that represent forged entitlements:
Disk Image Access:
<dict>
<key>com.apple.private.diskimages.kext.user-client-access</key><true/>
<key>com.apple.private.security.disk-device-access</key><true/>
<key>com.apple.security.iokit-user-client-class</key>
<array><string>IOHDIXControllerUserClient</string></array>
</dict>
This entitlement blob grants access to the IOHDIXController - the disk image kernel extension. The exploit uses this to mount disk images in the kernel, enabling:
ram://%u format string)VFS Snapshot Access:
<dict><key>com.apple.private.vfs.snapshot</key><true/></dict>
Used with orig-fs string - allows manipulation of APFS filesystem snapshots, specifically the root filesystem snapshot.
Disk Device Access:
<dict><key>com.apple.private.security.disk-device-access</key><true/></dict>
Direct block device access - used with /dev/disk0s1s1 (the root filesystem partition).
task_for_pid-allow:
<dict><key>task_for_pid-allow</key><true/></dict>
The ultimate privilege escalation entitlement - allows calling task_for_pid() on any process, granting full control over any task's Mach port namespace, virtual memory, and thread state.
Sandbox References:
com.apple.security.sandbox
com.apple.driver.AppleMobileFileIntegrity
sandbox_check
SANDBOX_CHECK_NO_REPORT
The exploit interacts with both the sandbox and AMFI (Apple Mobile File Integrity) subsystems - likely to disable sandbox enforcement and code signing checks after gaining kernel control.
Sandbox Operation Types:
Three sandbox operation type strings reveal specific sandbox_check calls the exploit performs:
mach-lookup - checks whether the sandbox permits Mach service lookups (e.g., to com.apple.system.notification_center, com.apple.lsd.mapdb). Used to verify post-exploitation IPC access before attempting service connections.syscall-unix - checks whether specific Unix syscalls are sandbox-allowed. The exploit likely tests whether privileged syscalls (mount, unmount, chown) are permitted before invoking them.appleevent-send - checks Apple event IPC permissions. Unusual for a kernel exploit; may relate to macOS cross-platform code paths in the shared Coruna framework codebase./usr/lib/libCoreEntitlements.dylib
kCENoError
CECRuntime
CEManagedContextFromCFData
CEQueryContextToCFDictionary
CESerializeCFDictionary
CEReleaseManagedContext
CEGetErrorString
The exploit dynamically loads libCoreEntitlements.dylib (via dlopen/dlsym) - this is the userspace library for parsing and manipulating the kernel's entitlement format. Combined with iokit.OSEntitlements and cs_blob zone, this reveals:
cs_blob zone refers to the kernel zone where code signing blobs (including entitlements) are allocatedcs_blob structure of the target process to inject forged entitlementsThe strings reveal a complete root filesystem remount sequence:
Step 1: /dev/disk0s1s1 → Identify root partition
Step 2: orig-fs → Reference APFS root snapshot
Step 3: /sbin/mount_apfs -o nobrowse → Mount APFS volume
Step 4: /private/var/MobileSoftwareUpdate/mnt1 → Mount point (OTA update path)
Step 5: /sbin/newfs_hfs -P → Create new HFS+ filesystem
Step 6: ram://%u → Create RAM disk
Step 7: autodiskmount → Auto-mount handling
Step 8: mount / unmount → Remount operations
This is a rootfs remount attack - the exploit:
/usr/libexec/corelliumd
CORELLIUM
hw.model
T8020
T8120
developer_mode_status
allows_security_research
The exploit performs environment fingerprinting before execution:
/usr/libexec/corelliumd and the string CORELLIUM - if running on a Corellium virtual device (used by security researchers), the exploit likely exits or behaves differentlyT8020 (A12 Bionic) and T8120 (A16 Bionic) - the exploit adapts its offsets/gadgets based on the target SoCdeveloper_mode_status and allows_security_research - may alter behavior on developer-enabled devicessysctlbyname to identify the exact device modelxnu-%u.%u.%u%*s, xnu-%d.%d.%d~%d, etc.) to parse the precise XNU version and select matching offsetsRELEASE is checked - the exploit likely confirms the target is running a RELEASE kernel (not DEBUG or DEVELOPMENT), as debug kernels have different memory layouts and additional mitigationsIOPlatformSerialNumber retrieved via IORegistryEntryCreateCFProperty from the IODeviceTree - used for C2 device fingerprinting or to avoid re-exploitationboot-manifest-hash read from IODeviceTree:/chosen - the boot manifest hash identifies the exact AP ticket / trust cache state, likely used for trust cache manipulation during entitlement forgingPost-Exploitation Target Awareness:
The binary also contains names of high-value system daemons:
backboardd: The iOS UI event dispatch daemon (handles touch, display, HID) - potential post-exploitation injection target or monitoring pointSpringBoard: The iOS home screen / app launcher process - potential target for UI-level persistence or user interaction monitoringAppleSEPManager: The Secure Enclave Processor IOKit driver - the exploit enumerates or interacts with the SEP manager, possibly to query keychain protection state or assess biometric bypass feasibilityAppleM2ScalerCSCDriver: GPU color space conversion driver - potentially queried for device capability fingerprinting or as an alternative IOKit attack surfaceThe binary contains 40 ARM64 instruction search patterns stored as C-strings (indices 3-4, 10-11, 14-15, 40, 44-48, 50-54, 58-60, 80-85, 102, 104, 107, 121, 126, 131-132, 141-145, 151-152 in the C-string table) - 20 fixed-byte patterns and 20 wildcard patterns (containing .. partial-match bytes):
08 3D 40 92 09 18 80 52 → AND x8, x8, #0xFFFF; MOV w9, #0xC0
1F 01 0A EB 41 00 00 54 → CMP x8, x10; B.NE +8; RET
29 01 00 8A → AND x9, x9, x0
08 FD 64 D3 1F 21 00 F1 → LSR x8, x8, #36; CMP x8, #8
These are kernel gadget signatures - byte patterns the exploit searches for in kernel memory to:
The patterns reference operations like masking (AND), comparison (CMP), branching (B.NE), shifts (LSR), and memory access (LDR/STR) - all consistent with kernel function prologues, epilogues, and critical code paths in IOKit drivers and the Mach subsystem.
The C-string table contains several kernel memory segment/section names that reveal what regions the exploit scans or navigates:
| String | Kernel Region | Exploit Purpose |
|---|---|---|
__TEXT_EXEC |
Kernel executable code segment | Primary gadget scanning target - contains all executable kernel code |
__PPLTEXT |
Page Protection Layer text | PPL is Apple's hardware-enforced code integrity mechanism (A12+). The exploit references this segment, implying it either scans PPL code for gadgets or needs to understand PPL boundaries to avoid triggering PPL violations during kernel memory manipulation |
__percpu |
Per-CPU kernel data section | Contains per-processor state (current thread, preemption counts, CPU data pointers). The exploit likely uses __percpu offsets to navigate to the current thread structure - a common technique for finding the current process's credentials in kernel memory |
__KLD |
Kernel Linker Data | Contains kext linking metadata - scanned to locate kext base addresses |
__DATA_CONST |
Kernel read-only data | Contains vtables after boot-time lockdown - vtable pointers are used as kernel ASLR oracles |
__DATA / __data |
Kernel writable data | Target for kernel data structure patching |
__TEXT / __text |
Kernel code | Scanned for function prologues and gadgets |
__thread_starts |
Thread start offsets | Used to locate thread-related kernel structures |
__mod_init_func |
Module initializer table | Kext initialization function pointers - scanned for kext base resolution |
MAC Labels |
Mandatory Access Control labels | The exploit interacts with MAC policy labels - likely to patch or bypass MAC enforcement (AMFI's MAC policy check is a key obstacle to entitlement forging) |
The presence of __PPLTEXT is particularly notable - PPL (Page Protection Layer) is Apple's hardware-backed kernel integrity mechanism introduced on A12+ that prevents even the kernel from modifying page tables or code signing state without going through PPL-protected code paths. The exploit's awareness of PPL boundaries suggests it either works around PPL (on A12+) or confirms PPL absence (on A11 and earlier, where PPL doesn't exist).
The binary contains 649 functions decoded from the LC_FUNCTION_STARTS load command (ULEB128-encoded deltas from the __text section base at 0x7514).
Total functions: 649
Smallest: 4 bytes
Largest: 29,972 bytes
Average: 412 bytes
Median: 180 bytes
| Range | Count | Description |
|---|---|---|
| Tiny (0-32 bytes) | 56 | Trampolines, thunks, single-instruction wrappers |
| Small (32-128 bytes) | 169 | Utility helpers, accessor functions, simple checks |
| Medium (128-512 bytes) | 307 | Core logic functions - the bulk of the exploit |
| Large (512-2048 bytes) | 107 | Complex operations - IOKit interaction, memory manipulation |
| Huge (2048+ bytes) | 9 | Multi-phase exploit routines |
The median of 180 bytes (~45 ARM64 instructions per function) with a long tail of 9 huge functions is characteristic of well-structured exploit code: many small utility/helper functions called by a few large orchestrating routines.
| Rank | Address | Size | Likely Role |
|---|---|---|---|
| 1 | 0x07514 | 29,972 B | _driver - main exploit entry point and orchestrator |
| 2 | 0x1E2AC | 11,340 B | Probable IOSurface exploitation / heap manipulation |
| 3 | 0x15794 | 10,656 B | Probable kernel read/write primitive setup |
| 4 | 0x37764 | 7,868 B | Probable post-exploitation / entitlement patching |
| 5 | 0x446C0 | 5,420 B | Probable filesystem remount sequence |
| 6 | 0x198F8 | 4,684 B | Probable kernel memory scanning (gadget finder) |
| 7 | 0x147C4 | 3,264 B | Probable IOKit service setup / connection management |
| 8 | 0x45F74 | 3,060 B | Probable AMFI / sandbox bypass |
| 9 | 0x2F720 | 2,180 B | Probable Mach port manipulation |
| 10 | 0x29998 | 2,016 B | Probable thread/task control |
At 29,972 bytes (~7,493 ARM64 instructions), _driver is the monolithic exploit orchestrator. Its size is 2.6× larger than the second-largest function and represents 12.6% of all executable code in the binary.
Based on the imported symbols and string references, _driver likely executes the following stages internally:
_driver() {
// Phase 1: Environment fingerprinting
- sysctlbyname("hw.model") → device identification
- Check for /usr/libexec/corelliumd → anti-analysis
- Parse kernel version (xnu-%d.%d.%d~%d patterns)
- Identify SoC (T8020/T8120) → select offset table
// Phase 2: IOSurface trigger
- dlopen(IOSurface.framework)
- IOServiceMatching("IOSurfaceRoot")
- IOServiceOpen → get connection
- Create surface with kIOSurfaceIsGlobal/Width/Height
- IOConnectCallMethod / IOConnectTrap6 → trigger vulnerability
// Phase 3: Kernel R/W primitives
- mach_vm_read_overwrite → kernel read
- mach_vm_write → kernel write
- Scan for gadget patterns (ARM64 byte strings)
- Defeat KASLR via info leak
// Phase 4: Privilege escalation
- task_get_special_port / task_set_special_port
- host_get_special_port / host_security_set_task_token
- Forge entitlements via cs_blob manipulation
- IOServiceSetAuthorizationID → impersonate entitlements
// Phase 5: Sandbox & AMFI bypass
- sandbox_check with SANDBOX_CHECK_NO_REPORT
- Patch AMFI (AppleMobileFileIntegrity) in kernel
- Inject task_for_pid-allow entitlement
// Phase 6: Filesystem persistence
- Mount /dev/disk0s1s1 → access root partition
- APFS snapshot manipulation (orig-fs)
- mount_apfs / newfs_hfs via posix_spawn
- Remount root filesystem read-write
}
The 649 functions are not randomly distributed - they cluster into logical groups based on address proximity:
| Address Range | Functions | Likely Module |
|---|---|---|
| 0x07514 - 0x0EA28 | 1 | _driver (monolithic entry point) |
| 0x0EA28 - 0x14000 | ~80 | Utility functions (comparisons, data manipulation, helpers) |
| 0x14000 - 0x1E000 | ~85 | IOKit interaction layer (service matching, connection, method calls) |
| 0x1E000 - 0x2A000 | ~136 | Kernel memory operations (read/write/scan/remap) |
| 0x2A000 - 0x37000 | ~156 | Mach port and task manipulation |
| 0x37000 - 0x41000 | ~128 | Post-exploitation (entitlements, sandbox, filesystem) |
| 0x41000 - 0x446C0 | ~27 | Stubs, string data, and cross-segment functions |
| 0x446C0 - 0x48880 | ~35 | Late-stage functions (possibly added in later builds) |
The 56 tiny functions (some as small as 4 bytes - a single RET instruction) serve as:
Six functions across the binary are exactly 4 bytes each (a single ARM64 instruction):
[179] 0x21AF0, [189] 0x21F4C, [317] 0x2AD68[642] 0x487F4, [643] 0x487F8, [647] 0x4887CThe three at the end are likely RET instructions or NOP sleds used as function table terminators. The three mid-binary instances may be single-instruction tail calls or stub entries within the kernel memory and Mach port function clusters.
The Global Offset Table contains 264 non-lazy symbol pointer entries, all already resolved to runtime addresses. This is a direct consequence of the memory dump - dyld had already performed binding before the dump was taken.
Sample resolved addresses reveal the runtime memory layout:
| GOT Symbol | Runtime Address | Framework Region |
|---|---|---|
_CC_SHA1_Final |
0x1DD24F6C8 | CommonCrypto |
_CFArrayAppendValue |
0x193383E74 | CoreFoundation |
_IOServiceOpen |
0x19AA63128 | IOKit |
___stack_chk_fail |
0x1DD329730 | libSystem |
_dlopen |
0x19A146D2C | libdyld |
_mach_vm_write |
0x1CDDB459C | libsystem_kernel |
These resolved addresses could be used to:
The __stubs section contains 244 lazy symbol stubs, each 12 bytes (3 ARM64 instructions):
ADRP x16, GOT_page
LDR x16, [x16, GOT_offset]
BR x16
This is the standard ARM64 stub pattern: load the resolved function pointer from the GOT and branch to it. With 244 stubs for 264 GOT entries, 20 symbols are only accessed via direct GOT loads (non-lazy) rather than through stubs - these are likely data symbols (kCFAllocatorDefault, _mach_task_self_, ___stack_chk_guard, etc.) rather than function imports.
The CORUNA framework is a multi-stage iOS/macOS exploit chain. dump.bin is the kernel stage - Stage 2 in the overall kill chain:
┌─────────────────────────────────────────────────────────────────────┐
│ CORUNA KILL CHAIN │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ STAGE 0: DELIVERY │
│ ├─ Victim visits compromised/lure site (gambling, fake exchange) │
│ ├─ Hidden iFrame loads exploit from b27.icu (or other domains) │
│ └─ CloudFront CDN serves JavaScript exploit modules │
│ │
│ STAGE 1: BROWSER EXPLOITATION │
│ ├─ WebKit trigger (yAerzw/KRfmo6/Fq2t1Q/YGPUu7 modules) │
│ ├─ Arbitrary R/W in WebKit renderer process │
│ ├─ PAC bypass + JIT cage escape → native code execution │
│ ├─ Final payload assembly (shellcode + Mach-O loader) │
│ └─ ArrayBuffer C2 channel established with b27.icu │
│ │
│ STAGE 2: KERNEL EXPLOITATION ← dump.bin (this analysis) │
│ ├─ C2 DOWNLOAD command fetches dump.bin │
│ ├─ Binary injected into powerd daemon │
│ ├─ _driver() → IOSurfaceRoot vulnerability trigger │
│ ├─ Kernel R/W achieved → privilege escalation │
│ ├─ Entitlement forging (cs_blob, AMFI bypass) │
│ ├─ Sandbox escape + task_for_pid-allow │
│ └─ Root filesystem remounted read-write │
│ │
│ STAGE 3: POST-EXPLOITATION │
│ ├─ PlasmaLoader / PLASMAGRID root-daemon stager deployed │
│ ├─ Primary C2 via hardcoded addresses │
│ ├─ Fallback DGA (seed: "lazarus", 15-char .xyz domains) │
│ └─ Final objectives: crypto theft, data exfiltration │
│ │
└─────────────────────────────────────────────────────────────────────┘
Our Step 8 analysis (decoded_output/kernel_08_crossref.txt) compared dump.bin against all extracted browser-chain binaries:
Byte-level matches found:
| Browser Binary | Match Region | Match Length | Actual Content |
|---|---|---|---|
final_payload_A_16434916_macho.bin |
dump[0x7AE]-dump[0x5A22] | 21,109 bytes | Zero padding (0x00 fill between load commands and __text) |
final_payload_A_16434916_macho_v2.bin |
dump[0x7AE]-dump[0x5A0A] | 21,085 bytes | Zero padding (24 fewer trailing bytes) |
final_payload_B_6241388a_macho.bin |
dump[0x7AE]-dump[0x5A22] | 21,109 bytes | Zero padding (identical to payload A) |
Key observations:
__text begins at 0x7514. The original analysis contained an arithmetic error: 0x7514 + 0x29A = 0x77AE, not 0x7AE. The matched offset 0x7AE is NOT inside _driver - it is 28,006 bytes before _driver starts.dump.bin and the browser-stage binaries. The kernel exploit and the browser payloads share no executable code.dump.bin is a native stage, not derived from the JavaScript chain.Delivery confirmation:
The shellcode blobs (final_payload_A/B_shellcode.bin, 31,308 bytes each, identical SHA-256) contain 733 ARM64 branch instructions and implement the ArrayBuffer C2 state machine. The DOWNLOAD command within this state machine is the mechanism that fetches dump.bin from the C2 server at runtime.
The March 2026 Google Threat Intelligence Group (GTIG) report identified the current operator of the Coruna framework as UNC6691 - a Chinese financially-motivated threat actor specializing in cryptocurrency theft.
Key attributional findings:
| Finding | Detail |
|---|---|
| Threat actor | UNC6691 (GTIG designation) |
| Origin | China |
| Motivation | Financial - cryptocurrency theft |
| Coruna acquisition | December 2025 (framework passed through several hands) |
| Delivery method | Hidden iFrames on fake crypto exchanges (e.g., impersonating WEEX) and fraudulent gambling sites |
| Post-exploitation | PlasmaLoader / PLASMAGRID root-daemon stager |
How our findings map to GTIG reporting:
7P.GAME is not an unrelated takeover - our discovery of the Chinese gambling site on b27.icu directly matches UNC6691's documented delivery mechanism of using fraudulent gambling sites as lure pages. The gambling frontend serves as the victim-facing bait; the exploit payloads sitting on the same server are loaded via hidden iFrame.
WHOIS CN → US shift - the registrant country change from China (Hong Kong) to United States in the historical WHOIS data aligns with UNC6691's operational timeline. The shift likely occurred as UNC6691 spun up their mass-exploitation campaign, adopting US-based privacy registration to obscure the Chinese operational origin while keeping the CloudFront infrastructure stable.
Multiple delivery domains - GTIG documented several Coruna delivery domains beyond b27.icu:
h4k[.]icu7p[.]gamespin7[.]icuk96[.]icuseven7[.]vipOur initial assessment that b27.icu was the sole delivery domain was based on analyzing the recovered urls.txt IOC file, which only contained b27.icu URLs. The broader domain set was not visible from the recovered codebase alone - it reflects UNC6691's operational deployment across multiple campaigns.
Cookie-based URL derivation - GTIG noted that the framework uses sha256(COOKIE + ID)[:40] to derive resource URLs, which explains the SHA1-hash-like filenames of all 13 JavaScript payloads on b27.icu (e.g., feeee5ddaf2659ba86423519b13de879f59b326d.js). The exploit avoids execution if the device is in Lockdown Mode.
Our initial infrastructure analysis concluded there was "zero evidence of DGA" in the Coruna codebase. This assessment requires nuance based on the GTIG findings:
Stage 1 (Delivery/Exploitation) - NO DGA ✓
Our analysis was correct for Stage 1. The browser exploit delivery uses static CDN-fronted domains with CloudFront. The JavaScript modules recovered from b27.icu contain no domain generation algorithms. The C2 URL (T.Dn.Cn) in the browser exploit is a static config string, not algorithmically generated. CloudFront CDN fronting is fundamentally incompatible with DGA tradecraft.
Stage 3 (Post-Exploitation C2) - DGA EXISTS
The final-stage payload dropped by the kernel exploit - PlasmaLoader (aka PLASMAGRID) - uses a custom DGA as a C2 fallback mechanism:
| Property | Value |
|---|---|
| DGA trigger | Primary hardcoded C2 servers unreachable |
| Seed string | "lazarus" |
| Domain length | 15 characters |
| TLD | .xyz |
| Validation | Checks generated domains against Google Public DNS |
| Purpose | Fallback C2 for persistence after primary infrastructure takedown |
Resolving the @ReturnRei debate:
dump.bin) contain zero DGA logic. The DGA lives in PlasmaLoader, which is deployed after the kernel exploit achieves root."lazarus"-seeded DGA generating 15-character .xyz domains as a C2 fallback.dump.bin is a 2MB Mach-O ARM64 DYLIB kernel exploit containing 649 functions (237KB of executable code), dumped from the powerd daemon's memory on an iPhone X running iOS 16.6. Its entry point _driver (29,972 bytes) orchestrates the attack, but calls on 648 additional helper functions. It is the Stage 2 of the Coruna attack chain - fetched at runtime via ArrayBuffer C2 after the browser exploit achieves native code execution, and injected into a long-lived system daemon for kernel-level exploitation.
The exploit follows a well-established iOS kernel exploitation pattern:
mach_vm_read_overwrite, mach_vm_write, vm_remap)task_set_special_port, host_security_set_task_token, and Mach port manipulationcs_blob structures in kernel memory using the CoreEntitlements frameworktask_for_pid-allowposix_spawn of system tools (mount_apfs, newfs_hfs)| Indicator | Assessment |
|---|---|
| Code volume | 649 functions, 237KB of ARM64 code - substantial |
| Multi-SoC support | T8020 (A12) + T8120 (A16) - maintained across chip generations |
| Anti-analysis | Corellium VM detection, developer mode checks |
| Kernel gadget scanning | 20 fixed + 20 wildcard byte patterns for symbolless kernel navigation |
| Entitlement forging | Full CoreEntitlements integration with cs_blob manipulation |
| Filesystem persistence | Complete rootfs remount chain with APFS + HFS+ + RAM disk support |
| Crypto diversity | SHA-1/SHA-256/SHA-384 - covers multiple code signing eras |
| Standalone kernel stage | No code overlap with browser-stage loaders (cross-ref matched only zero padding) |
This is professional-grade exploit development - not a one-off PoC. The codebase shows signs of long-term maintenance (multi-SoC offset tables, broad iOS version support from 14.0+, multiple hash algorithms for different signing eras), consistent with GTIG's assessment that the Coruna framework "passed through several hands" before reaching UNC6691.
The vulnerability exploited by dump.bin is tracked as CVE-2023-41974:
| Field | Value |
|---|---|
| CVE | CVE-2023-41974 |
| Affected | iOS / iPadOS / macOS |
| Component | IOKit (IOSurfaceRoot) |
| Type | Use-after-free (likely, based on IOSurface + Mach port patterns) |
| Impact | Kernel code execution, full device compromise |
| Patched | iOS 17.0 / macOS Sonoma (September 2023) |
| CISA KEV | Added 2025-01-29 |
| In-the-wild | Confirmed - this binary is the exploit |
Despite the technical sophistication of the exploit, UNC6691's operational security was notably poor:
b27.icu - byte-identical to originals - served from the same CloudFront distribution now fronting a gambling lure site_driver - no attempt at symbol stripping for the export018f0aea-e228-3440-ba05-14d21d036ce1) - traceable build artifactAll raw analysis output files are preserved in the workspace:
| File | Size | Contents |
|---|---|---|
extracted_binaries/dump.bin |
2,097,152 B | Original kernel exploit binary |
decoded_output/kernel_01_macho_header.txt |
5,378 B | Mach-O header + 21 load commands |
decoded_output/kernel_02_segments_sections.txt |
3,442 B | 4 segments, 12 sections, memory map |
decoded_output/kernel_03_symbols.txt |
6,245 B | 1 export, 265 imports (categorized) |
decoded_output/kernel_04_cstrings.txt |
10,527 B | 153 C-strings (categorized) |
decoded_output/kernel_05_objc_cfstring.txt |
2,200 B | 4 CFString entries, ObjC metadata |
decoded_output/kernel_06_got_stubs.txt |
25,458 B | 264 GOT entries, 244 stubs |
decoded_output/kernel_07_function_starts.txt |
25,115 B | 649 functions with size estimates |
decoded_output/kernel_08_crossref.txt |
6,576 B | Cross-reference with browser chain |
supplementary_evidence/infrastructure_recon.md |
- | b27.icu infrastructure recon |
KERNEL_ANALYSIS_PLAN.md |
- | 10-step analysis plan (tracking) |
Analysis by @Nadsec Thanks to matteyeux for posting the Coruna exploit dump publicly. I am an independent security researcher. This analysis was conducted independently and without prior knowledge of Google's or iVerify's Coruna research. All work was performed on publicly recovered JavaScript artifacts.
Find me: Twitter/X · Bluesky · Mastodon · Medium · GitHub
Document generated: 2026-03-09 Corrections: see ERRATA.md