An attacker with local privileged access can exploit this vulnerability to read the contents of the physical memory and use this information to exploit other vulnerabilities in DXE. A malicious code installed as a result of the vulnerability exploitation in a DXE driver could survive across an operating system (OS) boot process and runtime or modify NVRAM area on SPI flash storage (to gain persistence on target platform). Additionally, this vulnerability potentially could be used by threat actors to bypass OS security mechanisms (modify privileged memory or runtime variables), influence on the OS boot process, and in some cases would allow an attacker to hook or modify EFI Runtime services.
This vulnerability was detected by the Deep Vulnerability Analysis (DVA) component from Binarly Platform
Binarly REsearch Team has discovered a SMM memory corruption vulnerability in a Fujitsu device allowing a possible attacker to write fixed or predictable data to SMRAM. Exploiting this issue could lead to escalating privileges to SMM.
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 a malicious firmware code in BIOS could persist across operating system re-installs. Additionally, this vulnerability potentially could be used by malicious actors to bypass security mechanisms provided by UEFI firmware (for example, Secure Boot and some types of memory isolation for hypervisors).
The pseudocode of the vulnerable ChildSwSmiHandler
function (with the HandlerType
: EFI_HDD_PASSWORD_SERVICE_PROTOCOL_GUID
) is presented below:
EFI_STATUS __fastcall ChildSwSmiHandler(
EFI_HANDLE DispatchHandle,
const void *Context,
_QWORD *CommBuffer,
UINTN *CommBufferSize)
{
char *WpdhStructure; // rbp
EFI_STATUS Status; // rbx
_QWORD *Ptr2; // rcx
_QWORD *i; // rax
EFI_HANDLE *v9; // rdi
EFI_HANDLE *Buffer; // rax
UINTN v11; // r12
UINTN v12; // r13
_QWORD *Address; // r14
__int64 v14; // r15
EFI_DEVICE_PATH_PROTOCOL *v15; // rbx
__int64 Ptr1; // [rsp+30h] [rbp-48h]
EFI_DEVICE_PATH_PROTOCOL *EfiDevicePathProtocol; // [rsp+38h] [rbp-40h] BYREF
void *EfiStorageSecurityCommandProtocol; // [rsp+40h] [rbp-38h] BYREF
UINTN BufferSize; // [rsp+90h] [rbp+18h] BYREF
if ( !CommBuffer || !CommBufferSize )
return EFI_SUCCESS;
if ( CommBuffer != (gBuffer + 24) )
return EFI_ACCESS_DENIED;
WpdhStructure = (CommBuffer + 2);
if ( *CommBuffer == 1 )
goto _CheckSize;
if ( *CommBuffer != 2 )
{
if ( *CommBuffer != 3 )
{
if ( *CommBuffer == 4 )
{
if ( *CommBufferSize == 17 )
{
gFlag = *WpdhStructure;
goto _ExitSuccess;
}
return EFI_ACCESS_DENIED;
}
_Unsupported:
Status = EFI_UNSUPPORTED;
goto _Exit;
}
_CheckSize:
if ( *CommBufferSize != 248 )
return EFI_ACCESS_DENIED;
Ptr2 = (CommBuffer[22] + 8);
for ( i = *Ptr2; i != Ptr2; i = *i )
{
i[24] = 0; // unchecked write (SMRAM corruption)
i[4] = 0; // unchecked write (SMRAM corruption)
i[6] = 0; // unchecked write (SMRAM corruption)
}
if ( *WpdhStructure == 'wpdh' )
{
Ptr1 = CommBuffer[22];
if ( !Ptr1 )
{
Status = EFI_NOT_READY;
goto _Exit;
}
BufferSize = 0;
v9 = 0;
Status = gSmst_0->SmmLocateHandle(
ByProtocol,
&EFI_STORAGE_SECURITY_COMMAND_PROTOCOL_GUID,
0,
&BufferSize,
0);
if ( Status == EFI_BUFFER_TOO_SMALL )
{
Buffer = AllocatePool(6);
v9 = Buffer;
if ( !Buffer )
{
Status = EFI_OUT_OF_RESOURCES;
goto _Exit;
}
Status = gSmst_0->SmmLocateHandle(
ByProtocol,
&EFI_STORAGE_SECURITY_COMMAND_PROTOCOL_GUID,
0,
&BufferSize,
Buffer);
}
if ( Status )
{
if ( v9 )
Free(v9);
}
else if ( v9 )
{
v11 = 0;
v12 = BufferSize >> 3;
if ( BufferSize >> 3 )
{
do
{
Status = gSmst_0->SmmHandleProtocol(
v9[v11],
&EFI_STORAGE_SECURITY_COMMAND_PROTOCOL_GUID,
&EfiStorageSecurityCommandProtocol);
if ( !Status )
{
Status = gSmst_0->SmmHandleProtocol(v9[v11], &EFI_DEVICE_PATH_PROTOCOL_GUID, &EfiDevicePathProtocol);
if ( !Status )
{
Address = *(Ptr1 + 8);
if ( Address != (Ptr1 + 8) )
{
while ( 2 )
{
v14 = Address[57];
v15 = EfiDevicePathProtocol;
while ( *v14 != 127 || *(v14 + 1) != 0xFF )
{
if ( v15->Type == 127 && v15->SubType == 0xFF )
{
if ( *v14 != 127 || *(v14 + 1) != 0xFF )
goto _Continue;
break;
}
if ( *(v14 + 2) != *v15->Length || *v15->Length && v14 != v15 && sub_1060(v14, v15, *v15->Length) )
goto _Continue;
v14 += *(v14 + 2);
v15 = (v15 + *v15->Length);
}
if ( v15->Type == 127 && v15->SubType == 0xFF )
{
Status = 0;
Address[24] = v9[v11]; // unchecked write (SMRAM corruption)
Address[4] = v9[v11]; // unchecked write (SMRAM corruption)
Address[6] = EfiStorageSecurityCommandProtocol;// unchecked write (SMRAM corruption)
goto _Next;
}
_Continue:
Address = *Address;
if ( Address != (Ptr1 + 8) )
continue;
break;
}
Status = EFI_ABORTED;
}
}
}
_Next:
++v11;
}
while ( v11 < v12 );
WpdhStructure = (CommBuffer + 2);
}
Free(v9);
if ( *CommBuffer == 1 || !gWpdhBuffer )
sub_5B7C(WpdhStructure); // unchecked write (SMRAM corruption) inside this function
goto _Exit;
}
}
goto _Unsupported;
}
if ( *CommBufferSize == 16 )
{
if ( !gAcpiRestoreRegistration )
{
Status = gSmst_0->SmmRegisterProtocolNotify(
&ACPI_RESTORE_CALLBACK_START_PROTOCOL_GUID,
AcpiRestoreNotifier,
&gAcpiRestoreRegistration);
goto _Exit;
}
_ExitSuccess:
Status = 0;
_Exit:
CommBuffer[1] = Status;
return 0;
}
return EFI_ACCESS_DENIED;
}
As we can see from the comments in the pseudocode, the function contains many unchecked write operatons to the controlled pointer.
Let's break them down in more detail:
Ptr2 = (CommBuffer[22] + 8);
for ( i = *Ptr2; i != Ptr2; i = *i )
{
i[24] = 0; // unchecked write (SMRAM corruption)
i[4] = 0; // unchecked write (SMRAM corruption)
i[6] = 0; // unchecked write (SMRAM corruption)
}
2. The code snippet below contains multiple write operations to a buffer that may overlap with SMRAM since Ptr1 is controllable by an attacker:
Ptr1 = CommBuffer[22];
...
Address = *(Ptr1 + 8);
...
Address[24] = v9[v11]; // unchecked write (SMRAM corruption)
Address[4] = v9[v11]; // unchecked write (SMRAM corruption)
Address[6] = EfiStorageSecurityCommandProtocol;// unchecked write (SMRAM corruption)
3. The function below contains multiple write operations to a buffer that may overlap with SMRAM since WpdhStructure pointer is controllable by an attacker (when consider the case with gWpdhBuffer
= NULL
)
__int64 __fastcall sub_5B7C(__int64 WpdhStructure)
{
__int64 v1; // rax
__int64 v2; // rdi
__int64 CtrlPtr1; // rdx
__int64 CtrlPtr2; // rcx
__int64 result; // rax
__int64 v7; // rcx
EFI_HANDLE Handle; // [rsp+30h] [rbp+8h] BYREF
v1 = *(WpdhStructure + 160);
v2 = 0;
if ( !v1 || !*(WpdhStructure + 184) )
return EFI_NOT_READY;
CtrlPtr1 = gWpdhBuffer;
if ( !gWpdhBuffer )
{
CtrlPtr2 = *(WpdhStructure + 176);
CtrlPtr1 = *(CtrlPtr2 + 16);
*(CtrlPtr2 + 24) -= 232;
gWpdhBuffer = CtrlPtr1;
*(CtrlPtr2 + 16) = CtrlPtr1 + 232;
*(CtrlPtr1 + 176) = CtrlPtr2;
*CtrlPtr1 = 'wpdh';
*(CtrlPtr1 + 160) = *(WpdhStructure + 160);
v1 = *(WpdhStructure + 160);
}
*(CtrlPtr1 + 160) = v1;
*(CtrlPtr1 + 169) = 1;
*(CtrlPtr1 + 8) = sub_3D70;
*(CtrlPtr1 + 184) = 1;
*(CtrlPtr1 + 16) = sub_3DBC;
*(CtrlPtr1 + 24) = sub_3E04;
*(CtrlPtr1 + 32) = sub_30E0;
*(CtrlPtr1 + 40) = sub_2B8C;
*(CtrlPtr1 + 48) = sub_3E4C;
*(CtrlPtr1 + 56) = sub_3350;
*(CtrlPtr1 + 64) = sub_3A3C;
*(CtrlPtr1 + 72) = sub_4170;
*(CtrlPtr1 + 80) = sub_2B00;
*(CtrlPtr1 + 88) = sub_439C;
*(CtrlPtr1 + 186) = *(WpdhStructure + 186);
*(CtrlPtr1 + 96) = sub_6BC4;
*(CtrlPtr1 + 104) = sub_6C90;
*(CtrlPtr1 + 112) = sub_6CB8;
*(CtrlPtr1 + 120) = sub_793C;
*(CtrlPtr1 + 128) = sub_7B68;
*(CtrlPtr1 + 136) = sub_7C80;
*(CtrlPtr1 + 144) = sub_7D80;
*(CtrlPtr1 + 152) = sub_8380;
Handle = 0;
result = gSmst_0->SmmInstallProtocolInterface(
&Handle,
&EFI_HDD_PASSWORD_SERVICE_PROTOCOL_GUID,
EFI_NATIVE_INTERFACE,
(CtrlPtr1 + 8));
if ( result >= 0 )
{
*(gWpdhBuffer + 224) = *(WpdhStructure + 224);
sub_7F14();
v7 = gWpdhBuffer;
*(gWpdhBuffer + 200) = *(WpdhStructure + 200);
*(v7 + 208) = *(WpdhStructure + 208);
*(v7 + 216) = *(WpdhStructure + 216);
sub_6DB0(v7 + 8, 1);
if ( !gAcpiRestoreRegistration )
return gSmst_0->SmmRegisterProtocolNotify(
&ACPI_RESTORE_CALLBACK_START_PROTOCOL_GUID,
AcpiRestoreNotifier,
&gAcpiRestoreRegistration);
return v2;
}
return result;
}
In order to fix this vulnerability, it is necessary to check all nested pointers for overlapping with SMRAM before attempting to write to buffer pointed by it.
This 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