Advisory ID:
BRLY-DVA-2025-013

Multiple SMM memory corruption vulnerabilities in SMM module on Lenovo device (SMRAM write)

July 29, 2025
Severity:
High
CVSS Score
8.2
Public Disclosure Date:
July 29, 2025
CVE ID:

Summary

BINARLY REsearch team has discovered multiple memory corruption vulnerabilities in Lenovo device firmware that could allow a potential attacker to write fixed or predictable data to an attacker-controlled address.
Vendors Affected Icon

Vendors Affected

Lenovo
Insyde
Affected Products icon

Affected Products

Multiple

Potential Impact

An attacker could exploit this vulnerability to elevate privileges from ring 0 to ring -2 and execute arbitrary code in System Management Mode, an environment more privileged than and completely isolated from the operating system (OS). Running arbitrary code in SMM also bypasses SMM-based SPI flash protections against modification, which can help an attacker to install a firmware backdoor/implant. Such malicious code in the firmware could persist through operating system reinstallations. In addition, this vulnerability could potentially be used by malicious actors to bypass security mechanisms provided by UEFI firmware, such as Secure Boot and some types of memory isolation for hypervisors.

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

Vulnerability Information

  • BINARLY internal vulnerability identifier: BRLY-DVA-2025-013
  • Lenovo PSIRT assigned CVE identifier: CVE-2025-4421
  • Lenovo advisory: LEN-201013
  • CVSS v3.1: 8.2 High AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H

Affected firmware with confirmed impact by BINARLY team

Device name Firmware version OEM IBV Module name
ideacentre-aio-3-24arr9 O6KKT12A (2025-01-16) Lenovo Insyde EfiSmiServices
ideacentre-aio-3-24irh9 O6AKT1DA/1.0.0.29 (2024-08-09) Lenovo Insyde EfiSmiServices
yoga-aio-9-32irh8 O62KT24A (2024-08-08) Lenovo Insyde EfiSmiServices

Vulnerability description

Let's consider the module c04ba30145bb11f4e8b15969a6f59bc03b52e6ef160e1d9dcb2b3fc459428b55.

This module contains custom logic to register SMI handlers (callbacks) using EFI_L05_SMM_SW_SMI_INTERFACE_PROTOCOL:

EFI_STATUS RegisterCallbackFunctions()
{
  UINTN Offset;
  EFI_STATUS Status;
  EFI_L05_SMM_SW_SMI_INTERFACE_PROTOCOL *EfiL05SmmSwSmiInterfaceProtocol;

  Offset = 0;
  EfiL05SmmSwSmiInterfaceProtocol = 0;
  Status = gSmst->SmmLocateProtocol(&EFI_SMM_CPU_PROTOCOL_GUID, 0, &gEfiSmmCpuProtocol);
  if ( !EFI_ERROR(Status) )
  {
    Status = gSmst->SmmLocateProtocol(&EFI_SMM_VARIABLE_PROTOCOL_GUID, 0, &gEfiSmmVariableProtocol);
    if ( !EFI_ERROR(Status) )
    {
      Status = gSmst->SmmLocateProtocol(
                 &EFI_L05_SMM_SW_SMI_INTERFACE_PROTOCOL_GUID,
                 0,
                 &EfiL05SmmSwSmiInterfaceProtocol);
      if ( !EFI_ERROR(Status) )
      {
        Status = EfiL05SmmSwSmiInterfaceProtocol->RegisterCallbackFunction(
                   EfiL05SmmSwSmiInterfaceProtocol,
                   0xD9,
                   FeatureCallbackType,
                   CallbackFunction);
        if ( !EFI_ERROR(Status) )
        {
          do
          {
            Status = EfiL05SmmSwSmiInterfaceProtocol->RegisterCallbackFunction(
                       EfiL05SmmSwSmiInterfaceProtocol,
                       0x40,
                       FeatureCallbackType,
                       *(&gCallbacksTable.Function + Offset));
            if ( Status == EFI_OUT_OF_RESOURCES )
              break;
            Offset += 16;
          }
          while ( Offset < 0xF0 );
        }
      }
    }
  }
  return Status;
}

After executing this function, all handlers from gCallbacksTable will be registered in the following loop (with SwSmiNum = 0x40):

do
{
  Status = EfiL05SmmSwSmiInterfaceProtocol->RegisterCallbackFunction(
              EfiL05SmmSwSmiInterfaceProtocol,
              0x40,
              FeatureCallbackType,
              *(&gCallbacksTable.Function + Offset));
  if ( Status == EFI_OUT_OF_RESOURCES )
    break;
  Offset += 16;
}
while ( Offset < 0xF0 );

gCallbacksTable contains 15 SMI handlers:

.data:00000000000051B0 ; CALLBACK_ITEM gCallbacksTable
.data:00000000000051B0 gCallbacksTable CALLBACK_ITEM <0, offset Callback0>
.data:00000000000051B0                                         ; DATA XREF: RegisterCallbackFunctions+9D↑o
.data:00000000000051C0                 CALLBACK_ITEM <1, offset Callback1>
.data:00000000000051D0                 CALLBACK_ITEM <2, offset Callback2>
.data:00000000000051E0                 CALLBACK_ITEM <3, offset Callback3>
.data:00000000000051F0                 CALLBACK_ITEM <4, offset Callback4>
.data:0000000000005200                 CALLBACK_ITEM <5, offset Callback5>
.data:0000000000005210                 CALLBACK_ITEM <6, offset Callback6>
.data:0000000000005220                 CALLBACK_ITEM <7, offset Callback7>
.data:0000000000005230                 CALLBACK_ITEM <8, offset Callback8>
.data:0000000000005240                 CALLBACK_ITEM <9, offset Callback9>
.data:0000000000005250                 CALLBACK_ITEM <0Ah, offset Callback10>
.data:0000000000005260                 CALLBACK_ITEM <0Bh, offset Callback11>
.data:0000000000005270                 CALLBACK_ITEM <0Ch, offset Callback12>
.data:0000000000005280                 CALLBACK_ITEM <10h, offset Callback16>
.data:0000000000005290                 CALLBACK_ITEM <11h, offset Callback17>

The pseudocode of Callback7 function is shown below:

MACRO_EFI Callback7(UINTN CpuIndex)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS NUMPAD "+" TO EXPAND]

  RaxReg = 0;
  RbxReg = 0;
  RcxReg = 0;
  RdiReg = 0;
  RsiReg = 0;
  Length = 0;
  Data = 0;
  DataSize = 0;
  EfiSmmFwBlockServiceProtocol = 0;
  L05SPFlag = 0;
  gEfiSmmCpuProtocol->ReadSaveState(gEfiSmmCpuProtocol, 4, EFI_SMM_SAVE_STATE_REGISTER_RAX, CpuIndex, &RaxReg);
  gEfiSmmCpuProtocol->ReadSaveState(gEfiSmmCpuProtocol, 4, EFI_SMM_SAVE_STATE_REGISTER_RBX, CpuIndex, &RbxReg);
  gEfiSmmCpuProtocol->ReadSaveState(gEfiSmmCpuProtocol, 4, EFI_SMM_SAVE_STATE_REGISTER_RCX, CpuIndex, &RcxReg);
  gEfiSmmCpuProtocol->ReadSaveState(gEfiSmmCpuProtocol, 4, EFI_SMM_SAVE_STATE_REGISTER_RDI, CpuIndex, &RdiReg);
  gEfiSmmCpuProtocol->ReadSaveState(gEfiSmmCpuProtocol, 4, EFI_SMM_SAVE_STATE_REGISTER_RSI, CpuIndex, &RsiReg);
  if ( RaxReg != 0x534D0740 )
    return EFI_UNSUPPORTED;
  if ( !RbxReg )
  {
    *RsiReg = 0x3F6053C7BA22BEEC;             // SMRAM write
    goto _ToExit;
  }
  switch...
_ToExit:
  gEfiSmmCpuProtocol->WriteSaveState(gEfiSmmCpuProtocol, 4, EFI_SMM_SAVE_STATE_REGISTER_RAX, CpuIndex, &RaxReg);
  gEfiSmmCpuProtocol->WriteSaveState(gEfiSmmCpuProtocol, 4, EFI_SMM_SAVE_STATE_REGISTER_RBX, CpuIndex, &RbxReg);
  gEfiSmmCpuProtocol->WriteSaveState(gEfiSmmCpuProtocol, 4, EFI_SMM_SAVE_STATE_REGISTER_RCX, CpuIndex, &RcxReg);
  gEfiSmmCpuProtocol->WriteSaveState(gEfiSmmCpuProtocol, 4, EFI_SMM_SAVE_STATE_REGISTER_RDI, CpuIndex, &RdiReg);
  gEfiSmmCpuProtocol->WriteSaveState(gEfiSmmCpuProtocol, 4, EFI_SMM_SAVE_STATE_REGISTER_RSI, CpuIndex, &RsiReg);
  return 0;
}

As we can see from the pseudocode, the RsiReg value (obtained from the RSI register with gEfiSmmCpuProtocol->ReadSaveState) is not validated before attempting to write to the buffer pointed to by RsiReg:

gEfiSmmCpuProtocol->ReadSaveState(gEfiSmmCpuProtocol, 4, EFI_SMM_SAVE_STATE_REGISTER_RSI, CpuIndex, &RsiReg);
if ( RaxReg != 0x534D0740 )
  return EFI_UNSUPPORTED;
if ( !RbxReg )
{
  *RsiReg = 0x3F6053C7BA22BEEC;             // SMRAM write
  goto _ToExit;
}

This primitive may allow an attacker to corrupt SMRAM and execute an arbitrary code.

Disclosure timeline

This vulnerability is subject to a 90 day disclosure period. After 90 days or when a patch has been made generally available (whichever comes first) the advisory will be publicly disclosed.

Disclosure Activity Date
Lenovo PSIRT is notified 2025-04-08
Lenovo PSIRT is confirmed issue 2025-06-16
Lenovo PSIRT assigned CVE number 2025-06-16
BINARLY public disclosure date 2025-07-29

Acknowledgements

BINARLY REsearch team

Tags
Lenovo
Insyde
SMM
FWHunt
See if you are impacted now with our Firmware Vulnerability Scanner