Files
khanhduytran0 f536d5f01b TMP
2026-03-10 21:04:56 +07:00

8.8 KiB
Raw Permalink Blame History

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 1317.

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 03: Magic 0x0BEDF00D (little-endian)
    • Bytes 47: 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:

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/<hash>.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) ~196228 KB
0x09 Kernel/sandbox escape dylib (entitlements, mount manipulation) ~230334 KB
0x0f Persistence dylib (hooks launchd, powerd, AppleCredentialManagerDaemon) ~191192 KB
0x0a Additional exploit/persistence module (newer iOS variants) ~5068 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.016.2 arm64 5 (3 dylib)
0xf340 a78a9419... iOS 16.016.2 arm64e 5 (3 dylib)
0xf270 38af3c8b... iOS 16.617.0 arm64 5 (3 dylib)
0xf370 13344176... iOS 16.617.0 arm64e 7 (4 dylib)
0xf280 226cbd84... iOS 16.316.5 arm64 5 (3 dylib)
0xf380 ae7efd66... iOS 16.316.5 arm64e 5 (3 dylib)
0xf290 7a1cef00... iOS 17.017.2 arm64 5 (3 dylib)
0xf390 377bed74... iOS 17.017.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
│   └── <hash>.min.js               # Raw encrypted payloads
├── extracted/                      # Base64-decoded qbrdr payloads (from repo JS files)
│   └── <hash>.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)
    ├── <hash>.bin                  # F00DBEEF container
    └── <hash>/                     # 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/<hash>.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