baseband-and-soc-isolation-exploitation.md (7726B)
1 --- 2 title: "Baseband/Modem and SoC Isolation Exploitation" 3 section: "Mobile" 4 sectionSlug: "mobile-pentesting" 5 sourcePath: "src/mobile-pentesting/android-app-pentesting/baseband-and-soc-isolation-exploitation.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/mobile-pentesting/android-app-pentesting/baseband-and-soc-isolation-exploitation.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # Baseband/Modem and SoC Isolation Exploitation 14 15 ## From a modem foothold to the application-processor kernel 16 17 A baseband normally runs on a separate real-time core and should remain isolated from application-processor (AP) RAM even after full modem compromise. Therefore, after obtaining code execution on a modem, DSP, GPU, or other coprocessor, test the complete isolation path: local MPU permissions, interconnect/bus firewalls, IOMMU/SMMU mappings, and whether security-critical configuration registers are writable from the compromised context. A failure at any layer can turn coprocessor RCE into AP physical-memory read/write and kernel code execution.<sup>[[2]](#references)</sup> 18 19 The demonstrated UNISOC chain begins with a separate modem bug: recursive parsing of repeated SDP `acap` attributes makes two modem-task stacks collide; attacker data supplied through a `crypto` attribute then overwrites function pointers. A video-call/SRTP path activates the affected task, converting crafted SIP/SDP delivered over IMS into Cortex-R7 code execution.<sup>[[1]](#references)</sup> 20 21 ```text 22 crafted IMS SIP/SDP -> modem parser RCE -> disable/expand MPU region 23 -> AP physical R/W -> overwrite AArch64 kernel text 24 -> function trampoline -> kernel payload 25 ``` 26 27 ## Auditing MPU and bus isolation 28 29 On the affected Cortex-R7 firmware, modem code selects MPU region 0 and writes a configuration interpreted by the platform as a base-zero, 4 GB RWX mapping. The barriers make the permission change complete before later loads, stores, or instruction fetches.<sup>[[2]](#references)</sup> 30 31 ```text 32 MOV r0, #0 33 MCR p15, 0, r0, c6, c2, 0 34 DSB 35 MOV r0, #0x10b 36 MCR p15, 0, r0, c6, c1, 4 37 ISB 38 DSB 39 ``` 40 41 The tested build then permits the modem to access the AP kernel starting at physical `0x80080000`. Treat both the CP15 encoding and this address as firmware-specific: first verify the MPU model, region-size encoding, downstream firewall state, and mapped RAM ranges on the exact target.<sup>[[2]](#references)</sup> 42 43 ## Fragmented payload staging with an egg hunter 44 45 Protocol parsers may place only small instruction sequences at useful stack offsets while storing the larger body as non-contiguous heap chunks. In this case, the Thumb hunter is split into chunks of at most 28 bytes, each linked `0x180` bytes apart (`28` bytes plus a `356`-byte hole). Consecutive SIP `INVITE` bodies use repeated `acap:1` attributes to move the corrupted stack position; the PoC starts at 165 repetitions and subtracts eight for every later hunter chunk.<sup>[[1]](#references)[[2]](#references)</sup> 46 47 ```python 48 for i in range(0, len(hunter), 0x180): 49 part = hunter[i:i + 28] 50 stage = i // 0x180 51 stack_depth = 165 - stage * 8 52 body = b"v=0\r\n" 53 body += b"m=video 51372 RTP/AVP \r\n" 54 body += b"a=" + b"acap:1 " * stack_depth 55 body += b"crypto:1 " + part + part 56 body += b"a" * (0x80 - len(part)) + b"\r\n" 57 ``` 58 59 A negative SIP result does not prove that deep-parser side effects were rolled back: each staging request returned `100 Trying` and then `488 Not Acceptable`, while its bytes remained usable in modem memory. The final body consists of `0x280` padding bytes, the egg `ff fe aa ef`, and the main modem payload.<sup>[[2]](#references)</sup> 60 61 The final body is fragmented into `0x4b0`-byte heap pieces separated by `0x1bc`-byte gaps. The hunter searches byte-by-byte for the egg, walks backward across known `0x61` padding to derive the offset inside the first piece, and copies exactly `0xcb8` bytes to executable memory at `0x8de00000`; whenever the per-piece counter reaches `0x4b0`, it skips `0x1bc` source bytes before continuing. `BLX` then transfers execution to the reconstructed Cortex-R7 payload.<sup>[[2]](#references)</sup> 62 63 This pattern generalizes to fragmented protocol payloads: choose a low-collision marker, recover the first-fragment offset from recognizable padding, model allocator chunk and gap geometry, copy to a known executable destination, and bound the total reconstruction length. All search ranges, fragment sizes, destinations, and return addresses must be recovered again for each firmware.<sup>[[2]](#references)</sup> 64 65 ## Authenticating a custom IMS delivery endpoint 66 67 A custom exploit client can register as IMS user equipment instead of relying on a modified phone. After the first unauthenticated `REGISTER`, Base64-decode the AKA nonce into `RAND` (16 bytes), `SQN xor AK` (6), `AMF` (2), and `MAC` (8). Use Milenage `f2345(Ki, RAND)` to derive `RES`, `CK`, `IK`, and `AK`; recover `SQN`, calculate `XMAC = f1(Ki, RAND, SQN, AMF)`, and abort unless `XMAC == MAC`.<sup>[[1]](#references)[[2]](#references)</sup> 68 69 For `AKAv1-MD5`, the demonstrated client calculates the authenticated `REGISTER` response as follows, then completes the registration-event `SUBSCRIBE`/`NOTIFY` exchange before sending the crafted `INVITE` bodies.<sup>[[1]](#references)[[2]](#references)</sup> 70 71 ```text 72 A1 = MD5(username ":" realm ":" RES) 73 A2 = MD5("REGISTER:sip:" realm) 74 response = MD5(A1_hex ":" nonce ":" nc ":" cnonce ":auth:" A2_hex) 75 ``` 76 77 ## Converting modem physical access into AArch64 kernel execution 78 79 A modem primitive generally addresses physical RAM while kernel symbols are virtual. Resolve symbols from the exact `vmlinux` or `/proc/kallsyms`, determine the matching virtual and physical kernel bases, and translate each target with the following affine mapping.<sup>[[2]](#references)</sup> 80 81 ```python 82 physical = symbol_virtual - kernel_virtual_base + kernel_physical_base 83 # Demonstrated build: 84 # physical = virtual - 0xffffffc010080000 + 0x80080000 85 ``` 86 87 The PoC copies an AArch64 payload over a suitable kernel-text location, then replaces the first 4-byte instruction of `do_sys_open` with `B loader`. The loader saves `x0-x3`, `x29`, and `x30`, calls the payload, restores those registers, replays the displaced `SUB sp, sp, #0x80`, and branches to `do_sys_open+4`. Replaying the overwritten instruction is essential: returning directly to `+4` would violate the original function's expected stack layout.<sup>[[2]](#references)</sup> 88 89 The branch bytes must match the write primitive's byte order. The disclosed modem path treats the written instruction as big-endian, whereas another modem primitive may require the normal little-endian encoding. Also audit copy-loop boundaries: the published byte copier performs a copy, decrements the counter, and exits only when it becomes negative, so initializing the counter to the nominal payload length copies one extra byte.<sup>[[2]](#references)</sup> 90 91 A safe reproduction should first use a one-shot payload with an explicit guard and a non-destructive observable effect such as `printk`. Before any write, validate the target bytes and translated physical addresses against the exact firmware; kernel layout, KASLR/physical placement, hook instruction, and modem heap geometry are not portable across builds.<sup>[[2]](#references)</sup> 92 93 ## References 94 95 - [1] [SSD Secure Disclosure - UNISOC T612 modem RCE](https://ssd-disclosure.com/unisoc-t612-rce/) 96 - [2] [SSD Secure Disclosure - UNISOC T612 modem-to-kernel LPE](https://ssd-disclosure.com/unisoc-t612-lpe)