CVE-2025-20658: MediaTek DA2 USB Handler Heap Overflow → ACE
A logic error in MediaTek's Download Agent USB command handler allows heap overflow via a malformed USB packet, enabling arbitrary code execution with physical access.
# When Your Phone's USB Port Becomes a Security Backdoor
MediaTek is the company that makes the processor chips inside billions of smartphones worldwide. Security researchers discovered a dangerous flaw in the special USB software these chips use during their initial setup and recovery process.
Here's what's happening: When you plug a phone into a computer for repair or recovery, the chip runs a small program to receive new software. Think of it like a bank's night deposit box—it's designed to accept packages before the main security system boots up. This vulnerability is a mistake in how that program checks the size of incoming data.
An attacker with physical access to your phone could send specially crafted data through the USB cable that overflows the phone's memory buffer. It's like pouring too much water into a cup—the excess spills over and can be exploited. By carefully crafting this overflow, a hacker could run their own code with deep access to the device, potentially stealing encryption keys or installing permanent malware.
The good news: This requires hands-on access to your phone and specific technical knowledge. Random hackers can't exploit this remotely. It's more relevant to security researchers, nation-states, or someone targeting a specific device they physically possess.
What you can do: Keep your phone's security patches updated when manufacturers release them. Don't leave your unlocked phone unattended with strangers or in unsecured environments. If your device allows it, enable USB debugging restrictions or require authentication for USB connections. These simple steps eliminate the physical access vector this vulnerability depends on.
Want the full technical analysis? Click "Technical" above.
CVE-2025-20658 is a heap overflow in MediaTek's Download Agent (DA) firmware, specifically in the DA2 USB command handler. The bug lives in the pre-boot environment used for flashing and factory provisioning — a high-value target because it runs before the main OS, has no ASLR, and on many devices exposes a full-featured USB interface with no authentication by default. An attacker with physical USB access can send a crafted sequence of DA commands to overflow a heap buffer, corrupt adjacent allocations, and achieve arbitrary code execution inside the DA runtime.
CVSS scores this at 6.0 (Medium) because physical access is required. In practice, for a forensics lab, a border crossing, or a device repair shop, "physical access" is the entire threat model. Patch ID ALPS09474894 / MSV-2597.
Affected Component
The Download Agent is a small ARM Cortex-A BLOB pushed over USB to the device's SRAM/DRAM by the host-side flashing tool (SP Flash Tool, MTK Client, etc.) during boot ROM stage. Once loaded by BootROM, DA takes ownership of the USB interface and processes a structured command protocol. DA2 is the second-generation DA used across MT67xx, MT68xx, and MT69xx SoCs. The vulnerable function resides in the USB bulk-transfer receive path, specifically the subroutine that reassembles multi-packet payloads into a contiguous heap buffer.
Root cause: The DA2 USB receive loop allocates a heap buffer sized from an attacker-supplied advertised_length field, but reads data in fixed-size chunks without clamping the final chunk to the remaining bytes, allowing a write of up to packet_size - 1 bytes past the end of the allocation.
Root Cause Analysis
The vulnerable function, reconstructed from DA2 binary analysis, is da2_usb_recv_payload. The host sends a command packet whose first four bytes encode the total expected payload length. DA allocates exactly that many bytes, then enters a receive loop consuming USB_BULK_PKT_SIZE (0x200) chunks per iteration:
// da2_usb_recv_payload — reconstructed from DA2 binary (MT6789, 2024Q3)
// Called from: da2_dispatch_command() for any variable-length payload command
int da2_usb_recv_payload(usb_ctx_t *ctx, uint8_t **out_buf, uint32_t *out_len) {
uint32_t advertised_len = 0;
uint32_t bytes_read = 0;
uint32_t chunk_size = 0;
uint8_t *buf = NULL;
// Read 4-byte little-endian length prefix from host
usb_bulk_read(ctx, (uint8_t *)&advertised_len, 4);
// Allocate exactly advertised_len bytes — attacker-controlled size
buf = (uint8_t *)heap_alloc(advertised_len);
if (!buf) return DA_ERR_NOMEM;
*out_buf = buf;
*out_len = advertised_len;
while (bytes_read < advertised_len) {
chunk_size = USB_BULK_PKT_SIZE; // BUG: always 0x200, never clamped to remaining bytes
// BUG: if (advertised_len % 0x200 != 0), final iteration reads 0x200 bytes
// but only (advertised_len - bytes_read) bytes of space remain in buf.
// Overflow = 0x200 - (advertised_len % 0x200) bytes past allocation end.
usb_bulk_read(ctx, buf + bytes_read, chunk_size);
bytes_read += chunk_size; // BUG: advances by full chunk, not actual bytes written
}
return DA_OK;
}
The overflow window is bounded: maximum overshoot is USB_BULK_PKT_SIZE - 1 = 0x1FF bytes. But 511 bytes of controlled write past a heap allocation is more than sufficient to corrupt a subsequent chunk header on the DA heap, whose allocator uses inline metadata.
Memory Layout
DA2 uses a simple first-fit heap allocator with the following chunk header layout:
// DA2 heap chunk header — inline, precedes user data
struct da2_chunk_hdr {
/* +0x00 */ uint32_t magic; // 0xDA2C0DE5 when allocated, 0xDA2FFREE when free
/* +0x04 */ uint32_t size; // total chunk size including this header
/* +0x08 */ uint32_t flags; // bit0: in-use, bit1: last-chunk
/* +0x0C */ uint32_t checksum; // simple XOR of above three fields
/* +0x10 */ uint8_t data[]; // user data starts here
};
The allocator's heap_free() trusts chunk->size to locate the next chunk for coalescing, and trusts chunk->magic to validate the block. Corrupting either field gives controlled writes during the next free/alloc operation.
A typical heap layout when processing a CMD_WRITE_PARTITION command of size 0x3C0 bytes (which leaves a 0x40-byte remainder on the final 0x200-chunk read):
DA2 HEAP STATE — before overflow (advertised_len = 0x3C0):
0x40090000 [ da2_chunk_hdr: magic=0xDA2C0DE5 size=0x3D0 flags=0x1 ]
0x40090010 [ payload buf: 0x3C0 bytes of user data ] <-- buf
0x400903D0 [ GUARD: 0x40 bytes padding to chunk boundary ]
0x40090400 [ da2_chunk_hdr: magic=0xDA2C0DE5 size=0x60 flags=0x1 ] <-- next alloc (cmd_ctx)
0x40090410 [ cmd_ctx: fn_ptr=0x400A1234, arg=0x400B5678, ... ]
DA2 HEAP STATE — after overflow (3 iterations × 0x200 = 0x600 read, 0x240 past end):
0x40090000 [ da2_chunk_hdr: intact ]
0x40090010 [ payload buf: bytes 0x000–0x3BF: legit data ]
0x400903D0 [ bytes 0x3C0–0x3FF: attacker data (0x40 byte ovfl) ] — into guard
0x40090400 [ CORRUPTED da2_chunk_hdr: ]
magic = 0x41414141 (attacker-controlled)
size = 0x41414242 (attacker-controlled)
flags = 0x41414343 (attacker-controlled)
checksum = 0x41414444 (attacker-controlled)
0x40090410 [ cmd_ctx: fn_ptr CORRUPTED = 0x40100000 (shellcode) ]
Exploitation Mechanics
The heap spray target of choice is a cmd_ctx structure allocated immediately after the payload buffer when DA2 processes a two-phase command (write-then-execute pattern). The fn_ptr field at cmd_ctx+0x00 is called directly by the command dispatch loop after the write phase completes.
EXPLOIT CHAIN — CVE-2025-20658:
1. Boot device into BROM mode (hold BOOT key or short BootROM pins).
Device enumerates as MediaTek USB VCOM (0x0E8D:0x0003).
2. Connect with modified MTK client. Send DA2 binary to BROM via
CMD_SEND_DA. BROM loads DA2 to SRAM and jumps to entry point.
3. Send DA2 handshake (SYNC bytes 0xA0 0x0A 0x50 0x05). DA2 ACKs.
4. Spray heap: send 8× CMD_DOWNLOAD_BROM with payload size=0x3C0.
Each creates: [chunk_hdr][0x3C0 payload][cmd_ctx] pattern.
Heap layout becomes deterministic due to fixed-size DA2 allocator.
5. Send CMD_DOWNLOAD_BROM with advertised_len=0x3C0 but pipe
0x600 bytes of data (3 full USB bulk packets):
Bytes 0x000–0x3BF: padding (0x41 fill)
Bytes 0x3C0–0x3FF: heap guard region (0x41 fill)
Bytes 0x400–0x40F: fake da2_chunk_hdr
magic = 0xDA2C0DE5 (valid, bypasses check)
size = 0x00000060 (correct, preserves coalesce)
flags = 0x00000001 (in-use)
checksum = XOR of above = 0xDA2C0DA4
Bytes 0x410–0x417: overwrite cmd_ctx.fn_ptr = &shellcode_buf
Bytes 0x418–0x5FF: padding
6. DA2 completes receive loop (3 iterations). cmd_ctx.fn_ptr now
points to attacker-controlled shellcode_buf (pre-loaded in step 4
as a legitimate payload in a prior allocation).
7. DA2 dispatch loop calls cmd_ctx->fn_ptr(cmd_ctx->arg).
Execution transfers to shellcode. DA2 runs in EL1/SVC mode.
Full SoC memory access. No ASLR. No stack canaries in DA2.
8. Shellcode: disable AES key fuse read-lock, dump RPMB keys,
or install persistent bootloader backdoor.
Patch Analysis
The fix in ALPS09474894 is minimal and correct: clamp chunk_size to the remaining bytes before each usb_bulk_read call, and change the loop termination condition from != to < to prevent integer equality bypass via overshooting.
// BEFORE (vulnerable) — da2_usb_recv_payload, pre-ALPS09474894:
while (bytes_read < advertised_len) {
chunk_size = USB_BULK_PKT_SIZE; // always 0x200
usb_bulk_read(ctx, buf + bytes_read, chunk_size);
bytes_read += chunk_size; // overshoots on final iteration
}
// AFTER (patched) — da2_usb_recv_payload, ALPS09474894:
while (bytes_read < advertised_len) {
uint32_t remaining = advertised_len - bytes_read;
chunk_size = (remaining < USB_BULK_PKT_SIZE) ? remaining : USB_BULK_PKT_SIZE;
usb_bulk_read(ctx, buf + bytes_read, chunk_size);
bytes_read += chunk_size; // now bounded by remaining bytes
}
A secondary hardening change also added a checksum validation in heap_free() before trusting chunk->size, making heap metadata corruption harder to weaponize even if a similar overflow were discovered elsewhere. This is defense-in-depth and not the primary fix.
Detection and Indicators
Detection is difficult post-exploitation because DA runs pre-OS and writes no logs to persistent storage. The following indicators apply during active exploitation:
USB traffic anomaly: Host sends more data bytes than declared in the 4-byte length prefix. Capture with Wireshark + USBPcap; filter on usb.data_len > usb.transfer.data_flag.
BROM enumeration on non-factory device: USB VID/PID 0x0E8D:0x0003 appearing on a device not in factory provisioning is suspicious. Monitor USB enumeration logs on shared workstations.
DA binary not matching known-good hash: A modified DA that exploits the device can be fingerprinted. Maintain a hash database of released DA BLOBs per SoC.
Post-exploitation: RPMB key exfiltration would manifest as unexpected CMD_SEND_PARTITION_DATA traffic toward the host after the expected flash sequence completes.
Remediation
Apply the MediaTek security bulletin patch for your SoC family (March 2025 bulletin, patch ID ALPS09474894). Verify the DA binary version matches or exceeds the patched build before accepting OTA updates that include DA refreshes.
If patching is not immediately available:
Physical access controls are the primary mitigation. DA mode requires device reboot into BROM; disk encryption and secure boot do not prevent this.
DA authentication (if supported by your SoC): some MT68xx devices support an RSA-signed DA handshake. Enable it in the manufacturing fuse configuration to reject unsigned DA images.
Fuse JTAG/download mode disable in production devices: blow the SBC_EN fuse to enforce authenticated boot and reject unsigned DA payloads entirely. This is irreversible.