# Coruna Exploit Toolkit — Payload Decryption Analysis ## Overview This document describes the full decryption pipeline used to extract the encrypted binary payloads from the Coruna iOS exploit toolkit. Starting from the obfuscated JavaScript files, we reverse-engineered the encryption scheme, recovered the keys from static data in the repository, and successfully decrypted and decompressed all 19 payload bundles — yielding 65+ Mach-O arm64/arm64e dylibs targeting iOS 13–17. ## Exploit Chain Architecture The exploit chain operates in stages, orchestrated from `group.html`: 1. **Stage 1** — WebKit/WASM memory corruption (builds read/write primitives) 2. **Stage 2** — PAC (Pointer Authentication Code) bypass (for A12+ devices) 3. **Stage 3** — Sandbox escape + payload delivery (`Stage3_VariantB.js`) Stage 3 builds a Mach-O dylib in-memory (`MachOPayloadBuilder`), loads it into the exploited process, and then feeds encrypted payloads to it via the `window.qbrdr()` bridge function. ## Encryption Pipeline Payloads go through the following layers (outermost first): ``` [Server] raw encrypted blob ↓ (or base64-encoded in JS files for local hosting) [ChaCha20] DJB variant, 64-bit counter, 64-bit nonce (all zeros) ↓ [LZMA/XZ] Apple compression_decode_buffer (algorithm 0x306) ↓ [F00DBEEF] Custom container format with multiple Mach-O entries ↓ [Mach-O] arm64 / arm64e dylibs ``` ### ChaCha20 Details - **Variant**: Original DJB ChaCha20 (NOT IETF RFC 8439) - 64-bit block counter (starting at 0) - 64-bit nonce (all zeros) - 20 rounds (10 double-rounds) - **Implementation**: Found at offset `0xad8c` in `bootstrap.dylib` - **Sigma constant**: Standard `"expand 32-byte k"` at offset `0xbb80` ### LZMA Compression - Uses Apple's `compression_decode_buffer` with algorithm constant `0x306` (LZMA) - 8-byte header before compressed data: - Bytes 0–3: Magic `0x0BEDF00D` (little-endian) - Bytes 4–7: Expected decompressed size (`uint32`) - Bytes 8+: XZ/LZMA compressed stream (starts with `fd 37 7a 58 5a 00`) ## Key Recovery ### Master Key (for the manifest file) The master ChaCha20 key is derived from `fixedMachOVal2`, which is the 3rd argument to `platformModule.init()` in `group.html` line 293: ```javascript platformModule.init("", fqMaGkNg(), fqMaGkN4([3436285875, 2332907478, 2884495420, 233193687, 1144711575, 1605576699, 1942246444, 1994816675]), ...) ``` The `fqMaGkN4()` function decodes an array of `uint32` values into a 16-character JavaScript string (2 chars per word, using byte-pair encoding). When stored in the Mach-O as UTF-16LE, this yields the 32-byte key: ``` b38fd1ccd6570d8b3ce8edabd740e60d97e93a44fb27b35f2c54c473a37ce676 ``` This key decrypts the **manifest file** (`7a7d99099b035b2c6512b6ebeeea6df1ede70fbb`). ### Per-File Keys (for the 18 payload files) The decrypted manifest (`0xF00DBEEF` magic, 2192 bytes) contains 19 entries, each 100 bytes (0x64): ``` Offset Size Field 0x00 6 Header/padding (first entry has flags 0x0013, rest 0x0000) 0x06 2 Flags (iOS version / architecture identifier) 0x08 32 Per-file ChaCha20 key 0x28 48 Filename (40-char hex hash + ".min.js" + NUL + padding) ``` Each payload file has its own unique 32-byte ChaCha20 key. The nonce remains all-zeros. ### Base URL `fqMaGkNg()` decodes to `./7a7d99099b035b2c6512b6ebeeea6df1ede70fbb.min.js`. After `resolveUrl()` and base extraction, the base URL is `./` (same directory as `group.html`), meaning all payloads are served from `https://sadjd.mijieqi.cn/.min.js`. ## F00DBEEF Container Format Each decompressed payload is a custom container: ``` Offset Size Field 0x00 4 Magic: 0xF00DBEEF (little-endian, reads as ef be 0d f0) 0x04 4 Entry count (uint32) 0x08 16*N Entry table (N = entry count) +0x00 4 Field 1 (upper 16 bits = segment type) +0x04 4 Field 2 (flags, typically 0x00000003) +0x08 4 Data offset within file +0x0C 4 Data size [data] Concatenated entry payloads (Mach-O dylibs, config blobs) ``` ### Segment Types | Type | Description | Typical Size | |------|-------------|-------------| | 0x08 | Main implant dylib (targets `powerd`, HTTP C2, crash log paths) | ~196–228 KB | | 0x09 | Kernel/sandbox escape dylib (entitlements, mount manipulation) | ~230–334 KB | | 0x0f | Persistence dylib (hooks `launchd`, `powerd`, `AppleCredentialManagerDaemon`) | ~191–192 KB | | 0x0a | Additional exploit/persistence module (newer iOS variants) | ~50–68 KB | | 0x05 | Data blob (likely kernel offsets/gadgets) | ~24 KB | | 0x07 | Small config/metadata blobs | 44 or 468 bytes | ## Manifest Flags & Payload Targeting The 2-byte flags field (offset 0x06 in each manifest entry) encodes the target iOS version and architecture: | Flags | Hash | iOS Target | Arch | Entries | |-------|------|-----------|------|---------| | 0xf230 | `48000486...` | iOS 15.x | arm64 | 5 (3 dylib) | | 0xf330 | `b442ab11...` | iOS 15.x | arm64e | 5 (3 dylib) | | 0xf240 | `5258f6e3...` | iOS 16.0–16.2 | arm64 | 5 (3 dylib) | | 0xf340 | `a78a9419...` | iOS 16.0–16.2 | arm64e | 5 (3 dylib) | | 0xf270 | `38af3c8b...` | iOS 16.6–17.0 | arm64 | 5 (3 dylib) | | 0xf370 | `13344176...` | iOS 16.6–17.0 | arm64e | 7 (4 dylib) | | 0xf280 | `226cbd84...` | iOS 16.3–16.5 | arm64 | 5 (3 dylib) | | 0xf380 | `ae7efd66...` | iOS 16.3–16.5 | arm64e | 5 (3 dylib) | | 0xf290 | `7a1cef00...` | iOS 17.0–17.2 | arm64 | 5 (3 dylib) | | 0xf390 | `377bed74...` | iOS 17.0–17.2 | arm64e | 7 (4 dylib) | | 0xf275 | `e9f89858...` | Extended variant | arm64 | 6 (4 dylib) | | 0xf375 | `f4120dc6...` | Extended variant | arm64e | 6 (4 dylib) | | 0xf373 | `c8a14d79...` | Extended variant | arm64e | 6 (4 dylib) | | 0xf383 | `1b2cbbde...` | Extended variant | arm64e | 6 (4 dylib) | | 0xa205 | `f8a86cf3...` | Older/special | arm64 | 2 (1 dylib) | | 0xa305 | `72a5ac81...` | Older/special | arm64e | 2 (1 dylib) | | 0xa306 | `980c77f1...` | Older/special | arm64e | 2 (1 dylib) | | 0xa303 | `5e89f83e...` | Older/special | arm64 | 3 (2 dylib) | | 0xa304 | `2a1d692b...` | Older/special | arm64 | 3 (2 dylib) | ## Key Strings Found in Extracted Dylibs ### Implant (type 0x08) - Targets `powerd` daemon for persistence - References `/private/var/mobile/Library/Logs/CrashReporter` - References `/private/var/MobileSoftwareUpdate/Update.plist` - HTTP/HTTPS C2 communication capability - Corellium detection: `/usr/libexec/corelliumd` ### Kernel Exploit (type 0x09) - Mount manipulation: `/private/var/MobileSoftwareUpdate/mnt1` - Entitlement handling: `com.apple.security.sandbox`, `libCoreEntitlements.dylib` - Factory mount access: `/private/var/factory_mount` ### Persistence (type 0x0f) - Hooks into `launchd`, `powerd`, `AppleCredentialManagerDaemon`, `nanoregistrylaunchd` - Loads system libraries: `libdyld.dylib`, `libSystem.B.dylib`, `libobjc.A.dylib` ## File Layout ``` coruna-main/ ├── group.html # Main exploit entry point ├── platform_module.js # Platform detection, key derivation ├── utility_module.js # Crypto helpers, Int64, LZW ├── Stage3_VariantB.js # Sandbox escape + MachOPayloadBuilder ├── other/ ├── downloaded/ # 17 files fetched from C2 server │ └── .min.js # Raw encrypted payloads ├── extracted/ # Base64-decoded qbrdr payloads (from repo JS files) │ └── .bin └── payload/ # All 19 decrypted + decompressed F00DBEEF containers ├── bootstrap.dylib # Bootstrap dylib to validate and load other dylibs ├── 7a7d...payload # Decrypted manifest (F00DBEEF with 19 download entries) ├── .bin # F00DBEEF container └── / # Extracted entries per container ├── entry0_type0x08.dylib # Kernel exploit runner -> powerd injector ├── entry1_type0x09.dylib # Kernel exploit <- what jailbreak developers are most interested in ├── entry2_type0x0f.dylib # powerd implant ├── entry3_type0x07.bin └── ... ``` ## Reproduction Steps 1. **Decode master key** from `fqMaGkN4([3436285875, ...])` in `group.html:293` → UTF-16LE → 32 bytes 2. **Decrypt manifest** (`7a7d...`): ChaCha20(master_key, nonce=0) → check `0x0BEDF00D` → LZMA decompress 3. **Parse manifest**: 19 entries at offset 0x120, each 0x64 bytes; per-file key at entry+8 (32 bytes) 4. **Download missing payloads** from `https://sadjd.mijieqi.cn/.min.js` (raw binary) 5. **Decrypt each payload**: ChaCha20(per_file_key, nonce=0) → check `0x0BEDF00D` → LZMA decompress 6. **Parse F00DBEEF containers**: extract Mach-O dylibs by entry table offsets