Summary

In this advisory we are disclosing a heap overflow vulnerability in the MediaTek baseband. The vulnerability can be exploited to gain arbitrary code execution in the context of the baseband runtime. The vulnerability was fixed in 2020 in some models, and received a CVE and more widely deployed fix in 2021.

Vulnerability Details

When processing the CSN.1 decoding of the “UTRAN (3G) Individual Priority Parameters” element, the function rr_decode_3g_individual_priority_para_description implements a two-depth nested repetition (Repeated Individual UTRAN Priority Parameters Description struct and its child element FDD-ARFCN).

The outer loop is iterated by checking the single bit representing the ongoing repetition, and while that equals “1”, a new Repeated Individual UTRAN Priority Parameters Description struct is processed. Within that struct, the repeated FDD-ARFCN is handled in a different way, which could be described as “count-and-allocate”: first iterate over all the repeating elements and count them, then wind back the CSN.1 stream to the beginning of repetition, allocate memory based on the number of items and this time iterate over again, but put the values into the allocated memory.

As the commented decompiled pseudocode below shows, the amount of the stream windback is calculated by the following formula: element_counter * 15 + 5. But the calculation is performed on 8-bit wide numbers (like uint8_t), thus when element_counter reaches 17, the computation overflows and the required 260 bits of rewind becomes 4 bits, essentially corrupting the CSN.1 stream. And this rewinded and corrupted CSN.1 stream goes over the repeated-element parsing again.

void FDD_rr_decode_3g_individual_priority_para_description(byte **bs,int *global_context_ptr)

{
  undefined uVar1;
  int global_context;
  uint uVar3;
  uint uVar4;
  void *__dest;
  int write_ptr;
  void *__src;
  uint stream_bit_counter;
  int iVar7;
  uint previous_element_count;
  undefined local_1c;
  
  global_context = *global_context_ptr;
  uVar3 = FDD_rr_bit_stream_read(bs,1);
  default_priority = 0x2b;
                    /* DEFAULT_UTRAN_PRIORITY */
  if (uVar3 == 1) {
    uVar3 = FDD_rr_bit_stream_read(bs,3);
    default_priority = (undefined)uVar3;
  }
  *(undefined *)(global_context + 7) = default_priority;
  dhl_brief_trace(6,0,(byte *)0xf0000c4,PTR_s_ch_9068dd34);
  while (FDD_rr_bit_stream_read(bs,1) != 0) {
    uVar3 = bit_stream_read(bs,1);
                    /* go for the FDD elements */
    if (uVar3 == 0) {
      element_counter = 0;
      while( true ) {
                    /* stop condition: stream bit value */
        uVar4 = bit_stream_read(bs,1);
        /*
         * VULNERABILITY:
         *   bit_stream_move_back 2nd parameter susceptible to integer overflow.
         *   calculation based on 8-bit wide representation
         */
        stream_bit_counter = element_counter * 0xf + 2 & 0xff;

        if (uVar4 == 0) break;
        bit_stream_read(bs,0xe);
        element_counter = element_counter + 1 & 0xff;
      }
    }
    else {
      stream_bit_counter = 1;
      element_counter = 0;
    }
    uVar4 = bit_stream_read(bs,3);
                    /* calculation based on 8-bit wide representation, as well */
    bit_stream_move_back((int *)bs,stream_bit_counter + 3 & 0xff);
    iVar7 = global_context + (int)(char)uVar4 * 8;
    previous_element_count = (uint)*(byte *)(iVar7 + 8);
    if (element_counter != 0) {
      __src = *(void **)(iVar7 + 0xc);
                    /* allocations based on the element count previously counted */
      if (__src == (void *)0x0) {
        __src = get_ctrl_buffer_ext(element_counter << 1,PTR_s_pcore/modem/gas/common/src/rr_um_9068dd38,0x998);
        *(void **)(iVar7 + 0xc) = __src;
      }
      else {
        __dest = get_ctrl_buffer_ext((previous_element_count + element_counter) * 2, PTR_s_pcore/modem/gas/common/src/rr_um_9068dd38,0x98d);
        *(void **)(iVar7 + 0xc) = __dest;
        memcpy(__dest,__src,previous_element_count << 1);
        free_ctrl_buffer_ext((int)__src,(uint)PTR_s_pcore/modem/gas/common/src/rr_um_9068dd38,0x993)
        ;
      }
      local_1c = (undefined)(previous_element_count + element_counter);
      *(undefined *)(iVar7 + 8) = local_1c;
    }
    uVar3 = FDD_rr_bit_stream_read(bs,1);
    if (uVar3 == 0) {
                    /* go for FDD */
      while (FDD_rr_bit_stream_read(bs,1) != 0) {
        /*
         * OVERFLOW:
         *   on second iteration, write_ptr may end up pointing
         *   outside the allocated heap buffer.
         *   stop condition depends on the bit stream
         *   repetition ending bit signal
         */
        write_ptr = *(int *)(iVar7 + 0xc);
        uVar3 = FDD_rr_bit_stream_read(bs,0xe);
                    /* write to the allocated memory */
        *(short *)(write_ptr + previous_element_count * 2) = (short)uVar3;
        dhl_brief_trace(6,0,(byte *)0xf0000c5,PTR_DAT_9068dd3c);
        previous_element_count = previous_element_count + 1 & 0xff;
      }
    }
    else {
      while (uVar3 = FDD_rr_bit_stream_read(bs,1), uVar3 != 0) {
        FDD_rr_bit_stream_read(bs,0xe);
      }
    }
    FDD_rr_bit_stream_read(bs,3);
  }
  return;
}

Brief Impact Analysis

To read more about the exploitation of this heap overflow, check out our companion blog post which also provides details on the reverse engineering of the MediaTek baseband firmware to find the vulnerability.

Mitigation

MediaTek security update from September 2021 provides the fixes for this issue.e.

Timeline

  • 2019.12.11. Vulnerability reported to Huawei PSIRT (Huawei uses Helio chipsets in some devices)
  • 2019.12.16. Huawei PSIRT sends the report to MediaTek
  • 2020.02.26. MediaTek completes analysis and provides fix for their affected devices
  • 2021.04.23. We request CVE for publication, Huawei PSIRT indicates it will be handled by MediaTek and should be requested directly
  • 2021.05.06. Vulnerability reported directly to the MediaTek product security team
  • 2021.05.19. MediaTek assigns CVE-2021-32484, embargo until August 2021 agreed
  • 2021.09.01. MediaTek releases advisory, vulnerability rated Medium/DoS
  • 2022.01.10. Blog draft shared with MediaTek and Huawei
  • 2022.01.12. MediaTek upgraded vulnerability rating to High/RCE, vendors ask for a small delay to notify customers and update advisory
  • 2022.01.21. Delay requested by MediaTek ends
  • 2022.02.28. Delay requested by Huawei ends
  • 2022.03.10. Report released publicly