CVE-2025-33045
An attacker can exploit this vulnerability to elevate privileges from ring 0 to ring -2, and read SMRAM content (that can help to execute arbitrary code in System Management Mode - an evironment more privileged than operating system (OS) and completely isolated from it). 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.
BINARLY REsearch team has discovered an SMRAM read vulnerability in AMI-based Dell device firmware that could allow a potential attacker to read first 248 bytes of SMRAM.
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:
// SMRAM read (TSEG)
//
// 8 bytes from MMIOAddress checked for overlap with SMRAM
// BufferCopy.Offset is from 0 to 255
SredirParam->Value = *(BufferCopy.Offset + MMIOAddress);
break;
case 2:
*(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 reads from the same buffer at an offset controlled by the attacker:
case 1:
// SMRAM read (TSEG)
//
// 8 bytes from MMIOAddress checked for overlap with SMRAM
// BufferCopy.Offset is from 0 to 255
SredirParam->Value = *(BufferCopy.Offset + MMIOAddress);
break;
BufferCopy.Offset
is UINT8
field and it's not validated, which means that attacker can read any byte from (SMRAM
to SMRAM + 248
) by running the handler once. However, the attacker can execute the handler as many times as required.
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 deregistered 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.
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.
BINARLY REsearch team