Windows System Software -- Consulting, Training, Development -- Engineering Excellence, Every Time.

Mitigations and Best Practices for ExAllocatePoolZero Security Vulnerabilities

Mitigations and Best Practices for ExAllocatePoolZero Security Vulnerabilities

tl;dr

The Windows V2004 WDK/EWDK had a serious security vulnerability. It has been updated to mitigate that vulnerability, and you should update all of your machines that have the WDK/EWDK installed. However, updating the WDK/EWDK might not be enough in all circumstances. There are driver code-level mitigations that you should implement to ensure security regardless of the WDK version you build with, and to be consistent with best practices.

Background

Back in July, we discovered a security vulnerability in Windows 1909 and the Windows 2004 WDK, regarding a group of new security-oriented functions related to ExAllocatePoolZero. We immediately reported this issue to Microsoft, who posted a notice in the WDK forum and on the relevant doc pages. To avoid repeating everything we know about this vulnerability, please see our original blog post on this topic.

Microsoft Provides Mitigations

On 16 December 2020, Microsoft issued a “security refresh” of the Windows 2004 WDK. This updated both the standard Visual Studio WDK and the Enterprise WDK. These new versions have now replaced the original versions of the Windows 2004 WDK that are available for download from the WDK download page.

To determine which version of the WDK you have installed, look in the “Add or Remove Programs” dialog. The original (not security mitigated) WDK has the version 10.0.19041.0 or 10.0.19041.1 — The new (security mitigated) WDK shows up as version 10.0.19041.685.

In addition to issuing the WDK updates, Microsoft has issued a security update for Windows 1909 that fixes this vulnerability. It is not known which, specific, update includes this fix.

Best Practices — What You Need to Do. Now.

First, it’s important that you update to using the new, security mitigated, versions of the Windows 2004 WDK and EWDK as soon as possible. The changes that are made in this update are very focused and limited, but they are extremely important. In our testing here at OSR, we did not encounter any incompatibilities between the original WDK versions and the updated versions. We therefore strongly recommend that all new driver releases be built with the updated versions.

However, while building with the latest WDK is effective in mitigating the problem, this by itself is not sufficient and falls short of best practices unless you can be 100% sure that every build of your driver will always be built with nothing older than the updated WDK. Most development organizations are distributed, and use many different methods to build their drivers — even those for release to customers. It can, therefore, be exceptionally difficult to guarantee with any certainty that every single version of the WDK/EWDK on every build machine everywhere has been updated. Further, it may be inconvenient or impractical to immediately update some build environments.

To be sure that your driver always does the right thing, we strongly recommend that you follow the same practice that we use here at OSR, which we describe in the box below labeled “OSR’s ExAllocatePoolZero Mitigation” — You can see also an example of the code we use below.

OSR’s Code-Level ExAllocatePoolZero Mitigation

Even after the release of the Windows version 2004 WDK/EWDK security update, it can be difficult or impossible to be certain that your drivers will always be built using a properly updated version of the WDK. Are you sure that there isn’t a Jenkins build box somewhere that didn’t get updated? Why worry, when you can institute a simple mitigation?

At OSR, we’ve added code (right after calling ExInitializeDriverRuntime) that’s conditionalized on the symbol POOL_ZEROING_INFORMATION not being defined.  This symbol is defined in the updated, security mitigated, Windows 2004 WDK/EWDK (in WDM.H) and it is not defined in the original version of the Windows 2004 WDK/EWDK.  

IF POOL_ZEROING_INFORMATION is NOT defined when the driver is built (meaning we’re building the driver with the old WDK), and the driver is running on 1909, THEN the driver will case the the pool block to be zeroed after we allocate it.  This will ensure we’re always safe, even if in some cases we double zero (because the driver is running on an version of 1909 that has the security fix). If we knew precisely which update to Windows 1909 introduced the security fix we could presumably check for that; But we don’t.

IF the symbol IS defined, we’re building with the new WDK… and the new code in the WDK will take care of everything for us, so we don’t need to do any of our own code-level mitigations.

In all cases, if we’re running on a version of Windows different from 1909 we’ll let the OS handle things.

You can see an example of the code we use below. We call this function unconditionally in our drivers after calling ExInitializeDriverRuntime.

VOID OsrFixExPoolZeroingNativelySupported()
{
#ifndef POOL_ZEROING_INFORMATION

    RTL_OSVERSIONINFOW versionInfo;

    if (ExPoolZeroingNativelySupported) {
        //
        // Rather than testing for 1909
        // test against not being 2004 (19041)
        //
        RtlZeroMemory(&versionInfo, sizeof(RTL_OSVERSIONINFOW));
        versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);

        if (!NT_SUCCESS(RtlGetVersion(&versionInfo))) {
            ExPoolZeroingNativelySupported = FALSE;
        }
        else if ((versionInfo.dwMajorVersion == 10) &&
            (versionInfo.dwMinorVersion == 0) &&
            (versionInfo.dwBuildNumber < 19041)) {
            ExPoolZeroingNativelySupported = FALSE;
        }
    }
#endif
}