An attacker with local privileged access can exploit this vulnerability to elevate privileges from ring 3 or ring 0 (depends on the operating system) to DXE Runtime UEFI application and execute arbitrary code.A malicious code installed as a result of vulnerability exploitation in UEFI application 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 malicious actors to bypass OS security mechanisms (modify privileged memory or runtime variables), influence on OS boot process, and in some cases would allow an attacker to hook or modify EFI Runtime services.
Binarly REsearch Team has discovered a stack overflow vulnerability that allows a local root user to access UEFI Runtime DXE application and execute arbitrary code.
An attacker with local privileged access can exploit this vulnerability to elevate privileges from ring 3 or ring 0 (depends on the operating system) to DXE Runtime UEFI application and execute arbitrary code.A malicious code installed as a result of vulnerability exploitation in UEFI application 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 malicious actors to bypass OS security mechanisms (modify privileged memory or runtime variables), influence on OS boot process, and in some cases would allow an attacker to hook or modify EFI Runtime services.
The vulnerabile code below is located at offset 0x7B68
(firmware name: S05_02020000.bin
, application name: 0138
):
bool CheckBatterySafety()
{
EFI_GUID VendorGuid;
char Buffer;
UINTN DataSize;
VendorGuid.Data1 = 0xFB3B9ECE;
*&VendorGuid.Data2 = 0x49334ABA;
*VendorGuid.Data4 = 0xD6B49DB4;
*&VendorGuid.Data4[4] = 0x5123897D;
Buffer = 0;
DataSize = 1i64;
return (!gRT_157B30->GetVariable(L"BatterySafetyModeStatus", &VendorGuid, 0i64, &DataSize, &Buffer)
|| !gRT_157B30->GetVariable(L"BatterySafetyMode", &VendorGuid, 0i64, &DataSize, &Buffer))
&& Buffer == 1;
}
If the length of NVRAM variable BatterySafetyModeStatus
is greater than 1, the DataSize
is set to the length of BatterySafetyModeStatus
variable and a second call to GetVariable
service will occur.
After the second call to GetVariable
service, the value of NVRAM variable BatterySafetyMode
will be written to the Buffer
stack variable.
By controlling the values of BatterySafetyModeStatus
and BatterySafetyMode
variables, we can write any data onto the stack and thus execute arbitrary code on the stack.
Change BatterySafetyModeStatus
and BatterySafetyMode
variables.
By changing the value of BatterySafetyModeStatus
variable, we change the value of the DataSize
before the second call to GetVariable
.
By changing the value of BatterySafetyMode
variable, we overflow the stack and execute arbitrary code on the stack.
It is important to note that the Buffer
value on the stack lies after the current function return address:
-0000000000000010 VendorGuid EFI_GUID ?
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)
+0000000000000010 Buffer db ?
+0000000000000011 db ? ; undefined
+0000000000000012 db ? ; undefined
+0000000000000013 db ? ; undefined
+0000000000000014 db ? ; undefined
+0000000000000015 db ? ; undefined
+0000000000000016 db ? ; undefined
+0000000000000017 db ? ; undefined
+0000000000000018 DataSize dq ?
+0000000000000020
+0000000000000020 ; end of stack variables
So, it is necessary to overwrite the return address of the parent function and not the return address of the current function.
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