Research By: Slava Makkaveev
Taiwan’s MediaTek has been the global smartphone chip leader since Q3 2020. MediaTek Systems on a chip (SoCs) are embedded in approximately 37% of all smartphones and IoT devices in the world, including high-end phones from Xiaomi, Oppo, Realme, Vivo and more.
Modern MediaTek SoCs, including the latest Dimensity series, contain a special AI processing unit (APU) and audio Digital signal processor (DSP) to improve media performance and reduce CPU usage. Both the APU and the audio DSP have custom Tensilica Xtensa microprocessor architecture. The Tensilica processor platform allows chip manufacturers to extend the base Xtensa instruction set with custom instructions to optimize particular algorithms and prevent them from being copied. This fact makes MediaTek DSP a unique and challenging target for security research.
In this study, we reverse-engineered the MediaTek audio DSP firmware despite the unique opcodes and processor registers, and discovered several vulnerabilities that are accessible from the Android user space.
By chaining with vulnerabilities in Original equipment manufacturer (OEM) partner’s libraries, the MediaTek security issues we found could lead to local privilege escalation from an Android application. A successful exploitation of the DSP vulnerabilities could potentially allow an attacker to listen to user conversations and/or hide malicious code.
The goal of our research is to find a way to attack the audio DSP from Android. First, we need to understand how Android running on the Application processor (AP) communicates with the audio processor. Obviously, there must be a driver that waits for requests from the Android user space, and then, using some kind of Inter-processor communication (IPC), forwards these requests to the DSP for processing.
We used a rooted Xiaomi Redmi Note 9 5G smartphone based on MT6853 (Dimensity 800U) chipset as the testing device. The OS is MIUI Global 12.5.2.0 (Android 11 RP1A.200720.011).
As there are only a few media related drivers presented on the device, it was not difficult to find the driver that is responsible for communication between the AP and the DSP.
Figure 1: Media drivers
We are interested in the /dev/audio_ipi
driver.
A simple search for the driver name in the vendor partition allowed us to find the MediaTek API library /vendor/lib/hw/audio.primary.mt6853.so
. The library exports the AudioMessengerIPI
singleton, which contains the sendIpiMsg
method that can be used to send Inter-processor interrupt (IPI) messages to the audio DSP. We used this library to explore the communication flow between the Android user space and the kernel. In our PoC code, we deal with the driver ioctls
directly without additional wrapping.
The following ioctls
are defined in the /dev/audio_ipi
driver:
Let’s take a look at their purpose.
The DSP firmware must be initialized using the AUDIO_IPI_INIT_DSP ioctl
before sending messages.
We can use the following simple function to open and init the driver:
As we will show later, on the DSP side, there are several independent message handlers called task scenes. Each task scene has its own unique area of responsibility. For example, the phone call task controls speech enhancement. The AUDIO_IPI_LOAD_SCENE ioctl
is used to load a task scene on the DSP. The task scene ID is a mandatory parameter of the IPI message.
There are three different ioctls
for sending an IPI message to the audio DSP. The difference lies in the way the payload data associated with the message is transmitted. The possible options are:
The IPI message has this structure:
The significant fields are:
task_scene
– The DSP task scene ID.data_type
– The payload type. Set to 1 if the payload
field contains data associated with the message. Set to 2 if the payload
field contains information about the shared region.msg_id
– The message ID.param1
and param2
– The message parameters. Usually the param1
contains the payload size.We therefore have full control over the transmitted message right from the Android user space. We target the DSP handler through task_scene
and msg_id
fields, and provide it with our data through param1
, param2
and payload
fields.
Now let’s deal with shared memory. The AUDIO_IPI_REG_DMA ioctl
can be used to request the DSP driver to allocate a region in a dedicated Direct access memory (DMA) that is shared between the AP and the DSP. In fact, two memory regions are allocated: one is for transferring data from the AP to a DSP task scene, and the other is for transferring data in the opposite direction. The DSP driver uses these regions to transmit the message payload when calling the AUDIO_IPI_SEND_DRAM ioctl
and to receive the results.
The AUDIO_IPI_REG_DMA ioctl
expects an object with the following structure for the argument:
As you can see, we control the size of the allocated regions through the a2d_size
and d2a_size
fields.
The Android kernel log kindly provides us with the following information about the reserved DMA:
When we allocate the shared regions for a task scene, the corresponding offsets in the DMA are also logged.
Figure 2: Android kernel log
We should note that the physical address of the task scene’s shared region, calculated as the base physical address of the DMA + the offset of the shared region, is persistent on the device.
The following function can be used to send an IPI message with data transfer over the DMA:
The /dev/audio_ipi
driver does not communicate with the audio DSP directly. Instead, it forwards an IPI message to the System control processor (SCP) by adding the message to the SCP queue. The audio DSP firmware registers the SCP dispatcher to receive audio IPI messages from the SCP.
We know how to send an IPI message to the audio DSP. The next step is to find the handlers for such messages in DSP firmware.
The audio DSP is presented in the Xiaomi factory update by a separate audio_dsp.img
image file. Another way to get the image is to dump the /dev/block/platform/bootdevice/by-name/audio_dsp
partition from a rooted device.
The image file has a proprietary structure but it can be easily reconstructed. On our test device, the DSP image contains nine partitions.
Figure 3: The audio_dsp.img
structure
The cert1
and cert2
partitions are certificates in DER format that are used to verify the integrity of the hifi3
partitions. The hifi3_a_dram
partition is the dynamic memory used by the audio firmware. In the initial state, it is almost empty. The hifi3_a_iram
and hifi3_a_sram
partitions are the code and data of the customized FreeRTOS.
Each partition has a header that stores the size and name of that partition. The header starts with the magic 0x88168858, which can be used to quickly locate the beginning of the partition in the file. Figure 4 shows the hifi3_a_dram
header.
Figure 4: The hifi3_a_dram
header
The header and data sizes of the hifi3_a_dram
partition are 0x200 and 0x8000, respectively. We can cut the hifi3
content easily.
Let’s take a closer look at the hifi3_a_sram
(we skip the header from now on). The partition starts with the 0x400 zero bytes. So there is no special file format here. We are dealing with raw data. The next 0x37F8 bytes appear to be pointers to memory, mostly located after the 0x56000000 address. Starting from byte 0x3BF8 is the Xtensa code.
The IDA Pro 7.6 supports the Tensilica Xtensa architecture. Let’s open the hifi3_a_sram
partition in the IDA with 0x56000000 as the base address.
We used this simple script to recognize the leading raw bytes as pointers (double words):
Now we have thousands of pointers to code and data. But how do we deal with the code? Xtensa opcodes have variable length and IDA has no idea how to proceed.
We first tried to write a script that would find the beginning of functions and try to disassemble. This is possible because most functions start with the entry
opcode that allocates the stack. But it does not work well here because there are too many custom opcodes that IDA is not aware of. Disassembly gets stuck when it reaches an unknown opcode. All we got are snippets like the following:
Eventually, we found another good solution. We used the Xtensa SDK to help IDA.
The HiFi DSP software development toolchain can be freely downloaded from the tensilicatools.com web site. The XtDevTools is part of the installation packet. We used the ~/xtensa/XtDevTools/install/tools/RI-2020.5-linux/XtensaTools/bin/xt-objdump
tool to create object dumps of hifi3
partitions. This way we dumped the hifi3_a_sram
:
The object dump contains disassembled Xtensa code. Let’s take a look at the instruction where IDA got stuck:
As you can see, the hifi3_ss_spfpu_7
core of the xt-objdump
tool knows more Xtensa opcodes than the IDA plugin. Apparently, MediaTek used the standard audio DSP template prepared by Tensilica as a basis for its processor. MediaTek added several particular instructions, but their number is small compared to those offered by Tensilica for audio DSPs.
The object dump contains many errors and cannot be used as the main source for the research. But it can help IDA disassemble the hifi3
partitions more easily.
The Xtensa plugin is represented in the IDA by the xtensa.so
library. It is not easy to patch because there are too many instructions to add. The best solution is to use the object dump to find all basic Xtensa instructions and add the disassembly as a comment to any unrecognized instructions. A simple IDA script can do this job. In Figure 5, you can see what the IDA navigation bar looks like after applying the dump. Almost all code chunks were recognized.
Figure 5: IDA navigation bar
The disassembled code looks like this:
This representation is quite convenient for manual research.
Note that most of the firmware functions contain code to log debug information. A log message includes the name of the current function. MediaTek gave us self-describing function names and the ability to quickly search for functions in the code.
We disassembled the hifi3_a_iram
partition in the same way as the hifi3_a_sram
. The base addresses of the hifi3_a_dram
and the hifi3_a_iram
are 0x4FFB0000 and 0x4FFE0000, respectively.
Now that we found a way to research the audio DSP firmware, let’s take a look at its content.
The MediaTek audio DSP OS is an adapted version of FreeRTOS. MediaTek used the third-party kernel and implemented audio and messaging logic on top of it.
The OS creates a number of audio tasks at startup and associates them with scene IDs. The create_all_audio_task
function is a factory where we can find all supported tasks and scene IDs. The following tasks run on our test device:
Each audio task is represented by a task object that contains a pointer to a recv_message
function. The SCP message dispatcher calls this function when a new IPI message arrives. The IPI message is passed to the function as the second argument.
The recv_message
functions are what we are looking for. This is where audio tasks begin to handle IPI messages sent from the Android side. After a quick look at the code, we see that most tasks, other than the phone call, offload, controller and daemon, use the same task_common_recv_message
function. After all, only the next five functions parse IPI messages, and this is where we can search for vulnerabilities:
task_phone_call_parsing_message
do_offload_actions_post
task_common_task_loop
task_controller_parsing_message
task_auddaemon_task_loop
We manually reviewed these functions and discovered several vulnerabilities that can be exploited to attack the DSP from Android.
This issue is related to all common audio DSP tasks. When processing an IPI message with ID 6 (AUDIO_DSP_TASK_MSGA2DSHAREMEM), the task_common_task_loop
function copies the message payload into the atod_share
field of the common task object. The message param1
is used as the number of bytes to copy. The check that param1
is not larger than the atod_share
field size is omitted. Therefore, the payload overwrites the memory after atod_share
when the payload size is greater than 0x20 bytes.
The following call to the send_ipi_dma
on the Android side overwrites the DSP memory with garbage and causes a crash:
The Android kernel log confirms the issue:
init_share_mem_core
functionThe task_auddaemon_task_loop
function of the daemon task, upon receiving an IPI message with ID 7, calls the init_share_mem_core
function. The init_share_mem_core
copies the message payload to an internal audio_dsp_dram
buffer using the param1
as the number of bytes to copy. The function has a check that param1
is less than 0xE0 bytes, but the audio_dsp_dram
size is 0x20 bytes. 0xC0 bytes can be overwritten.
To fix the DSP heap with controlled values, we can send an IPI message carrying the payload as part of the message:
audio_dsp_hw_open_op
functionWhen processing an IPI message with ID 0x203 (AUDIO_DSP_TASK_PCM_PREPARE), the task_common_task_loop
function calls the get_audiobuf_from_msg
to extract an audio buffer from the physical memory addressed by the param2
. Next, this buffer is passed as an argument to the audio_dsp_hw_open
function that is a wrapper over the audio_dsp_hw_open_op
. The audio_dsp_hw_open_op
function copies this audio buffer to a static array. The field at offset 0x54 in the audio buffer is used as the array index. There is no overflow check of the index value. Therefore, we can provide an arbitrary index to overwrite a portion of memory after the array with controlled values.
To own the audio buffer, we can send the IPI message to the DSP through the shared DMA region and point the param2
to the memory where the payload is located. As we showed earlier, the physical address of the shared DMA region is permanently on the device.
The following PoC code reboots our test device:
Note that the get_audiobuf_from_msg
function also does not validate the param2
. Using any unsuitable or null address in param2
will crash the DSP in memcpy
function:
Now we know how the audio DSP can be attacked from Android through the /dev/audio_ipi
driver. Unfortunately, an unprivileged Android application as well as the adb shell
have no permissions to communicate with this driver. SELinux allows access to the audio_ipi_device
object from the factory
, meta_tst
, and mtk_hal_audio
contexts only. An attacker needs to find a way to exploit the MediaTek Hardware abstraction layer (HAL) to access the DSP driver from under the mtk_hal_audio
context.
While looking for a way to attack the Android HAL, we found several dangerous audio settings implemented by MediaTek for debugging purposes. A third-party Android application can abuse these settings to attack MediaTek Aurisys HAL libraries.
Android documentation states that the AudioManager
provides access to volume and ringer mode control. An Android application can bind the audio service and then use the setParameters
method of the AudioManager
to configure the hardware.
A device manufacturer can add their own audio settings and keep track of their changes. MediaTek provides proprietary parameters to configure the Aurisys libraries. On our test device, the /vendor/lib/hw/audio.primary.mt6853.so
library is responsible for handling audio parameters added by MediaTek. In Figure 6 you can see the accepted format of the setParameters
string argument.
Figure 6: MediaTek audio parameter
The parameter string contains the following information:
The /vendor/etc/aurisys_config.xml
and aurisys_config_hifi3.xml
files define all supported aurisys scenarios and command keys.
For example, the following parameter can be used to enable logging of speech processing information:
Most of the supported commands are interesting for us in terms of information leak. But we want to pay attention only to the PARAM_FILE
command that allows us to set the location of the configuration file related to a particular Aurisys HAL library.
For example, an unprivileged Android app can customize the libfvaudio.so
HAL library provided by an OEM by setting the following parameter:
The Aurisys library parses the configuration file when provided.
Note that generally, device manufacturers do not care about validating configuration files properly because they are not available to unprivileged users. But in our case, we are in control of the configuration files. The HAL configuration becomes an attack vector. A malformed config file could be used to crash an Aurisys library which could lead to LPE.
We have prepared an example of the attack against the libfvaudio.so
HAL library on the Xiaomi device but we cannot share the details for ethical reasons.
To mitigate the described audio configuration issues, MediaTek decided to remove the ability to use the PARAM_FILE
command via the AudioManager
in the release build of Android. CVE-2021-0673 was assigned to the issue.
In our research, we looked at MediaTek audio DSP as an attack target. We reverse engineered the Android API that is responsible for communication with the audio processor, as well as the firmware that runs on the audio DSP.
We show how an unprivileged Android application could abuse the AudioManager API by setting a crafted parameter value in order to attack the Android Aurisys HAL (CVE-2021-0673). By chaining CVE-2021-0673 with the vulnerabilities in original equipment manufacturer (OEM) partner’s libraries, the MediaTek security issue we found could lead to local privilege escalation from an Android application.
With the above local privilege escalation, an Android application may be able to send messages to the audio DSP firmware.
CVE-2021-0661, CVE-2021-0662 and CVE-2021-0663 which present vulnerabilities in the MediaTek audio DSP firmware, may further allow to preform malicious actions such as, for example, execute and hide malicious code within the firmware itself.
Since the DSP firmware has access to the audio data flow, a malformed IPI message could potentially be used by a local attacker to do privilege escalation, and theoretically eavesdrop on the mobile phone’s user.
The discovered vulnerabilities in the DSP firmware (CVE-2021-0661, CVE-2021-0662, CVE-2021-0663) have already been fixed and published in the October 2021 MediaTek Security Bulletin. The security issue in the MediaTek audio HAL (CVE-2021-0673) was fixed in October and will be published in the December 2021 MediaTek Security Bulletin.
Check Point’s customer remain fully protected against such threats with Harmony Mobile Security that Prevents malware from infiltrating devices by detecting and blocking the download of malicious apps in real-time.
By extending Check Point’s industry-leading network security technologies to mobile devices, Harmony Mobile offers a broad range of network security capabilities, ensuring devices are not exposed to compromise with real-time risk assessments
Protection name: VULN__CVE20210673