CVE-2025-7029
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 e07a5de6ced42c97783bf81375729c78d4233eaa294820eb2624b83c9dcd644b
.
The pseudocode of the vulnerable function at 0xB10
is shown below (SwSmiInputValue
: 0xB2
):
EFI_STATUS SwSmiHandler(
EFI_HANDLE DispatchHandle,
const void *Context,
EFI_SMM_SW_CONTEXT *CommBuffer,
UINTN *CommBufferSize)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS NUMPAD "+" TO EXPAND]
...
Value = 0;
DataSize = 0x5F;
Length = 0;
Count = 0;
Status = gEfiSmmVariableProtocol->SmmGetVariable(
L"OcSetup",
&OC_SETUP_VARIABLE_GUID,
&Attributes,
&DataSize,
&OcSetupData);
if ( Status < 0 || !OcSetupData.OverclockingSupport )
return Status;
if ( CommBuffer && CommBufferSize )
CpuIndex = CommBuffer->SwSmiCpuIndex;
if ( CpuIndex == -1 )
return EFI_UNSUPPORTED;
Status = gEfiSmmCpuProtocol->ReadSaveState(
gEfiSmmCpuProtocol,
4,
EFI_SMM_SAVE_STATE_REGISTER_RBX,
CpuIndex,
&RbxRegister);
Status = gEfiSmmCpuProtocol->ReadSaveState(gEfiSmmCpuProtocol, 4, EFI_SMM_SAVE_STATE_REGISTER_RCX, CpuIndex, &Buffer);
BiosSettingHeader = RbxRegister;
BiosSettingEntries = (RbxRegister + 16);
TurboBoostEnabled = IsTurboBoostEnabled();
ProgrammableTdpLimit = IsProgrammableTdpLimit();
ProgrammableTjOffset = IsProgrammableTjOffset();
if ( Buffer > 2 )
{
Value = 0x8004;
Status0 = gEfiSmmCpuProtocol->WriteSaveState(
gEfiSmmCpuProtocol,
4,
EFI_SMM_SAVE_STATE_REGISTER_RBX,
CpuIndex,
&Value);
Status = Status0;
return Status0;
}
if ( !Buffer )
{
Length = 0x110;
Count = 0x20;
if ( TurboBoostEnabled )
{
Length += 8;
++Count;
if ( ProgrammableTdpLimit )
{
Length += 32;
Count += 4;
}
if ( ProgrammableTjOffset )
{
Length += 16;
Count += 2;
if ( gCoreThreadCount > 2 )
{
Length += 16;
Count += 2;
}
}
}
if ( BiosSettingHeader->Signature != '2DB$' )
{
if ( BiosSettingHeader->Signature == '$DB$' )
{
// SMRAM write
BiosSettingHeader->Signature = '2DB$';
BiosSettingHeader->Length = Length;
BiosSettingHeader->MajorRev = 2;
BiosSettingHeader->MinorRev = 0;
Value = 1;
}
else
{
Value = 0x8001;
}
Status0 = gEfiSmmCpuProtocol->WriteSaveState(
gEfiSmmCpuProtocol,
4,
EFI_SMM_SAVE_STATE_REGISTER_RBX,
CpuIndex,
&Value);
Status = Status0;
return Status0;
}
if ( BiosSettingHeader->Length > 0xC00 )
{
Value = 0x8008;
Status0 = gEfiSmmCpuProtocol->WriteSaveState(
gEfiSmmCpuProtocol,
4,
EFI_SMM_SAVE_STATE_REGISTER_RBX,
CpuIndex,
&Value);
Status = Status0;
return Status0;
}
if ( BiosSettingHeader->Length <= Length )
{
if ( BiosSettingHeader->Length < 0xC )
{
Value = 32771;
Status0 = gEfiSmmCpuProtocol->WriteSaveState(
gEfiSmmCpuProtocol,
4,
EFI_SMM_SAVE_STATE_REGISTER_RBX,
CpuIndex,
&Value);
Status = Status0;
return Status0;
}
if ( BiosSettingHeader->Length < Length )
{
// SMRAM write
BiosSettingHeader->Length = Length;
BiosSettingHeader->MajorRev = 2;
BiosSettingHeader->MinorRev = 0;
Value = 32770;
Status0 = gEfiSmmCpuProtocol->WriteSaveState(
gEfiSmmCpuProtocol,
4,
EFI_SMM_SAVE_STATE_REGISTER_RBX,
CpuIndex,
&Value);
Status = Status0;
return Status0;
}
Value = 0;
Status = gEfiSmmCpuProtocol->WriteSaveState(
gEfiSmmCpuProtocol,
4u,
EFI_SMM_SAVE_STATE_REGISTER_RBX,
CpuIndex,
&Value);
// SMRAM write
BiosSettingHeader->MajorRev = 2;
BiosSettingHeader->MinorRev = 0;
}
else
{
// SMRAM write
BiosSettingHeader->Length = Length;
BiosSettingHeader->MajorRev = 2;
BiosSettingHeader->MinorRev = 0;
Value = 2;
Status = gEfiSmmCpuProtocol->WriteSaveState(
gEfiSmmCpuProtocol,
4,
EFI_SMM_SAVE_STATE_REGISTER_RBX,
CpuIndex,
&Value);
}
// SMRAM write
BiosSettingHeader->Count = Count;
// SMRAM write
BiosSettingEntries->BiosImplementationType = 0x29;
BiosSettingEntries->SettingValue = OcSetupData.EnableGv;
BiosSettingEntries[1].BiosImplementationType = 0;
BiosSettingEntries[1].SettingValue = OcSetupData.CpuRatio;
BiosSettingEntries[2].BiosImplementationType = 7;
BiosSettingEntries[2].SettingValue = OcSetupData.tCL;
BiosSettingEntries[3].BiosImplementationType = 8;
BiosSettingEntries[3].SettingValue = OcSetupData.tRCDtRP;
BiosSettingEntries[4].BiosImplementationType = 0xA;
BiosSettingEntries[4].SettingValue = OcSetupData.tRAS;
BiosSettingEntries[5].BiosImplementationType = 0xB;
if ( OcSetupData.tWR )
BiosSettingEntries[5].SettingValue = OcSetupData.tWR;
else
BiosSettingEntries[5].SettingValue = 0xFFFFFFFE;
BiosSettingEntries[6].BiosImplementationType = 0x15;
BiosSettingEntries[6].SettingValue = OcSetupData.tRFC;
BiosSettingEntries[7].BiosImplementationType = 0x16;
BiosSettingEntries[7].SettingValue = OcSetupData.tRRD;
BiosSettingEntries[8].BiosImplementationType = 0x17;
BiosSettingEntries[8].SettingValue = OcSetupData.tWTR;
BiosSettingEntries[9].BiosImplementationType = 0x19;
BiosSettingEntries[9].SettingValue = OcSetupData.tRTP;
BiosSettingEntries[10].BiosImplementationType = 0x28;
BiosSettingEntries[10].SettingValue = OcSetupData.tFAW;
BiosSettingEntries[11].BiosImplementationType = 0x18;
if ( OcSetupData.NModeSupport )
BiosSettingEntries[11].SettingValue = OcSetupData.NModeSupport;
else
BiosSettingEntries[11].SettingValue = 0xFFFFFFFE;
...
}
if...
if...
return Status;
}
As we can see from the pseudocode, the pointers to the BiosSettingHeader
and BiosSettingEntries
buffers (where BiosSettingEntries
is BiosSettingHeader + 16
) are controlled by the attacker as they are derived from the RBX
value obtained with gEfiSmmCpuProtocol->ReadSaveState
. These buffers are not validated to avoid overlapping with SMRAM.
The function includes multiple write operations to the controlled buffer:
// SMRAM write
BiosSettingHeader->Signature = '2DB$';
BiosSettingHeader->Length = Length;
BiosSettingHeader->MajorRev = 2;
BiosSettingHeader->MinorRev = 0;
...
// SMRAM write
BiosSettingHeader->Count = Count;
// SMRAM write
BiosSettingEntries->BiosImplementationType = 0x29;
BiosSettingEntries->SettingValue = OcSetupData.EnableGv;
BiosSettingEntries[1].BiosImplementationType = 0;
BiosSettingEntries[1].SettingValue = OcSetupData.CpuRatio;
BiosSettingEntries[2].BiosImplementationType = 7;
BiosSettingEntries[2].SettingValue = OcSetupData.tCL;
BiosSettingEntries[3].BiosImplementationType = 8;
BiosSettingEntries[3].SettingValue = OcSetupData.tRCDtRP;
BiosSettingEntries[4].BiosImplementationType = 0xA;
BiosSettingEntries[4].SettingValue = OcSetupData.tRAS;
BiosSettingEntries[5].BiosImplementationType = 0xB;
...
This allows an attacker to corrupt the SMRAM if BiosSettingHeader
(RBX
) points to SMRAM or just before SMRAM.
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