Header bannerHeader banner

[BRLY-2022-160] Memory contents leak / information disclosure vulnerability in DXE driver on Dell platform.

June 22, 2023

Summary

BINARLY efiXplorer 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.

Vulnerability Information

  • BINARLY internal vulnerability identifier: BRLY-2022-160
  • Dell PSIRT assigned CVE identifier: CVE-2023-28052
  • DSA identifier: DSA-2023-099
  • CVSS v3.1: 6.0 Medium AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:N/A:N

Affected Dell firmware with confirmed impact by Binarly team

Product Firmware version CPU Module name Module GUID Module SHA256
Latitude 3190 2-in-1 0.1.23.0 Intel AcpiPlatform 1dffe9f3-7b5f-4b44-8ebd-39a739eba903 b1be42f188d0770079bc38d00d082de423a8327cf94b69715ade9bdbda7fd8e4
Wyse 5070 Thin Client 0.1.19.1 Intel AcpiPlatform 1dffe9f3-7b5f-4b44-8ebd-39a739eba903 f572e3eb75853734c4dce58fb0b8be5b10112bdafe823891d4cc2dc687e8ab87

Potential impact

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.

Vulnerability description

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:

  • a call to a gRT->GetVariable() offset: 0x5ca
  • a call to a 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().

Disclosure timeline

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.

Disclosure Activity Date (YYYY-mm-dd)
Dell PSIRT is notified 2022-12-29
Dell PSIRT confirmed reported issue 2023-03-16
Dell PSIRT assigned CVE number 2023-06-15
Dell PSIRT provide patch release 2023-06-15
BINARLY public disclosure date 2023-06-21

Acknowledgements

BINARLY efiXplorer team

Tags
No items found.