CVE-2025-10451
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
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 2beee5386dc06e953c7c5261b2d68d3480c0d5b6ac587a05d0a29a1150332bb3.
The pseudocode of the vulnerable function at 0x780 is shown below:
EFI_STATUS SwSmiHandler(
EFI_HANDLE DispatchHandle,
const void *Context,
EFI_SMM_SW_CONTEXT *CommBuffer,
UINTN *CommBufferSize)
{
HP_SERVICE *HpService;
UINTN CpuIndex;
EFI_STATUS Status;
void *BufferCopy;
bool NotFound;
UINT32 RaxRegister;
UINT32 RsiRegister;
EFI_SMM_CPU_PROTOCOL *EfiSmmCpuProtocol;
HpService = gHpService;
CpuIndex = 0;
if ( !gHpService )
{
gSmst->SmmLocateProtocol(&HP_SERVICE_GUID, 0, &gHpService);
HpService = gHpService;
if ( !gHpService )
return 0;
}
if ( !HpService->OfcSmmDriverProtocol )
{
gSmst->SmmLocateProtocol(&OFC_SMM_DRIVER_PROTOCOL_GUID, 0, &HpService->OfcSmmDriverProtocol);
HpService = gHpService;
}
if ( !HpService || !HpService->OfcSmmDriverProtocol )
return 0;
Status = CheckValueFromProtocol(gBufferFromInterface);
if ( !EFI_ERROR(Status) )
{
BufferCopy = AllocateAndCopy(0x2E8, gBufferFromInterface);
if ( !BufferCopy )
BufferCopy = gBufferFromInterface;
Status = gSmst->SmmLocateProtocol(&EFI_SMM_CPU_PROTOCOL_GUID, 0, &EfiSmmCpuProtocol);
if ( !EFI_ERROR(Status) )
{
NotFound = gSmst->NumberOfCpus == 0;
if ( gSmst->NumberOfCpus )
{
do
{
EfiSmmCpuProtocol->ReadSaveState(
EfiSmmCpuProtocol,
4,
EFI_SMM_SAVE_STATE_REGISTER_RAX,
CpuIndex,
&RaxRegister);
if ( RaxRegister == *(*(gHpService + 55) + 517) )
break;
++CpuIndex;
}
while ( CpuIndex < gSmst->NumberOfCpus );
NotFound = CpuIndex == gSmst->NumberOfCpus;
}
if ( NotFound )
return EFI_NOT_FOUND;
EfiSmmCpuProtocol->ReadSaveState(EfiSmmCpuProtocol, 4, EFI_SMM_SAVE_STATE_REGISTER_RSI, CpuIndex, &RsiRegister);
switch ( *RsiRegister )
{
case 0x5F11:
Command5F11(RsiRegister);
break;
case 0x5F12:
if ( (*(gHpService + 6) & 0x20) != 0 )
return 0;
Command5F12(RsiRegister, BufferCopy);
break;
case 0x5F13:
Command5F13(RsiRegister);
break;
default:
if ( *RsiRegister == 0x9999 && *(RsiRegister + 4) == 0xDEAD )
CommandDefault(RsiRegister, BufferCopy);
break;
}
if ( BufferCopy != gBufferFromInterface )
{
CopyMem(gBufferFromInterface, BufferCopy, 0x2E8);
Free(BufferCopy);
}
return 0;
}
}
return Status;
}As we can see from the pseudocode, RsiRegister is controlled by the attacker as it's equal to the RSI value obtained with gEfiSmmCpuProtocol->ReadSaveState. Depending on the *(UINT16 *)RsiRegister, the following functions can be called with the RsiRegister parameter:
Additionally, with *RsiRegister == 0x9999 && *(RsiRegister + 4) == 0xDEAD, the CommandDefault function will be called.
Each of the above functions contains arbitrary write operations to the buffer pointed to by RsiRegister.
Each of the functions are called such that the first parameter is controllable by the attacker. This parameter has the following format:
00000000 struct COMM_BUFFER_PARAM
00000000 {
00000000 UINT32 Result;
00000004 UINT32 Value4;
00000008 UINT32 Value8;
0000000C UINT32 ValueC;
00000010 UINT32 Value10;
00000014 UINT32 Value14;
00000018 UINT32 Value18;
0000001C UINT32 Value1C;
00000020 UINT32 Value20;
00000024 UINT32 Value24;
00000028 };As we can see in the example of the Command5F12 function, the write operations to this buffer are performed without prior validation of the pointer for overlapping with SMRAM:
void Command5F12(COMM_BUFFER_PARAM *Param, void *Buffer)
{
UINT32 Value18;
UINT32 Res;
if ( Param->ValueC == '_HPQ' )
{
Param->Value18 |= 1; // SMRAM write
Value18 = Param->Value18;
if ( Buffer )
{
if ( *(Buffer + 683) == 1 )
Param->Value4 |= 1; // SMRAM write
else
Param->Value4 &= ~1; // SMRAM write
Res = Value18 & 0xFFFFFFFE;
}
else
{
Res = Value18 | 1;
}
Param->Value18 = Res; // SMRAM write
}
}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