An attacker can exploit this vulnerability to elevate privileges from ring 0 to ring -2, execute arbitrary code in System Management Mode - an environment more privileged than operating system (OS) and completely isolated from it. Running arbitrary code in SMM additionally bypasses SMM-based SPI flash protections against modifications, which can help an attacker to install a firmware backdoor/implant into BIOS. Such malicious firmware code in BIOS could persist across operating system re-installs. Additionally, this vulnerability potentially could be used by threat actors to bypass security mechanisms provided by UEFI firmware (for example, Secure Boot and some types of memory isolation for hypervisors).
Binarly REsearch Team has discovered a heap buffer overflow vulnerability in a child SW SMI handler on multiple HP devices that allows corruption of heap metadata.
An attacker can exploit this vulnerability to elevate privileges from ring 0 to ring -2, execute arbitrary code in System Management Mode - an environment more privileged than operating system (OS) and completely isolated from it. Running arbitrary code in SMM additionally bypasses SMM-based SPI flash protections against modifications, which can help an attacker to install a firmware backdoor/implant into BIOS. Such malicious firmware code in BIOS could persist across operating system re-installs. Additionally, this vulnerability potentially could be used by threat actors to bypass security mechanisms provided by UEFI firmware (for example, Secure Boot and some types of memory isolation for hypervisors).
The vulnerability was found in the child SW SMI handler registered with GUID 3b46cda7-0bd6-4323-a03b-bdf94a023f0a
and located at offset 0x2158
in the driver.The pseudocode for this handler is shown below:
EFI_STATUS __fastcall SmiHandler_2158(
EFI_HANDLE DispatchHandle,
const void *Context,
_QWORD *CommBuffer,
UINTN *CommBufferSize)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
Num = 4;
if ( CommBuffer && CommBufferSize && (-(__int64)(*CommBufferSize != 24) & EFI_INVALID_PARAMETER) == 0 )
{
Size = GetStrSize((_WORD *)CommBuffer[1]);
CommBuffer1 = CommBuffer[1];
Size1 = Size;
Res = 0;
if ( CommBuffer1 && Size1 )
Res = SmmIsBufferOutsideSmmValid(CommBuffer1, Size1);
if ( (-(__int64)(Res == 0) & EFI_INVALID_PARAMETER) == 0 )
{
if ( *(_BYTE *)CommBuffer )
{
if ( *(_BYTE *)CommBuffer == 1 )
{
if ( GetWideStrSize((_WORD *)CommBuffer[1]) )
{
Res1 = FactoryConfigHandler((_WORD *)CommBuffer[1], (__int64)&Num, 1);// possible heap overflow
goto _Exit;
}
}
else if ( *(_BYTE *)CommBuffer == 2 )
{
Res1 = BuildIdHandler((_WORD *)CommBuffer[1], (__int64)&Num, 1);// possible heap overflow
_Exit:
CommBuffer[2] = Res1;
return 0;
}
}
else if ( GetWideStrSize((_WORD *)CommBuffer[1]) )
{
CommBuffer[2] = Validate((_WORD *)CommBuffer[1]) ? 0 : EFI_CRC_ERROR;
return 0;
}
CommBuffer[2] = EFI_INVALID_PARAMETER;
}
}
return 0;
}
If the initial checks pass, the following happens in this handler:
CommBuffer
is 1
and a pointer to a wide string is located at offset CommBuffer + 8
, then function FactoryConfigHandler
(offset: 0x1A60
) will be calledCommBuffer
is 2
and a pointer to a wide string is located at offset CommBuffer + 8
, then function BuildIdHandler
(offset: 0x1C88
) will be calledConsider the FactoryConfigHandler
function.
Status = SmmGetVariable(L"FactoryConfig", &VariableValue);
if ( Stautus )
goto _Exit;
if ( GetWideStrSize(StringFromCommBufferPtr) )
{
StrSize = GetStrSize(StringFromCommBufferPtr);
Value = VariableValue;
if ( VariableValue && StrSize )
{
if ( StrSize > 500 )
StrSize = 500;
if ( VariableValue != StringFromCommBufferPtr )
CopyMem(VariableValue, StringFromCommBufferPtr, StrSize);
}
...
}
SmmGetVariable
function will do the following:
FactoryConfig
NVRAM variablegSmst->SmmAllocatePool(EfiRuntimeServicesData, Size, &Buffer)
(inside SmmAllocatePool
, offset: 0x2F98
)FactoryConfig
NVRAM variable to the allocated bufferVariableValue
variable will contain the address of the buffer in the heap containing the value of the FactoryConfig
variableFactoryConfig
NVRAM variableCopyMem
will copy the data at the address that an attacker can control into VariableValue
. In this case, the size of the data copied is equal to the size of the string located at the controlled address in CommBuffer (but not more than 500
).
In other words, attacker control both buffers:* the size of the destination buffer* the size and data of the source buffer
This will lead to a heap buffer overflow.
To exploit this vulnerability it is enough to:
FactoryConfig
NVRAM variable >= 500, set the value of this variable so that its size is less than 50002 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
0xB2
IO port.This function has the same vulnerability.But the allocated buffer will contain the value of the BuildId
variable, and the maximum data size that can be written in the allocated buffer is 112
.
To exploit this vulnerability it is enough to:
BuildId
NVRAM variable >= 112, set the value of this variable so that its size is less than 11201 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
0xB2
IO portThis bug is subject to a 90 day disclosure deadline. After 90 days elapsed or a patch has been made broadly available (whichever is earlier), the bug report will become visible to the public.
Binarly REsearch Team