CVE-2025-4422
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.
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, CallbackFunction
(with SwSmiNum = 0xD9
) and all handlers from gCallbacksTable
(with SwSmiNum = 0x40
) will be registered:
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 );
}
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 CallbackFunction
function is shown below:
EFI_STATUS CallbackFunction(UINTN CpuIndex)
{
EFI_PCD_PROTOCOL *EfiPcdProtocol0;
UINT32 *PcdValue0;
EFI_PCD_PROTOCOL *EfiPcdProtocol1;
UINT32 PcdValue1;
UINT32 Value;
UINT32 RdiReg;
UINT32 RsiReg;
UINT32 RaxReg;
UINT32 RbxReg;
UINT32 RcxReg;
RaxReg = 0;
RbxReg = 0;
RcxReg = 0;
RdiReg = 0;
RsiReg = 0;
EfiPcdProtocol0 = LocateEfiPcdProtocol();
// potentially controlled by the attacker
// as it's possible to use EfiPcdProtocol->Set32(...) from UEFIShell
PcdValue0 = (EfiPcdProtocol0->Get32)(&L05_SERVICES_TOKEN_SPACE_GUID, 0x3002009D);
EfiPcdProtocol1 = LocateEfiPcdProtocol();
// potentially controlled by the attacker
// as it's possible to use EfiPcdProtocol->Set32(...) from UEFIShell
PcdValue1 = (EfiPcdProtocol1->Get32)(&L05_SERVICES_TOKEN_SPACE_GUID, 0x3002009E);
Value = PcdValue0[6];
if ( Value == 0x200 )
{
// SMRAM write
PcdValue0[2] = PcdValue1;
PcdValue0[3] = 0;
PcdValue0[4] = 0;
PcdValue0[8] = 0;
}
else if ( Value == 0x30
|| Value == 0x13C
|| Value == 0xFE
|| Value == 0x44
|| Value == 0x4A
|| Value == 0x5A
|| Value == 0x23CA
|| Value == 0x42
|| Value == 0xBB
|| Value == 0xBA
|| Value == 0x3A
|| Value == 0x1003B )
{
// SMRAM write
PcdValue0[2] = PcdValue1;
PcdValue0[4] = 0;
}
else
{
// SMRAM write
*PcdValue0 = 0;
PcdValue0[1] = 0;
PcdValue0[2] = 0;
PcdValue0[3] = 0;
PcdValue0[4] = 0;
PcdValue0[5] = 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);
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 PcdValue0
value (obtained with (EfiPcdProtocol->Get32)(&L05_SERVICES_TOKEN_SPACE_GUID, 0x3002009D)
) is not validated before attempting to write to the buffer pointed to by PcdValue0
: This primitive may allow an attacker to corrupt SMRAM and execute an arbitrary code.
Similar patterns exist in the following handlers:
Callback0
Callback1
Callback10
Callback12
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