Advisory ID:
BRLY-2025-003

SMM memory corruption vulnerability on Dell device (SMRAM write)

September 9, 2025
Severity:
High
CVSS Score
8.2
Public Disclosure Date:
September 9, 2025
CVE ID:

Summary

BINARLY REsearch team has discovered a memory corruption vulnerability in Dell device firmware that could allow a potential attacker to write arbitrary data to an attacker-controlled address limited to the first 248 bytes in SMRAM.
Vendors Affected Icon

Vendors Affected

AMI
Dell
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.

Vulnerability Information

  • BINARLY internal vulnerability identifier: BRLY-2025-003
  • AMI PSIRT assigned CVE identifier: CVE-2025-33045
  • AMI advisory: AMI-SA-2025007
  • Dell advisory: DSA-2025-295
  • 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 Version OEM IBV Module name Module GUID
Dell PowerEdge T30 1.15.0 (2024-05-20) Dell AMI LegacySmmSredir 291e46d4-ca63-4d33-9857-1397c9ad7c0d

Let's consider the module 66505b60fd0d7276658cd80fd680c829f05fb67bd5293572466db09e3e2b5059.

The pseudocode of the vulnerable function is shown below:

EFI_STATUS SwSmiHandler(
        EFI_HANDLE DispatchHandle,
        const void *Context,
        EFI_SMM_SW_CONTEXT *CommBuffer,
        UINTN *CommBufferSize)
{
  UINTN CpuIndex;
  SREDIR_INPUT_PARAMETER *SredirParam;
  EFI_STATUS Status;
  EFI_PHYSICAL_ADDRESS MMIOAddress;
  UINT32 RcxValue;
  UINT32 RbxValue;
  SREDIR_INPUT_PARAMETER BufferCopy;

  RcxValue = 0;
  RbxValue = 0;
  if ( !gAcpiDisTriggered && gMMIOAddress )
  {
    if ( !CommBuffer )
      return 0;
    if ( !CommBufferSize )
      return 0;
    CpuIndex = CommBuffer->SwSmiCpuIndex;
    if ( CommBuffer->SwSmiCpuIndex == -1 )
      return 0;
    gEfiSmmCpuProtocol->ReadSaveState(
      gEfiSmmCpuProtocol,
      4,
      EFI_SMM_SAVE_STATE_REGISTER_RBX,
      CommBuffer->SwSmiCpuIndex,
      &RbxValue);
    gEfiSmmCpuProtocol->ReadSaveState(gEfiSmmCpuProtocol, 4, EFI_SMM_SAVE_STATE_REGISTER_RCX, CpuIndex, &RcxValue);
    SredirParam = (RbxValue + (RcxValue << 32));
    Status = BufferInSmram(SredirParam, 13);
    if ( Status != 0 )
      return Status;
    if ( &BufferCopy != SredirParam )
      CopyMem(&BufferCopy, SredirParam, 13);
    MMIOAddress = gMMIOAddress;
    Status = BufferInSmram(gMMIOAddress, 8);

    if ( Status )
    {
      SredirParam->Value = 0;
      return Status;
    }

    switch ( BufferCopy.Cmd )
    {
      case 1:
        SredirParam->Value = *(BufferCopy.Offset + MMIOAddress);
        break;
      case 2:
        // SMRAM write (TSEG)
        //
        // 8 bytes from MMIOAddress checked for overlap with SMRAM
        // BufferCopy.Offset is from 0 to 255
        *(BufferCopy.Offset + MMIOAddress) = BufferCopy.Value;
        break;
      case 3:
        if ( BufferCopy.Size )
          return WriteBufferSerialPort(&BufferCopy);
        else
          return 0;
      default:
        return EFI_INVALID_PARAMETER;
    }
    return Status;
  }
  return EFI_INVALID_PARAMETER;
}

As we can see from the pseudocode, 8 bytes from buffer at MMIOAddress is checked for overlap with SMRAM:

MMIOAddress = gMMIOAddress;
Status = BufferInSmram(gMMIOAddress, 8);

It then writes to the same buffer at an offset controlled by the attacker:

case 2:
  // SMRAM write (TSEG)
  //
  // 8 bytes from MMIOAddress checked for overlap with SMRAM
  // BufferCopy.Offset is from 0 to 255
  *(BufferCopy.Offset + MMIOAddress) = BufferCopy.Value;
  break;

BufferCopy.Offset is UINT8 field and it's not validated, which means that attacker can corrupt any byte from (SMRAM to SMRAM + 248) by running the handler once. However, the attacker can execute the handler as many times as required.

Similar primitive is located in WriteBufferSerialPort function:

EFI_STATUS WriteBufferSerialPort(SREDIR_INPUT_PARAMETER *SredirParam)
{
  EFI_STATUS Status;
  UINTN Size;
  EFI_PHYSICAL_ADDRESS BufferAddress;
  UINT8 *BufferCopy;
  UINT8 *Copy;
  UINT8 Index;
  EFI_PHYSICAL_ADDRESS MMIOAddress;
  UINTN i;

  if ( SredirParam->Size > 0xF )
    return 0;
  if ( !SredirParam->BufferAddress )
    return EFI_INVALID_PARAMETER;

  Size = SredirParam->Size;
  BufferAddress = SredirParam->BufferAddress;
  Status = BufferInSmram(BufferAddress, Size);
  if ( !Status )
  {
    BufferCopy = SmmAllocatePool(6, Size);
    Copy = BufferCopy;
    if ( BufferCopy )
    {
      if ( Size && BufferCopy != BufferAddress )
        Copy = CopyMem(BufferCopy, BufferAddress, Size);
      if ( Copy )
      {
        Index = 0;
        if ( SredirParam->Size )
        {
          MMIOAddress = gMMIOAddress;
          do
          {
            i = Index++;
            // SMRAM write (TSEG)
            //
            // 8 bytes from MMIOAddress checked for overlap with SMRAM
            // BufferCopy.Offset is from 0 to 255
            *(SredirParam->Offset + MMIOAddress) = Copy[i];
          }
          while ( Index < SredirParam->Size );
        }
        if ( InSmram(Copy) )
          gSmst->SmmFreePool(Copy);
        else
          gBS->FreePool(Copy);
        return 0;
      }
    }
    return EFI_OUT_OF_RESOURCES;
  }
  return Status;
}

MMIOAddress can be controlled by ChildSwSmiHandler with HandlerType value 94eea196-43b8-00db-0066-8944244cb863:

EFI_STATUS ChildSwSmiHandler(
        EFI_HANDLE DispatchHandle,
        const void *Context,
        void *CommBuffer,
        UINTN *CommBufferSize)
{
  if ( !CommBuffer || !CommBufferSize )
    return 0;
  gMMIOAddress = *CommBuffer;
  return gSmst->SmiHandlerUnRegister(DispatchHandle);
}

The fact that the handler is unregistered with gSmst->SmiHandlerUnRegister after initialising the gMMIOAddress variable makes it more difficult to exploit the vulnerability at runtime. However, this vulnerability can be exploited at the DXE stage if a vulnerability exists that allows arbitrary code to be executed in the DXE.

In the target firmware (SHA256: 009bcc7ac14257f3227d1b0a09751797506b52d952bcc129a71b6089cd514418) this buffer will be set with the following function from LegacySredir DXE module:

EFI_STATUS SetMMIOAddress(UINT64 MMIOAddress)
{
  EFI_STATUS Status;
  EFI_SMM_COMMUNICATE_HEADER Source;
  UINTN Size;
  UINT8 CommBuffer[16];
  UINT64 MMIOAddressParam;
  UINTN CommSize;
  EFI_SMM_COMMUNICATION_PROTOCOL *EfiSmmCommunicationProtocol;

  MMIOAddressParam = MMIOAddress;
  Source.HeaderGuid.Data1 = 0x94EEA196;
  Source.HeaderGuid.Data2 = 0xDB43;
  Status = 0x4263;
  Source.HeaderGuid.Data4[0] = 0x87;
  Source.HeaderGuid.Data4[1] = 0x93;
  Source.HeaderGuid.Data4[2] = 0x60;
  Source.HeaderGuid.Data4[3] = 0xB;
  Source.HeaderGuid.Data3 = 0x4263;
  Source.HeaderGuid.Data4[4] = 0xC4;
  Source.HeaderGuid.Data4[5] = 0xFF;
  Source.HeaderGuid.Data4[6] = 0xE1;
  Source.HeaderGuid.Data4[7] = 8;
  if ( !gSmiHandlerTriggered )
  {
    Status = gBS->LocateProtocol(&EFI_SMM_COMMUNICATION_PROTOCOL_GUID, 0, &EfiSmmCommunicationProtocol);
    if ( Status == EFI_SUCCESS )
    {
      gBS->CopyMem(&Source.MessageLength, &Source, 16);
      Size = 8;
      gBS->CopyMem(CommBuffer, &MMIOAddressParam, 8);
      CommSize = 32;
      Status = EfiSmmCommunicationProtocol->Communicate(EfiSmmCommunicationProtocol, &Source.MessageLength, &CommSize);
      gSmiHandlerTriggered = 1;
    }
  }
  return Status;
}

This code is only executed after the EFI_EVENT_READY_TO_BOOT_GUID event (BDS). This event is triggered very late in the boot process, increasing the attack surface.

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
Dell PSIRT is notified 2025-04-08
Dell PSIRT confirmed issue 2025-07-03
AMI PSIRT assigned CVE number 2025-07-03
Dell provided patch release 2025-09-04
Dell published advisory 2025-09-09
AMI published advisory 2025-09-09
BINARLY public disclosure 2025-09-09

Acknowledgements

BINARLY REsearch team

Tags
AMI
Dell
SMM
supply chain
FWHunt
See if you are impacted now with our Firmware Vulnerability Scanner