Header bannerHeader banner

[BRLY-2022-009] The arbitrary write vulnerability leads to arbitrary code execution during PEI phase on Intel platform.

August 10, 2022

Summary

BINARLY efiXplorer team has discovered a arbitrary write vulnerability on Intel platforms allowing a possible attacker to execute arbitrary code during PEI phase.

Vulnerability Information

  • BINARLY internal vulnerability identifier: BRLY-2022-009
  • Intel PSIRT assigned CVE identifier: CVE-2022-36372
  • AMI PSIRT assigned CVE identifier: CVE-2022-40262
  • CERT/CC assigned case number: VU#158026
  • FwHunt rule: BRLY-2022-009
  • CVSS v3.1: 8.2 High AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H

Affected Intel firmwares with confirmed impact by Binarly team

Device/Firmware File Name SHA256 (File PE32 section) File GUID
Intel Server Board M10JNP2SB S3Resume2Pei 7bb29f05534a8a1e010443213451425098faebd45948a4642db969b19d0253fc 89E549B0-7CFE-449D-9BA3-10D8B2312D71

Potential impact

A potential attacker can execute an arbitrary code at the time of the PEI phase and influence the subsequent boot stages. This can lead to the mitigasions bypassing, physical memory contents disclosure, discovery of any secrets from any Virtual Machines (VMs) and bypassing memory isolation and confidential computing boundaries. Additionally, an attacker can build a payload which can be injected into the SMRAM memory.

Vulnerability description

The pseudocode for vulnerable function is shown below:

int sub_FFEBFB2C()
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  DataSize = 4;
  S3PerformanceTablePointer = 0;
  Status = sub_FFEC0607(&EFI_PEI_READ_ONLY_VARIABLE2_PPI_GUID, &This);
  if ( Status >= 0 )
  {
    Status = This->GetVariable(
               This,
               L"FPDT_Variable_NV",
               &AMI_GLOBAL_VARIABLE_GUID,
               0,
               &DataSize,
               &S3PerformanceTablePointer);
    if ( Status >= 0 )
    {
      Status = S3PerformanceTablePointer;
      // Extracted from memory pointed by FPDT_Variable_NV variable value
      AcpiS3PerformanceTable = S3PerformanceTablePointer->AcpiS3PerformanceTable;
      if ( *S3PerformanceTablePointer->AcpiS3PerformanceTable == 'TP3S' )
      {
        if ( *&S3PerformanceTablePointer->ResumeCount )
        {
          if ( !AcpiS3PerformanceTable->S3Resume.Header.Type )
          {
            S3ResumeTotal = MultU64x32(__rdtsc(), *&S3PerformanceTablePointer->ResumeCount);
            LODWORD(v3) = S3ResumeTotal;
            HIDWORD(v3) = HIDWORD(S3ResumeTotal) % 0xF4240;
            FullResumeLo = v3 / 0xF4240;
            FullResumeHi = HIDWORD(S3ResumeTotal) / 0xF4240;
            v6 = __PAIR64__(HIDWORD(S3ResumeTotal) / 0xF4240, FullResumeLo)
               + AcpiS3PerformanceTable->S3Resume.AverageResume * AcpiS3PerformanceTable->S3Resume.ResumeCount;
            ResumeCount = AcpiS3PerformanceTable->S3Resume.ResumeCount + 1;
            LODWORD(v3) = v6;
            HIDWORD(v3) = HIDWORD(v6) % ResumeCount;
            Status = v3 / ResumeCount;
            AcpiS3PerformanceTable->S3Resume.ResumeCount = ResumeCount;
            LODWORD(AcpiS3PerformanceTable->S3Resume.AverageResume) = Status;
            HIDWORD(AcpiS3PerformanceTable->S3Resume.AverageResume) = HIDWORD(v6) / ResumeCount;
            LODWORD(AcpiS3PerformanceTable->S3Resume.FullResume) = FullResumeLo;
            HIDWORD(AcpiS3PerformanceTable->S3Resume.FullResume) = FullResumeHi;
          }
        }
      }
    }
  }
  return Status;
}

The AcpiS3PerformanceTable pointer is controlled by the attacker:  * S3PerformanceTablePointer value is occured from value of FPDT_Variable_NV NVRAM variable  * AcpiS3PerformanceTable = S3PerformanceTablePointer->AcpiS3PerformanceTable (extracted from memory pointed by FPDT_Variable_NV variable value)

Thus the memory pointed to by AcpiS3PerformanceTable can be overwritten with predictable values:

AcpiS3PerformanceTable->S3Resume.ResumeCount = ResumeCount;
LODWORD(AcpiS3PerformanceTable->S3Resume.AverageResume) = Status;
HIDWORD(AcpiS3PerformanceTable->S3Resume.AverageResume) = HIDWORD(v6) / ResumeCount;
LODWORD(AcpiS3PerformanceTable->S3Resume.FullResume) = FullResumeLo;
HIDWORD(AcpiS3PerformanceTable->S3Resume.FullResume) = FullResumeHi;

This can lead to arbitrary write during the PEI stage.

The primitive described above allows to perform privilege escalation from the PEI to the DXE/SMM phase (or from PEI to the SMM during S3 sleep/wake up circle).In turn, code execution in PEI allows the disable security features that are initialized at the PEI-DXE junction (like PPAM or Intel BIOS Guard).

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
Intel PSIRT is notified 2022-03-22
Intel PSIRT confirmed reported issue 2022-07-28
Intel PSIRT assigned CVE number 2022-07-28
BINARLY public disclosure date 2022-08-10

Acknowledgements

BINARLY efiXplorer team

Tags
PEI