CVE-2025-7026
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
Let's consider the module 5f42fc844985adaf4dcb21aeced55f40128e33ef454607f910cbedf7e9e08c4a
.
The pseudocode of the vulnerable function at 0x179B8
is shown below (SwSmiInputValue
: 0xB2
):
EFI_STATUS __cdecl SwSmiHandler(
EFI_HANDLE DispatchHandle,
const void *Context,
EFI_SMM_SW_CONTEXT *CommBuffer,
UINTN *CommBufferSize)
{
UINTN SwSmiCpuIndex;
INT32 Result;
UINT32 RbxRegister;
UINT32 RcxRegister;
UINTN Value;
LODWORD(Value) = 0;
if ( CommBuffer && CommBufferSize )
SwSmiCpuIndex = CommBuffer->SwSmiCpuIndex;
else
SwSmiCpuIndex = Value;
if ( SwSmiCpuIndex != -1 )
{
// 1. read buffer address in RbxRegister
gEfiSmmCpuProtocol->ReadSaveState(
gEfiSmmCpuProtocol,
4,
EFI_SMM_SAVE_STATE_REGISTER_RBX,
SwSmiCpuIndex,
&RbxRegister);
// 2. read command in RcxRegister
gEfiSmmCpuProtocol->ReadSaveState(
gEfiSmmCpuProtocol,
4,
EFI_SMM_SAVE_STATE_REGISTER_RCX,
SwSmiCpuIndex,
&RcxRegister);
if ( RcxRegister )
{
if ( RcxRegister != 1 )
{
LODWORD(Value) = 0x8004;
_WriteRbx:
gEfiSmmCpuProtocol->WriteSaveState(
gEfiSmmCpuProtocol,
4,
EFI_SMM_SAVE_STATE_REGISTER_RBX,
SwSmiCpuIndex,
&Value);
return 0;
}
Result = CommandRcx1(RbxRegister);
}
else
{
// vulnerable function
Result = CommandRcx0(RbxRegister);
}
LODWORD(Value) = Result;
if ( (Result - 0x9001) <= 1 )
{
gEfiSmmCpuProtocol->WriteSaveState(
gEfiSmmCpuProtocol,
4,
EFI_SMM_SAVE_STATE_REGISTER_RCX,
SwSmiCpuIndex,
&Value);
LODWORD(Value) = 0xFFFF;
}
goto _WriteRbx;
}
return 0;
}
As we can see from the pseudocode, this handler defines the following logic:
EFI_SMM_SAVE_STATE_REGISTER_RCX
in RcxRegister
variableEFI_SMM_SAVE_STATE_REGISTER_RBX
in RbxRegister
variableCommandRcx1
or CommandRcx0
depending on RcxRegister
(command) valueThe pseudocode of the CommandRcx0
function is shown below:
INT32 CommandRcx0(BIOS_SETTINGS_DATA_HEADER *RbxRegister)
{
SetupDataSize = 0xD6C;
ResultStatus = 0;
if ( gRT->GetVariable(L"Setup", &EFI_SETUP_VARIABLE_GUID, 0, &SetupDataSize, SetupData) != EFI_SUCCESS )
return 0x9001;
GetSetupXtuBufferAddress(&SetupXtuBufferAddress);
SetMem(Buffer, 0xBA, 0);
CopyMem(Buffer, (SetupXtuBufferAddress + 12), 0xBA);
...
if ( RbxRegister->Signature == '2DB$' )
{
Length = RbxRegister->Length;
if ( Length == End )
{
if ( RbxRegister->MajorRev == 2 || !RbxRegister->MinorRev )
{
// SMRAM write
RbxRegister->Count = Index;
// SMRAM write
sub_175D4(v11, RbxRegister);
v10 = gGlobalStructureSmm;
*(gGlobalStructureSmm + 0x61E8) = 4;
*(v10 + 25056) = 8;
sub_153D0(v10);
return 0;
}
else
{
return 0x8006;
}
}
else if ( Length >= 0xC )
{
// SMRAM write
RbxRegister->Signature = '2DB$';
*&RbxRegister->MajorRev = 2;
RbxRegister->Length = End;
RbxRegister->Count = Index;
if ( Length >= End )
{
if ( Length > End )
{
ResultStatus = 2;
// SMRAM write
sub_175D4(v11, RbxRegister);
}
return ResultStatus;
}
else
{
return 0x8002;
}
}
else
{
return 0x8003;
}
}
else if ( RbxRegister->Signature == '$DB$' )
{
// SMRAM write
RbxRegister->Length = End;
Result = 1;
RbxRegister->Signature = '2DB$';
*&RbxRegister->MajorRev = 2;
RbxRegister->Count = Index;
}
else
{
return 0x8001;
}
return Result;
}
RbxRegister
is an attacker-controlled pointer and isn't validated. So writing to the RbxRegister
buffer can cause SMRAM corruption if the buffer points to SMRAM or just before SMRAM. While the checks if ( RbxRegister->Signature == '2DB$' )
and if ( RbxRegister->Signature == '$DB$' )
make the vulnerability harder to exploit, they do not completely mitigate the vulnerability. For example, consider the scenario where the attacker writes code to one of the check locations (shown below), in this case, at 0x1775d
:
.text:000000000001775B 81 3B 24 42 44 24 cmp dword ptr [rbx], '$DB$'
.text:0000000000017761 74 0A jz short loc_1776D
.text:0000000000017763 B8 01 80 00 00 mov eax, 8001h
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