An attacker with high local access can exploit this vulnerability to read the contents of stack memory or global memory. This information could help with exploitation of other vulnerabilities in DXE to elevate privileges from ring 3 or ring 0 (depends on the operating system) to a DXE driver and execute arbitrary code. Malicious code installed as a result of this exploitation could survive operating system (OS) boot process and runtime, or modify NVRAM area on the SPI flash storage (to gain persistence). Additionally, threat actors could use this vulnerability to bypass OS security mechanisms (modify privileged memory or runtime variables), influence OS boot process, and in some cases allow an attacker to hook or modify EFI Runtime services.
Binarly REsearch Team has discovered a memory contents leak / information disclosure vulnerability that allows a potential attacker to dump stack memory or global memory into an NVRAM variable. This in turn could help building a successful attack vector based on exploiting a memory corruption vulnerability.
An attacker with high local access can exploit this vulnerability to read the contents of stack memory or global memory. This information could help with explotation of other vulnerabilities in DXE to elevate privileges from ring 3 or ring 0 (depends on the operating system) to a DXE driver and execute arbitrary code. Malicious code installed as a result of this exploitation could survive operating system (OS) boot process and runtime, or modify NVRAM area on the SPI flash storage (to gain persistence). Additionally, threat actors could use this vulnerability to bypass OS security mechanisms (modify privileged memory or runtime variables), influence OS boot process, and in some cases allow an attacker to hook or modify EFI Runtime services.
Let's take Latitude 3190 2-in-1's firmware (version: 0.1.23.0, module sha256: b1be42f188d0770079bc38d00d082de423a8327cf94b69715ade9bdbda7fd8e4) as an example.
The following code in the module actually allows leaking memory:
gRT->GetVariable()
offset: 0x5ca
gRT->SetVariable()
offset: 0x5fd
unsigned __int64 __fastcall sub_4AC(unsigned __int64 a1)
{
char *v2; // r12
char *v3; // rbp
void *v4; // rax
void *v6; // rax
_DWORD *v7; // rax
_DWORD *v8; // rbx
char *v9; // rax
char v10; // dl
__int64 v11; // rcx
__int64 v12; // r9
__int64 v13; // rax
__int64 v14; // r8
char *v15; // rax
char *v16; // rsi
char *v17; // rax
char *v18; // rax
unsigned __int64 v19; // rbx
unsigned __int64 v20; // rdx
unsigned __int64 v21; // r9
unsigned __int64 v22; // r8
char *v23; // rcx
char *v24; // rcx
char *v25; // rcx
char *v26; // rcx
int v27; // eax
unsigned int v28; // eax
unsigned __int64 v29; // rbx
unsigned __int64 v30; // rbp
unsigned __int8 v31; // di
__int64 v32; // rax
char v33; // cl
unsigned __int64 v34; // rax
__int64 v35; // rcx
void *v36; // rcx
void *v37; // rdx
char *v38; // rdi
__int64 v39; // rbx
__int64 v40; // rbx
unsigned int v41; // ecx
_DWORD *v42; // rdx
char v43[8]; // [rsp+30h] [rbp-B78h] BYREF
int v44; // [rsp+38h] [rbp-B70h] BYREF
__int64 v45; // [rsp+40h] [rbp-B68h] BYREF
unsigned __int64 v46; // [rsp+48h] [rbp-B60h] BYREF
unsigned __int64 v47; // [rsp+50h] [rbp-B58h] BYREF
unsigned __int64 v48; // [rsp+58h] [rbp-B50h] BYREF
int v49; // [rsp+60h] [rbp-B48h] BYREF
__int16 v50; // [rsp+64h] [rbp-B44h]
__int16 v51; // [rsp+66h] [rbp-B42h]
char v52; // [rsp+68h] [rbp-B40h]
char v53; // [rsp+69h] [rbp-B3Fh]
char v54; // [rsp+6Ah] [rbp-B3Eh]
char v55; // [rsp+6Bh] [rbp-B3Dh]
char v56; // [rsp+6Ch] [rbp-B3Ch]
char v57; // [rsp+6Dh] [rbp-B3Bh]
char v58; // [rsp+6Eh] [rbp-B3Ah]
char v59; // [rsp+6Fh] [rbp-B39h]
int v60; // [rsp+70h] [rbp-B38h] BYREF
__int16 v61; // [rsp+74h] [rbp-B34h]
__int16 v62; // [rsp+76h] [rbp-B32h]
char v63; // [rsp+78h] [rbp-B30h]
char v64; // [rsp+79h] [rbp-B2Fh]
char v65; // [rsp+7Ah] [rbp-B2Eh]
char v66; // [rsp+7Bh] [rbp-B2Dh]
char v67; // [rsp+7Ch] [rbp-B2Ch]
char v68[11]; // [rsp+7Dh] [rbp-B2Bh] BYREF
int v69; // [rsp+88h] [rbp-B20h]
int v70; // [rsp+8Ch] [rbp-B1Ch]
int v71; // [rsp+90h] [rbp-B18h]
__int64 v72; // [rsp+98h] [rbp-B10h] BYREF
__int64 v73; // [rsp+A0h] [rbp-B08h] BYREF
__int64 v74; // [rsp+A8h] [rbp-B00h] BYREF
__int64 (__fastcall **v75)(void *, __int64, _DWORD *); // [rsp+B0h] [rbp-AF8h] BYREF
__int64 v76; // [rsp+B8h] [rbp-AF0h] BYREF
__int64 v77; // [rsp+C0h] [rbp-AE8h] BYREF
char v78; // [rsp+C8h] [rbp-AE0h]
_BYTE v79[2760]; // [rsp+E0h] [rbp-AC8h] BYREF
__int64 v80; // [rsp+BB8h] [rbp+10h] BYREF
char v81; // [rsp+BC0h] [rbp+18h] BYREF
char v82; // [rsp+BC8h] [rbp+20h] BYREF
v60 = -1267788995;
v63 = -97;
v64 = 3;
v65 = -79;
v66 = -64;
v67 = 28;
strcpy(v68, "Tx[");
v50 = -25766;
v51 = 20151;
v49 = 1093152498;
v61 = 9055;
v52 = -107;
v62 = 17972;
v53 = -40;
v54 = -39;
v68[4] = 0;
*&v68[5] = 0;
*&v68[7] = 0;
v69 = 0;
v70 = 0;
v71 = 0;
v55 = -51;
v56 = 123;
v57 = -36;
v58 = -29;
v59 = 103;
v2 = 0i64;
v3 = 0i64;
if ( !byte_58E0 )
{
byte_58E0 = 1;
v45 = 4i64;
if ( (gRT->GetVariable)(L"SLP20Magic", &v49, 0i64, &v45, &v44) < 0 )
{
v44 = 1;
(gRT->SetVariable)( // <= second call
L"SLP20Magic",
&v49,
7i64,
v45,
&v44);
}
v45 = 156i64;
v4 = sub_3C1C(4i64, 156i64);
Buffer = v4;
if ( !v4 )
return 0x8000000000000009ui64;
if ( (gRT->GetVariable)(L"SLP20OEMPublicKey", &OA2_OEM_PUBLIC_KEY_VARIABLE_GUID, 0i64, &v45, v4) >= 0 )
{
v45 = 182i64;
v6 = sub_3C1C(4i64, 182i64);
qword_58F0 = v6;
if ( !v6 )
return 0x8000000000000009ui64;
if ( (gRT->GetVariable)(L"SLP20Marker", &OA2_MARKER_VARIABLE_GUID, 0i64, &v45, v6) >= 0 )
{
v45 = 284i64;
v7 = sub_3C1C(4i64, 284i64);
v8 = v7;
if ( !v7 )
return 0x8000000000000009ui64;
if ( (gRT->GetVariable)(
L"SLP20EncryptedOEMPublicKey",
&OA2_ENCRYPTED_OEM_PUBLIC_KEY_VARIBLE_GUID,
0i64,
&v45,
v7) >= 0
&& (*v8 == -1853721756
|| (gBS->LocateProtocol)(&EFI_DPSD_R_S_A1024_AND_S_H_A256_SIGNATURE_VERIFICATION_PROTOCOL_GUID, 0i64, &v75) >= 0
&& (*v75)(Buffer, 156i64, v8) >= 0) )
{
v9 = qword_58F0;
byte_58E1 = 1;
if ( &qword_59C0 != (qword_58F0 + 12) )
{
sub_4A20(&qword_59C0, qword_58F0 + 12, 6ui64);
v9 = qword_58F0;
}
qword_59C8 = *(v9 + 18);
v45 = 4i64;
v44 = -1853721756;
(gRT->SetVariable)(
L"SLP20EncryptedOEMPublicKey#!rtUY9o",
&OA2_ENCRYPTED_OEM_PUBLIC_KEY_VARIBLE_GUID,
7i64,
4i64,
&v44);
}
(gBS->FreePool)(v8);
}
}
}
v10 = byte_58E1;
v11 = qword_6480;
v12 = 1413763923i64;
if ( *a1 == 1396916550 )
{
v14 = v80;
}
else
{
v13 = *(qword_6480 + 149);
v14 = a1;
if ( byte_58E1 )
v13 = qword_59C0;
*(a1 + 10) = v13;
*(a1 + 14) = WORD2(v13);
if ( *a1 != 1413763923 )
*(a1 + 16) = *(v11 + 157);
if ( v10 && *a1 != 1413763923 )
*(a1 + 16) = qword_59C8;
*(a1 + 24) = 3;
*(a1 + 28) = 1415074370;
*(a1 + 32) = 16777229;
}
if ( *a1 > 0x54445344u )
{
if ( *a1 == 1413763923 )
{
if ( *(v14 + 16) == 0x62615466747044i64 )
{
v76 = 2701i64;
(gRT->GetVariable)(L"Setup", &EFI_SETUP_VARIABLE_GUID, &v73, &v76, v79);
if ( v79[1491] == 1 && a1 <= a1 + *(a1 + 4) )
{
v42 = (a1 + 1);
while ( *v42 != 827346260 )
{
v42 = (v42 + 1);
if ( v42 - 1 > a1 + *(a1 + 4) )
return 0i64;
}
*v42 = 1347245151;
}
}
return 0i64;
}
if ( *a1 != 1413828680 )
{
if ( *a1 == 1414221902 )
{
if ( MEMORY[0xE0002000] != -1 )
sub_39E4(a1);
}
else if ( *a1 == 1414353751 )
{
*(a1 + 36) = 7;
}
return 0i64;
}
if ( byte_5B25 )
{
MEMORY[0xFED00010] |= 1u;
(gBS->LocateProtocol)(&EFI_CPU_IO2_PROTOCOL_GUID, 0i64, &v72, 1413763923i64);
v39 = v72;
LOBYTE(v80) = 125;
(*(v72 + 24))(v72, 0i64, 114i64, 1i64, &v80);
(*(v39 + 16))(v39, 0i64, 115i64, 1i64, &v81);
v40 = v72;
v43[0] = v81 | 1;
v82 = 125;
(*(v72 + 24))(v72, 0i64, 114i64, 1i64, &v82);
(*(v40 + 24))(v40, 0i64, 115i64, 1i64, v43);
*(a1 + 44) = 4275044352i64;
*(a1 + 36) = MEMORY[0xFED00000];
v41 = -2138660351;
if ( (MEMORY[0xFED00000] & 0x1F00) != 0 )
v41 = MEMORY[0xFED00000] & 0x1F00 | 0x8086A201;
*(a1 + 36) = v41;
return 0i64;
}
return 0x8000000000000003ui64;
}
switch ( *a1 )
{
case 0x54445344:
sub_3A98(a1);
return 0i64;
case 0x43494C53:
if ( v10 )
{
v36 = Buffer;
if ( (a1 + 36) != Buffer )
{
sub_4A20((a1 + 36), Buffer, 0x9Cui64);
v36 = Buffer;
}
v37 = qword_58F0;
v38 = (a1 + 192);
if ( v38 != qword_58F0 )
{
sub_4A20(v38, qword_58F0, 0xB6ui64);
v36 = Buffer;
}
(gBS->FreePool)(v36, v37, v14, v12);
v26 = qword_58F0;
goto LABEL_98;
}
return 0x8000000000000003ui64;
case 0x43495041:
(*(gEfiPiMpServicesProtocol + 6))(gEfiPiMpServicesProtocol, &v74, v14, 1413763923i64);
v29 = a1 + 44;
v30 = a1 + *(a1 + 4);
if ( a1 + 44 < v30 )
{
do
{
if ( *v29 )
{
if ( *v29 == 1 )
{
MEMORY[0xFEC00000] = 0;
*(v29 + 2) = MEMORY[0xFEC00013];
}
}
else
{
v45 = 0i64;
*(v29 + 4) = 0;
v31 = 0;
if ( qword_6470 )
{
v32 = 0i64;
while ( (*(gEfiPiMpServicesProtocol + 1))(gEfiPiMpServicesProtocol, v32, &v77) || v77 != *(v29 + 3) )
{
v32 = ++v31;
if ( v31 >= qword_6470 )
goto LABEL_90;
}
if ( v74 == v31 || (v78 & 2) != 0 )
{
v33 = *(v29 + 3);
*(v29 + 4) = 1;
v34 = 0i64;
while ( *(&unk_59B0 + v34 + 1) != v33 || !*(&unk_59B0 + v34 + 2) )
{
v34 += 4i64;
if ( v34 >= 0x10 )
{
v35 = 0i64;
while ( *(&unk_59B0 + 2 * v35 + 1) )
{
if ( ++v35 >= 4 )
goto LABEL_90;
}
*(&unk_59B0 + 2 * v35 + 1) = 1;
*(&unk_59B0 + 4 * v35 + 1) = *(v29 + 3);
*(&unk_59B0 + 4 * v35) = *(v29 + 2);
break;
}
}
}
}
}
LABEL_90:
v29 += *(v29 + 1);
}
while ( v29 < v30 );
}
return 0i64;
case 0x4746434D:
*(a1 + 44) = *(v11 + 46);
*(a1 + 55) = (*(v11 + 54) >> 20) - 1;
break;
case 0x50434146:
*(a1 + 112) |= 0x400u;
v27 = *(a1 + 112);
if ( !byte_5E9E || !byte_5E9F )
*(a1 + 109) |= 0x10u;
if ( byte_5EA0 )
v28 = v27 | 0x200000;
else
v28 = v27 & 0xFFDFFFFF;
*(a1 + 112) = v28;
if ( byte_5E90 == 3 )
*(a1 + 112) &= ~0x10u;
else
*(a1 + 112) |= 0x10u;
return 0i64;
case 0x5246534F:
v46 = 0i64;
if ( (gRT->GetVariable)(L"OcurMfg", &ACPI_OSFR_REF_DATA_BLOCK_VARIABLE_GUID, 0i64, &v46, 0i64) == 0x8000000000000005ui64 )
{
v15 = sub_3C1C(4i64, v46);
v16 = v15;
if ( !v15 )
return 0x8000000000000009ui64;
if ( (gRT->GetVariable)(L"OcurMfg", &ACPI_OSFR_REF_DATA_BLOCK_VARIABLE_GUID, 0i64, &v46, v15) < 0 )
goto LABEL_58;
v48 = 0i64;
if ( (gRT->GetVariable)(L"OcurModel", &ACPI_OSFR_REF_DATA_BLOCK_VARIABLE_GUID_1, 0i64, &v48, 0i64) == 0x8000000000000005ui64 )
{
v17 = sub_3C1C(4i64, v48);
v2 = v17;
if ( !v17 )
return 0x8000000000000009ui64;
if ( (gRT->GetVariable)(L"OcurModel", &ACPI_OSFR_REF_DATA_BLOCK_VARIABLE_GUID_1, 0i64, &v48, v17) >= 0 )
{
v47 = 0i64;
if ( (gRT->GetVariable)(L"OcurRef", &ACPI_OSFR_REF_DATA_BLOCK_VARIABLE_GUID_0, 0i64, &v47, 0i64) != 0x8000000000000005ui64 )
{
LABEL_44:
v19 = a1 + 44;
*(a1 + 36) = 1;
*(a1 + 4) = 40;
*(a1 + 40) = 44;
if ( (a1 + 44) != &v60 )
sub_4A20((a1 + 44), &v60, 0x24ui64);
v20 = v46;
v21 = v47;
v22 = v48;
*(a1 + 64) = 80;
*(a1 + 68) = v20 + 80;
if ( v21 )
*(a1 + 76) = v20 + v22 + 80;
v23 = (a1 + 80);
if ( v20 && v23 != v16 )
{
sub_4A20(v23, v16, v20);
v20 = v46;
v22 = v48;
v21 = v47;
}
v24 = (v19 + v20 + 36);
if ( v22 && v24 != v2 )
{
sub_4A20(v24, v2, v22);
v20 = v46;
v22 = v48;
v21 = v47;
}
if ( v21 )
{
v25 = (v19 + v22 + v20 + 36);
if ( v25 != v3 )
{
sub_4A20(v25, v3, v21);
LODWORD(v20) = v46;
LODWORD(v22) = v48;
LODWORD(v21) = v47;
}
}
*(a1 + 4) += v21 + v22 + v20 + 40;
goto LABEL_58;
}
v18 = sub_3C1C(4i64, v47);
v3 = v18;
if ( v18 )
{
(gRT->GetVariable)(L"OcurRef", &ACPI_OSFR_REF_DATA_BLOCK_VARIABLE_GUID_0, 0i64, &v47, v18);
goto LABEL_44;
}
return 0x8000000000000009ui64;
}
LABEL_58:
(gBS->FreePool)(v16);
(gBS->FreePool)(v2);
v26 = v3;
LABEL_98:
(gBS->FreePool)(v26);
return 0i64;
}
}
return 0x8000000000000003ui64;
}
return 0i64;
}
The gRT->SetVariable()
service is called with the DataSize
as an argument, which will be overwritten inside the gRT->GetVariable()
service if the length of SLP20Magic
NVRAM variable is greater than ``.
Thus, a potential attacker can dump X -
bytes from the stack (or global memory) into SLP20Magic NVRAM variable by setting SLP20Magic
NVRAM variable's size to X >
.
To fix this vulnerability the DataSize
must be re-initialized with the size of SLP20Magic
before calling gRT->SetVariable()
.
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