Header bannerHeader banner
Advisory ID:
BRLY-LOGOFAIL-2023-026

[BRLY-DVA-2023-026] SMM memory corruption vulnerability in combined DXE/SMM driver on Fujitsu device (SMRAM write)

July 2, 2024
Severity:
Low
CVSS Score
3.2
Public Disclosure Date:
June 19, 2024

Summary

Binarly REsearch Team has discovered a SMM memory corruption vulnerability in a Fujitsu device allowing a possible attacker to write fixed or predictable data to SMRAM. Exploiting this issue could lead to escalating privileges to SMM.

Vendors Affected

Lenovo
Phoenix

Affected Products

Yoga Slim 7 Pro

Potential Impact

An attacker with local privileged access can exploit this vulnerability to read the contents of the physical memory and use this information to exploit other vulnerabilities in DXE. A malicious code installed as a result of the vulnerability exploitation in a DXE driver could survive across an operating system (OS) boot process and runtime or modify NVRAM area on SPI flash storage (to gain persistence on target platform). Additionally, this vulnerability potentially could be used by threat actors to bypass OS security mechanisms (modify privileged memory or runtime variables), influence on the OS boot process, and in some cases would allow an attacker to hook or modify EFI Runtime services.

This vulnerability was detected by the Deep Vulnerability Analysis (DVA) component from Binarly Platform

Summary

Binarly REsearch Team has discovered a SMM memory corruption vulnerability in a Fujitsu device allowing a possible attacker to write fixed or predictable data to SMRAM. Exploiting this issue could lead to escalating privileges to SMM.

Vulnerability Information

  • BINARLY internal vulnerability identifier: BRLY-DVA-2023-026
  • Fujitsu PSIRT assigned CVE identifier: CVE-2024-25079
  • CVSS v3.1: 8.2 High AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H

Affected Insyde-based Fujitsu firmware images with confirmed impact by Binarly REsearch Team

Device name Unpacked firmware SHA256 Firmware version IBV Module name Module GUID Module SHA256 Module kind
SystemImageDecoderDxe 5F65D21A-8867-45D3-A41A-526F9FE2C598 86E6C85A8FF7C1DB8FF7292521223C546DD4F40F5168F7DA3134916BF52DA81D 86E6C85A8FF7C1DB8FF7292521223C546DD4F40F5168F7DA3134916BF52DA81D 86E6C85A8FF7C1DB8FF7292521223C546DD4F40F5168F7DA3134916BF52DA81D 86E6C85A8FF7C1DB8FF7292521223C546DD4F40F5168F7DA3134916BF52DA81D 86E6C85A8FF7C1DB8FF7292521223C546DD4F40F5168F7DA3134916BF52DA81D 86E6C85A8FF7C1DB8FF7292521223C546DD4F40F5168F7DA3134916BF52DA81D

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 a malicious firmware code in BIOS could persist across operating system re-installs. Additionally, this vulnerability potentially could be used by malicious actors to bypass security mechanisms provided by UEFI firmware (for example, Secure Boot and some types of memory isolation for hypervisors).

Vulnerability description

The pseudocode of the vulnerable ChildSwSmiHandler function (with the HandlerType: EFI_HDD_PASSWORD_SERVICE_PROTOCOL_GUID) is presented below:

EFI_STATUS __fastcall ChildSwSmiHandler(
        EFI_HANDLE DispatchHandle,
        const void *Context,
        _QWORD *CommBuffer,
        UINTN *CommBufferSize)
{
  char *WpdhStructure; // rbp
  EFI_STATUS Status; // rbx
  _QWORD *Ptr2; // rcx
  _QWORD *i; // rax
  EFI_HANDLE *v9; // rdi
  EFI_HANDLE *Buffer; // rax
  UINTN v11; // r12
  UINTN v12; // r13
  _QWORD *Address; // r14
  __int64 v14; // r15
  EFI_DEVICE_PATH_PROTOCOL *v15; // rbx
  __int64 Ptr1; // [rsp+30h] [rbp-48h]
  EFI_DEVICE_PATH_PROTOCOL *EfiDevicePathProtocol; // [rsp+38h] [rbp-40h] BYREF
  void *EfiStorageSecurityCommandProtocol; // [rsp+40h] [rbp-38h] BYREF
  UINTN BufferSize; // [rsp+90h] [rbp+18h] BYREF

  if ( !CommBuffer || !CommBufferSize )
    return EFI_SUCCESS;
  if ( CommBuffer != (gBuffer + 24) )
    return EFI_ACCESS_DENIED;
  WpdhStructure = (CommBuffer + 2);
  if ( *CommBuffer == 1 )
    goto _CheckSize;
  if ( *CommBuffer != 2 )
  {
    if ( *CommBuffer != 3 )
    {
      if ( *CommBuffer == 4 )
      {
        if ( *CommBufferSize == 17 )
        {
          gFlag = *WpdhStructure;
          goto _ExitSuccess;
        }
        return EFI_ACCESS_DENIED;
      }
_Unsupported:
      Status = EFI_UNSUPPORTED;
      goto _Exit;
    }
_CheckSize:
    if ( *CommBufferSize != 248 )
      return EFI_ACCESS_DENIED;
    Ptr2 = (CommBuffer[22] + 8);
    for ( i = *Ptr2; i != Ptr2; i = *i )
    {
      i[24] = 0;                             // unchecked write (SMRAM corruption)
      i[4] = 0;                              // unchecked write (SMRAM corruption)
      i[6] = 0;                              // unchecked write (SMRAM corruption)
    }
    if ( *WpdhStructure == 'wpdh' )
    {
      Ptr1 = CommBuffer[22];
      if ( !Ptr1 )
      {
        Status = EFI_NOT_READY;
        goto _Exit;
      }
      BufferSize = 0;
      v9 = 0;
      Status = gSmst_0->SmmLocateHandle(
                 ByProtocol,
                 &EFI_STORAGE_SECURITY_COMMAND_PROTOCOL_GUID,
                 0,
                 &BufferSize,
                 0);
      if ( Status == EFI_BUFFER_TOO_SMALL )
      {
        Buffer = AllocatePool(6);
        v9 = Buffer;
        if ( !Buffer )
        {
          Status = EFI_OUT_OF_RESOURCES;
          goto _Exit;
        }
        Status = gSmst_0->SmmLocateHandle(
                   ByProtocol,
                   &EFI_STORAGE_SECURITY_COMMAND_PROTOCOL_GUID,
                   0,
                   &BufferSize,
                   Buffer);
      }
      if ( Status )
      {
        if ( v9 )
          Free(v9);
      }
      else if ( v9 )
      {
        v11 = 0;
        v12 = BufferSize >> 3;
        if ( BufferSize >> 3 )
        {
          do
          {
            Status = gSmst_0->SmmHandleProtocol(
                       v9[v11],
                       &EFI_STORAGE_SECURITY_COMMAND_PROTOCOL_GUID,
                       &EfiStorageSecurityCommandProtocol);
            if ( !Status )
            {
              Status = gSmst_0->SmmHandleProtocol(v9[v11], &EFI_DEVICE_PATH_PROTOCOL_GUID, &EfiDevicePathProtocol);
              if ( !Status )
              {
                Address = *(Ptr1 + 8);
                if ( Address != (Ptr1 + 8) )
                {
                  while ( 2 )
                  {
                    v14 = Address[57];
                    v15 = EfiDevicePathProtocol;
                    while ( *v14 != 127 || *(v14 + 1) != 0xFF )
                    {
                      if ( v15->Type == 127 && v15->SubType == 0xFF )
                      {
                        if ( *v14 != 127 || *(v14 + 1) != 0xFF )
                          goto _Continue;
                        break;
                      }
                      if ( *(v14 + 2) != *v15->Length || *v15->Length && v14 != v15 && sub_1060(v14, v15, *v15->Length) )
                        goto _Continue;
                      v14 += *(v14 + 2);
                      v15 = (v15 + *v15->Length);
                    }
                    if ( v15->Type == 127 && v15->SubType == 0xFF )
                    {
                      Status = 0;
                      Address[24] = v9[v11];    // unchecked write (SMRAM corruption)
                      Address[4] = v9[v11];     // unchecked write (SMRAM corruption)
                      Address[6] = EfiStorageSecurityCommandProtocol;// unchecked write (SMRAM corruption)
                      goto _Next;
                    }
_Continue:
                    Address = *Address;
                    if ( Address != (Ptr1 + 8) )
                      continue;
                    break;
                  }
                  Status = EFI_ABORTED;
                }
              }
            }
_Next:
            ++v11;
          }
          while ( v11 < v12 );
          WpdhStructure = (CommBuffer + 2);
        }
        Free(v9);
        if ( *CommBuffer == 1 || !gWpdhBuffer )
          sub_5B7C(WpdhStructure);              // unchecked write (SMRAM corruption) inside this function
        goto _Exit;
      }
    }
    goto _Unsupported;
  }
  if ( *CommBufferSize == 16 )
  {
    if ( !gAcpiRestoreRegistration )
    {
      Status = gSmst_0->SmmRegisterProtocolNotify(
                 &ACPI_RESTORE_CALLBACK_START_PROTOCOL_GUID,
                 AcpiRestoreNotifier,
                 &gAcpiRestoreRegistration);
      goto _Exit;
    }
_ExitSuccess:
    Status = 0;
_Exit:
    CommBuffer[1] = Status;
    return 0;
  }
  return EFI_ACCESS_DENIED;
}

As we can see from the comments in the pseudocode, the function contains many unchecked write operatons to the controlled pointer.

Let's break them down in more detail:

  1. The code snippet below contains multiple write operations to a buffer that may overlap with SMRAM since Ptr2 is controllable by an attacker:
Ptr2 = (CommBuffer[22] + 8);
for ( i = *Ptr2; i != Ptr2; i = *i )
{
    i[24] = 0;                             // unchecked write (SMRAM corruption)
    i[4] = 0;                              // unchecked write (SMRAM corruption)
    i[6] = 0;                              // unchecked write (SMRAM corruption)
}

        2. The code snippet below contains multiple write operations to a buffer that may overlap with SMRAM since Ptr1 is controllable by an attacker:

Ptr1 = CommBuffer[22];
...
Address = *(Ptr1 + 8);
...
Address[24] = v9[v11];    // unchecked write (SMRAM corruption)
Address[4] = v9[v11];     // unchecked write (SMRAM corruption)
Address[6] = EfiStorageSecurityCommandProtocol;// unchecked write (SMRAM corruption)

        3. The function below contains multiple write operations to a buffer that may overlap with SMRAM since WpdhStructure pointer is controllable by an attacker (when consider the case with gWpdhBuffer = NULL)

__int64 __fastcall sub_5B7C(__int64 WpdhStructure)
{
    __int64 v1; // rax
    __int64 v2; // rdi
    __int64 CtrlPtr1; // rdx
    __int64 CtrlPtr2; // rcx
    __int64 result; // rax
    __int64 v7; // rcx
    EFI_HANDLE Handle; // [rsp+30h] [rbp+8h] BYREF

    v1 = *(WpdhStructure + 160);
    v2 = 0;
    if ( !v1 || !*(WpdhStructure + 184) )
        return EFI_NOT_READY;
    CtrlPtr1 = gWpdhBuffer;
    if ( !gWpdhBuffer )
    {
        CtrlPtr2 = *(WpdhStructure + 176);
        CtrlPtr1 = *(CtrlPtr2 + 16);
        *(CtrlPtr2 + 24) -= 232;
        gWpdhBuffer = CtrlPtr1;
        *(CtrlPtr2 + 16) = CtrlPtr1 + 232;
        *(CtrlPtr1 + 176) = CtrlPtr2;
        *CtrlPtr1 = 'wpdh';
        *(CtrlPtr1 + 160) = *(WpdhStructure + 160);
        v1 = *(WpdhStructure + 160);
    }
    *(CtrlPtr1 + 160) = v1;
    *(CtrlPtr1 + 169) = 1;
    *(CtrlPtr1 + 8) = sub_3D70;
    *(CtrlPtr1 + 184) = 1;
    *(CtrlPtr1 + 16) = sub_3DBC;
    *(CtrlPtr1 + 24) = sub_3E04;
    *(CtrlPtr1 + 32) = sub_30E0;
    *(CtrlPtr1 + 40) = sub_2B8C;
    *(CtrlPtr1 + 48) = sub_3E4C;
    *(CtrlPtr1 + 56) = sub_3350;
    *(CtrlPtr1 + 64) = sub_3A3C;
    *(CtrlPtr1 + 72) = sub_4170;
    *(CtrlPtr1 + 80) = sub_2B00;
    *(CtrlPtr1 + 88) = sub_439C;
    *(CtrlPtr1 + 186) = *(WpdhStructure + 186);
    *(CtrlPtr1 + 96) = sub_6BC4;
    *(CtrlPtr1 + 104) = sub_6C90;
    *(CtrlPtr1 + 112) = sub_6CB8;
    *(CtrlPtr1 + 120) = sub_793C;
    *(CtrlPtr1 + 128) = sub_7B68;
    *(CtrlPtr1 + 136) = sub_7C80;
    *(CtrlPtr1 + 144) = sub_7D80;
    *(CtrlPtr1 + 152) = sub_8380;
    Handle = 0;
    result = gSmst_0->SmmInstallProtocolInterface(
                &Handle,
                &EFI_HDD_PASSWORD_SERVICE_PROTOCOL_GUID,
                EFI_NATIVE_INTERFACE,
                (CtrlPtr1 + 8));
    if ( result >= 0 )
    {
        *(gWpdhBuffer + 224) = *(WpdhStructure + 224);
        sub_7F14();
        v7 = gWpdhBuffer;
        *(gWpdhBuffer + 200) = *(WpdhStructure + 200);
        *(v7 + 208) = *(WpdhStructure + 208);
        *(v7 + 216) = *(WpdhStructure + 216);
        sub_6DB0(v7 + 8, 1);
        if ( !gAcpiRestoreRegistration )
        return gSmst_0->SmmRegisterProtocolNotify(
                &ACPI_RESTORE_CALLBACK_START_PROTOCOL_GUID,
                AcpiRestoreNotifier,
                &gAcpiRestoreRegistration);
        return v2;
    }
    return result;
}

In order to fix this vulnerability, it is necessary to check all nested pointers for overlapping with SMRAM before attempting to write to buffer pointed by it.

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 (YYYY-mm-dd)
Fujitsu PSIRT is notified 2023-12-22
Fujitsu PSIRT informed Insyde 2024-01-22
Insyde PSIRT confirmed issue 2024-01-24
Insyde PSIRT provide patch release 2024-05-13
Binarly Public Disclosure Date 2024-06-17

Acknowledgements

Binarly REsearch Team

Tags
Fujitsu
Vulnerability
FWHunt
See if you are impacted now with our Firmware Vulnerability Scanner