[BRLY-2021-032]
The heap buffer overflow vulnerability in child SW SMI handler on multiple HP devices.
BINARLY efiXplorer team

Summary

BINARLY efiXplorer team has discovered a heap buffer overflow vulnerability in a child SW SMI handler on multiple HP devices that allows corruption of heap metadata.

Vulnerability Information

  • BINARLY internal vulnerability identifier: BRLY-2021-032
  • HP PSIRT assigned CVE identifier: CVE-2022-23924
  • CERT/CC assigned case number: VU#683814
  • CVSS v3.1 8.2 High AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H

Affected HP firmwares with confirmed impact by Binarly team

Device/Firmware File Name SHA256 (File PE32 section) File GUID
Device / firmware version: 02.05.01 Rev.A 0135 b4bafc81429259891b9b9fd60bc06eedf2e61cbee3fbdd1963579d00c7b025a5 3F626A59-87D2-4FCF-B5DE-9D12A8B2AF88

Potential impact

An attacker can exploit this vulnerability to elevate privileges from ring 0 to ring -2, execute arbitrary code in System Management Mode - an environment more privileged than operating system (OS) and completely isolated from it. Running arbitrary code in SMM additionally bypasses SMM-based SPI flash protections against modifications, which can help an attacker to install a firmware backdoor/implant into BIOS. Such malicious firmware code in BIOS could persist across operating system re-installs. Additionally, this vulnerability potentially could be used by threat actors to bypass security mechanisms provided by UEFI firmware (for example, Secure Boot and some types of memory isolation for hypervisors).

Vulnerability description

The vulnerability was found in the child SW SMI handler registered with GUID 3b46cda7-0bd6-4323-a03b-bdf94a023f0a and located at offset 0x2158 in the driver. The pseudocode for this handler is shown below:

EFI_STATUS __fastcall SmiHandler_2158(
        EFI_HANDLE DispatchHandle,
        const void *Context,
        _QWORD *CommBuffer,
        UINTN *CommBufferSize)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  Num = 4;
  if ( CommBuffer && CommBufferSize && (-(__int64)(*CommBufferSize != 24) & EFI_INVALID_PARAMETER) == 0 )
  {
    Size = GetStrSize((_WORD *)CommBuffer[1]);
    CommBuffer1 = CommBuffer[1];
    Size1 = Size;
    Res = 0;
    if ( CommBuffer1 && Size1 )
      Res = SmmIsBufferOutsideSmmValid(CommBuffer1, Size1);
    if ( (-(__int64)(Res == 0) & EFI_INVALID_PARAMETER) == 0 )
    {
      if ( *(_BYTE *)CommBuffer )
      {
        if ( *(_BYTE *)CommBuffer == 1 )
        {
          if ( GetWideStrSize((_WORD *)CommBuffer[1]) )
          {
            Res1 = FactoryConfigHandler((_WORD *)CommBuffer[1], (__int64)&Num, 1);// possible heap overflow
            goto _Exit;
          }
        }
        else if ( *(_BYTE *)CommBuffer == 2 )
        {
          Res1 = BuildIdHandler((_WORD *)CommBuffer[1], (__int64)&Num, 1);// possible heap overflow
_Exit:
          CommBuffer[2] = Res1;
          return 0;
        }
      }
      else if ( GetWideStrSize((_WORD *)CommBuffer[1]) )
      {
        CommBuffer[2] = Validate((_WORD *)CommBuffer[1]) ? 0 : EFI_CRC_ERROR;
        return 0;
      }
      CommBuffer[2] = EFI_INVALID_PARAMETER;
    }
  }
  return 0;
}

If the initial checks pass, the following happens in this handler:

  • if the first byte from CommBuffer is 1 and a pointer to a wide string is located at offset CommBuffer + 8, then function FactoryConfigHandler (offset: 0x1A60) will be called
  • if the first byte from CommBuffer is 2 and a pointer to a wide string is located at offset CommBuffer + 8, then function BuildIdHandler (offset: 0x1C88) will be called

BuildIdHandler

Consider the FactoryConfigHandler function.

Status = SmmGetVariable(L"FactoryConfig", &VariableValue);
if ( Stautus )
    goto _Exit;
if ( GetWideStrSize(StringFromCommBufferPtr) )
{
    StrSize = GetStrSize(StringFromCommBufferPtr);
    Value = VariableValue;
    if ( VariableValue && StrSize )
    {
        if ( StrSize > 500 )
        StrSize = 500;
        if ( VariableValue != StringFromCommBufferPtr )
        CopyMem(VariableValue, StringFromCommBufferPtr, StrSize);
    }
...
}

SmmGetVariable function will do the following:

  • gets the size of the FactoryConfig NVRAM variable
  • allocates a buffer with gSmst->SmmAllocatePool(EfiRuntimeServicesData, Size, &Buffer) (inside SmmAllocatePool, offset: 0x2F98)
  • сopies the value of the FactoryConfig NVRAM variable to the allocated buffer
  • as a result, the VariableValue variable will contain the address of the buffer in the heap containing the value of the FactoryConfig variable
    • at the same time, we control the size of this buffer, because we can change the FactoryConfig NVRAM variable

CopyMem will copy the data at the address that an attacker can control into VariableValue. In this case, the size of the data copied is equal to the size of the string located at the controlled address in CommBuffer (but not more than 500).

In other words, attacker control both buffers:

  • the size of the destination buffer
  • the size and data of the source buffer

This will lead to a heap buffer overflow.

To exploit this vulnerability it is enough to:

  1. If the size of the FactoryConfig NVRAM variable >= 500, set the value of this variable so that its size is less than 500
  2. Setup Communication Buffer:
    • 02 00 00 00 00 00 00 00
    • pointer to wide string with size >= 500
    • 00 00 00 00 00 00 00 00
  3. Trigger the SW SMI Handler (SW SMI number and pointer to Communication Buffer are specified in UEFI ACPI table) via 0xB2 IO port.

BuildIdHandler

This function has the same vulnerability. But the allocated buffer will contain the value of the BuildId variable, and the maximum data size that can be written in the allocated buffer is 112.

To exploit this vulnerability it is enough to:

  1. If the size of the BuildId NVRAM variable >= 112, set the value of this variable so that its size is less than 112
  2. Setup Communication Buffer:
    • 01 00 00 00 00 00 00 00
    • pointer to wide string with size >= 112
    • 00 00 00 00 00 00 00 00
  3. Trigger the SW SMI Handler (SW SMI number and pointer to Communication Buffer are specified in UEFI ACPI table) via 0xB2 IO port

Disclosure timeline

This bug is subject to a 90 day disclosure deadline. After 90 days elapsed or a patch has been made broadly available (whichever is earlier), the bug report will become visible to the public.

Disclosure Activity Date
HP PSIRT is notified 2021-07-12
HP PSIRT confirmed reported issue 2021-08-09
HP PSIRT assigned CVE number 2021-08-19
CERT/CC created a case 2021-11-16
HP PSIRT provide patch release 2022-03-08
BINARLY public disclosure date 2022-03-08

Acknowledgements

BINARLY efiXplorer team