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 “E-UTRAN IPP with extended EARFCNs” element, the function rr_decode_eutran_ipp_extended_earfcns implements a two-depth nested repetition (Repeated Individual E-UTRAN PP with extended EARFCNs Description struct and its child element EARFCN_extended).

The outer loop is iterated by checking the single bit representing the ongoing repetition, and while that equals “1”, a new Repeated Individual E-UTRAN PP with extended EARFCNs Description struct is processed. Within that struct, the repeated EARFCN_extended 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 * 19 + 4. But the calculation is performed on 8-bit wide numbers (like uint8_t), thus when element_counter reaches 14, the computation overflows and the required 270 bits of rewind becomes 14 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_eutran_ipp_extended_earfcns(byte **bs)

{
  int global_context;
  uint element_counter;
  uint uVar3;
  void *__dest;
  int iVar4;
  void *__src;
  int *piVar5;
  int iVar6;
  uint local_24;
  undefined local_18;
  
  global_context = *(int *)(PTR_global_state2_9068ca38 + 0x14);
  dhl_brief_trace(5,0,(byte *)0xf0004b6,(byte *)0x0);
  piVar5 = (int *)(global_context + 0x5ae8);
  do {
                    /* iterate over the 8 priority levels (8byte a single element) and free them */
    if (*piVar5 != 0) {
      free_ctrl_buffer_ext(*piVar5,(uint)PTR_s_pcore/modem/gas/common/src/rr_re_9068ca3c,0x245);
      *piVar5 = 0;
    }
    *(undefined *)(piVar5 + -1) = 0;
    piVar5 = piVar5 + 2;
  } while (piVar5 != (int *)(global_context + 0x5b28));
  *(undefined *)(global_context + 0x5ae0) = 0x2b;
  element_counter = FDD_rr_bit_stream_read(bs,1);
  if (uVar2 == 1) {
    uVar2 = FDD_rr_bit_stream_read(bs,3);
    *(char *)(global_context + 0x5ae0) = (char)uVar2;
  }
  else {
    *(undefined *)(global_context + 0x5ae0) = 0x2b;
  }
  dhl_brief_trace(6,0,(byte *)0xf0000c6,PTR_s_ch_9068ca40);
  while (FDD_rr_bit_stream_read(bs,1) != 0) {
    element_counter = 0;
    while( true ) {
                    /* stop condition: stream bit -> 0 */
      uVar3 = bit_stream_read(bs,1);
      if (uVar3 == 0) break;
      bit_stream_read(bs,0x12);
                    /* element counter stored on a single byte */
      element_counter = element_counter + 1 & 0xff;
    }
    uVar3 = bit_stream_read(bs,3);
    /*
     * VULNERABILITY:
     *   bit_stream_move_back 2nd parameter susceptible
     *   to integer overflow. 
     *   calculation based on 8-bit wide integers,
     *   thus overflow can happen!
     */
    bit_stream_move_back((int *)bs,(element_counter * 0x13 + 1 & 0xff) + 3 & 0xff);
    iVar6 = global_context + (int)(char)uVar3 * 8;
    local_24 = (uint)*(byte *)(iVar6 + 0x5ae4);
    if (element_counter != 0) {
      __src = *(void **)(iVar6 + 0x5ae8);
                    /* allocate memory based on the counted element previously */
      if (__src == (void *)0x0) {
        __src = get_ctrl_buffer_ext(element_counter << 2,PTR_s_pcore/modem/gas/common/src/rr_re_9068ca3c,0x287);
        *(void **)(iVar6 + 0x5ae8) = __src;
      }
      else {
        __dest = get_ctrl_buffer_ext((local_24 + element_counter) * 4,
                                     PTR_s_pcore/modem/gas/common/src/rr_re_9068ca3c,0x27c);
        *(void **)(iVar6 + 0x5ae8) = __dest;
        memcpy(__dest,__src,local_24 << 2);
        free_ctrl_buffer_ext((int)__src,(uint)PTR_s_pcore/modem/gas/common/src/rr_re_9068ca3c,0x282);
      }
      local_18 = (undefined)(local_24 + element_counter);
      *(undefined *)(iVar6 + 0x5ae4) = local_18;
    }
    while (FDD_rr_bit_stream_read(bs,1) != 0) {
                    /* actually reading the values */
      iVar4 = *(int *)(iVar6 + 0x5ae8);
      data = FDD_rr_bit_stream_read(bs,0x12);
        /*
         * OVERFLOW:
         *   write to out-of-bound memory is possible
         *   when local_24 variable is greater then the
         *   value used for allocation!
         *   while stop condition depends on repetition end bit
         */
      *(uint *)(iVar4 + local_24 * 4) = data;
      dhl_brief_trace(6,0,(byte *)0xf0000c7,PTR_DAT_9068ca44);
      local_24 = local_24 + 1 & 0xff;
    }
    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.

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