// HackTricks · Mobile

Baseband/Modem and SoC Isolation Exploitation

Baseband/Modem and SoC Isolation Exploitation

From a modem foothold to the application-processor kernel

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.[2]

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.[1]

crafted IMS SIP/SDP -> modem parser RCE -> disable/expand MPU region
                     -> AP physical R/W -> overwrite AArch64 kernel text
                     -> function trampoline -> kernel payload

Auditing MPU and bus isolation

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.[2]

MOV     r0, #0
MCR     p15, 0, r0, c6, c2, 0
DSB
MOV     r0, #0x10b
MCR     p15, 0, r0, c6, c1, 4
ISB
DSB

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.[2]

Fragmented payload staging with an egg hunter

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.[1][2]

for i in range(0, len(hunter), 0x180):
    part = hunter[i:i + 28]
    stage = i // 0x180
    stack_depth = 165 - stage * 8
    body = b"v=0\r\n"
    body += b"m=video 51372 RTP/AVP \r\n"
    body += b"a=" + b"acap:1 " * stack_depth
    body += b"crypto:1 " + part + part
    body += b"a" * (0x80 - len(part)) + b"\r\n"

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.[2]

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.[2]

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.[2]

Authenticating a custom IMS delivery endpoint

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.[1][2]

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.[1][2]

A1 = MD5(username ":" realm ":" RES)
A2 = MD5("REGISTER:sip:" realm)
response = MD5(A1_hex ":" nonce ":" nc ":" cnonce ":auth:" A2_hex)

Converting modem physical access into AArch64 kernel execution

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.[2]

physical = symbol_virtual - kernel_virtual_base + kernel_physical_base
# Demonstrated build:
# physical = virtual - 0xffffffc010080000 + 0x80080000

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.[2]

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.[2]

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.[2]

References