Skip to main content
Patch Diffing Microsoft Windows Wi-Fi Driver Vulnerability (CVE-2024-30078) - Part 1

Patch Diffing Microsoft Windows Wi-Fi Driver Vulnerability (CVE-2024-30078) - Part 1

Kapil Khot
Windows CVE-2024-30078 Vulnerability Analysis

Background
#

On June 11, 2024, Microsoft released a patch for a Remote Code Execution (RCE) vulnerability in the Windows Wi-Fi driver (CVE-2024-30078). According to the advisory, an unauthenticated attacker within the range of the target computer’s wireless network adapter could send a specially crafted Wireless network packet to trigger the vulnerability.

In this blog series, we will examine the patch to find the specific function that was updated and try to determine the conditions required to trigger this bug.

Finding the Patched File(s)
#

While this vulnerability affects several Windows versions, we will analyse the patched and unpatched files for Windows 11 version 23H2. You can download a CSV file listing the changed files from the associated KB article, KB5039212. As with every Patch Tuesday, tens of thousands of files were updated. We need to figure out which of these are related to this vulnerability. Given that this vulnerability is in the Wi-Fi driver, filtering the files by name wifi shows 29 files were updated:

list of changed files

From the advisory, we can infer that it is likely a Kernel mode driver which was modified. While the file extension doesn’t really matter in the Kernel mode, we can narrow down the list of files by focusing on .sys and .dll files. That leaves us with the following files:

FileComment
nwifi.sysnative 802.11 miniport driver. From files.net
WdiWiFi.sysSystem driver associated with the Wi-Fi Direct feature in Windows operating systems. From MalwareTips.com
WifiCx.sys
WiFiCloudStore.dllDoes not seem to be related to the Wireless network adapter?
WiFiConfigSP.dllDoes not seem to be related to the Wireless network adapter?
wifidatacapabilityhandler.dll
WiFiDisplay.dll
wifinetworkmanager.dllMicrosoft-Windows-WiFiHotspotService or Windows WLAN AutoConfig?
Windows.Devices.WiFi.dllProvides a means to scan for nearby WiFi access points, enumerate those that are found, and connect to an access point
Windows.Devices.WiFiDirect.dllContains classes that support connecting to associated Wi-Fi Direct devices and associated endpoints for PCs, tablets, and phones

I performed a recursive grep on the C:\Program Files (x86)\Windows Kits\10\Include directory for each file mentioned above (just the filename without the extension) and found following header files for some of them:

$ grep -irl "nwifi" .
./10.0.22621.0/shared/windot11.h # Definitions for native 802.11 miniport driver specifications. Contains macros used by nwifi.sys
./10.0.26100.0/shared/windot11.h
$ grep -irl "WifiCx" .
./10.0.26100.0/km/dot11wificxintf.h # Definitions for native 802.11 miniport driver specifications. macros,structs, and enums etc.
./10.0.26100.0/km/dot11wificxtypes.hpp # WDI specific type definitions. Status codes for 802.11
./10.0.26100.0/km/wificx/1.0/wificx.h
./10.0.26100.0/km/wificx/1.1/wificx.h
$ grep -irl "Windows.Devices.WiFi" .
./10.0.22621.0/cppwinrt/winrt/windows.devices.wifi.h
./10.0.22621.0/winrt/windows.devices.wifi.h
./10.0.22621.0/cppwinrt/winrt/windows.devices.wifidirect.h
./10.0.22621.0/winrt/windows.devices.wifidirect.h

Based on the information found on the Internet as well as the header files mentioned above, we should analyse the following files:

  • nwifi.sys
  • WifiCx.sys
  • wifinetworkmanager.dll

An article on file.net describes nwifi.sys as:

The Nwifi.sys is a driver associated with the Native WiFi Miniport Drivers of Windows. The native 802.11 miniport drivers provide access to IEEE 802.11 wireless LAN (WLAN) media and basic service set (BSS) networks. They support standard IEEE 802.11 Media Access Control (MAC) frame formats, standard 802.11 Media Access Control (MAC) services and protocols, standard 802.11 authentication algorithms, standard 802.11 cipher algorithms, 802.11 MAC/PHY configuration and 802.11 network connectivity/security configuration.

Given the Microsoft advisory states an unauthenticated attacker within range of the target computer’s wireless network adapter could send a specially crafted wireless network packet to trigger the vulnerability, nwifi.sys is our best starting point. This is because it directly handles 802.11 wireless networks, frame formats, algorithms, and services.

At this point, we need both the vulnerable and patched versions of nwifi.sys. There are a few methods to obtain these:

  1. Set up vulnerable and patched Windows 11 23H2 machines, then copy the nwifi.sys file from both them.
  2. Download the patch directly from Microsoft and then use the PatchExtract.ps1 script.
  3. Use WinBindex, a more straightforward way to get the patched and unpatched versions of Windows binaries.

We will go ahead with WinBindex since it is the easiest way to get specific versions of Windows binaries. We will analyse the patched version of nwifi.sys (version 10.0.22621.3733, released on June, 11 2024) and the most recent version prior to this patch (version 10.0.22621.3672, released on May 29, 2024). It’s important to download the version closest to the patched version to minimise unrelated changes in the diff.

Diffing the nWifi.Sys
#

Patch diffing is a technique used to analyse and compare two versions of a binary or source code to identify the changes made between them. This is extremely useful when trying to understand and identify the exact changes made to address a vulnerability within a security patch.

Let’s load the patched and unpatched versions of nwifi.sys into Ghidra, load the correct symbol files (.PDB files), and let Ghidra analyse them. I set the following Analysis Options in Ghidra:

  • Aggressive Instruction Finder
  • PDB Universal
  • Variadic Function Signature Override
  • WindowsPE x86 Propogate External Parameters

Once Ghidra finishes analysing these files, we will export them using BinExport for further analysis in BinDiff. The exported files were named:

  • nwifi.sys-Vulnerable-May29-KB5037853-10.0.22621.3672.binexport
  • nwifi.sys-patched-june11-10.0.22621.3733.BinExport

Creating a new diff in BinDiff

As you can see in the BinDiff dialogue box above, we’ve loaded the vulnerable binary as Primary file and patched one as Secondary file. After BinDiff finishes analysing these files, we will focus on the following results:

  • Matched Functions: Functions that are present in both vulnerable and patched binaries
  • Primary Unmatched Functions: Functions present in the vulnerable binary but removed or deleted from the patched binary. These may include the vulnerable functions
  • Secondary Ummatched Functions: New functions added in the patched binary. These could be part of the security fix or new features introduced in the patch

Matched Functions
#

matched functions

As shown in the screenshot above, only four functions have been modified in the patched file. Out of those four functions, the Dot11Translate80211ToEthernetNdisPacket() and SymCryptFatal() have low similarity score (0.68 and 0.70 respectively). It means that more changes have been made to these functions. The Dot11Translate80211ToEthernetNdisPacket() function seems to be relevant here since 802.11 is the IEEE Wireless Standard.

Unmatched Functions
#

Now, let’s look at the unmatched functions. While Primary unmatched functions shows no function has been removed from the patched binary, the Secondary unmatched functions shows three new functions have been added:

Secondary unmatched functions

As we discussed above, out of those four functions from the patched nwifi.sys, it’s highly likely that the Dot11Translate80211ToEthernetNdisPacket() function might have contained the vulnerable code. From the function call trees in Ghidra, we can see that this function calls two of the newly added functions (secondary unmatched functions) and one of these functions in turn calls another newly added function, Feature_1281542463__private_IsEnabledFallback().

function call trees

As you can see from the screenshot above, the outgoing function call chains look like this:

%%{init: {'theme':'forest'}}%% flowchart TD Dot11Translate80211ToEthernetNdisPacket["Dot11Translate80211ToEthernetNdisPacket()"] Feature_1281542463__private_IsEnabledDeviceUsage["Feature_1281542463__private_IsEnabledDeviceUsage()"] Feature_1281542463__private_IsEnabledFallback["Feature_1281542463__private_IsEnabledFallback()"] wil_details_isEnabled_Fallback["wil_details_isEnabledFallback()"] Dot11CheckSelectiveTranslationTable["Dot11CheckSelectiveTranslationTable()"] guard_dispatch_icall["_guard_dispatch_icall()"] Dot11Translate80211ToEthernetNdisPacket --calls--> Feature_1281542463__private_IsEnabledDeviceUsage Feature_1281542463__private_IsEnabledDeviceUsage --calls--> Feature_1281542463__private_IsEnabledFallback Feature_1281542463__private_IsEnabledFallback --calls--> wil_details_isEnabled_Fallback Dot11Translate80211ToEthernetNdisPacket --calls--> Dot11CheckSelectiveTranslationTable Dot11CheckSelectiveTranslationTable --calls--> guard_dispatch_icall

Also, the function Dot11Translate80211ToEthernetNdisPacket() is being called from two other functions. This is how those incoming call chains look like:

%%{init: {'theme':'forest'}}%% flowchart TD DriverEntry["DriverEntry()"] Dot11DriverEntry["Dot11DriverEntry()"] ExtSTADriverEntry["ExtSTADriverEntry()"] ExtSTAReceivePacket["ExtSTAReceivePacket()"] ExtSTAReceiveDataPacket["ExtSTAReceiveDataPacket()"] ExtAPDriverEntry["ExtAPDriverEntry()"] ExtAPReceivePacket["ExtAPReceivePacket()"] APPktRcvHandlePackets["APPktRcvHandlePackets()"] APPkUtilsPerformPacketAction["APPkUtilsPerformPacketAction()"] Dot11PacketConverterReceivePacket["Dot11PacketConverterReceivePacket()"] Dot11Translate80211ToEthernetNdisPacket["Dot11Translate80211ToEthernetNdisPacket()"] DriverEntry --calls--> Dot11DriverEntry Dot11DriverEntry --calls--> ExtSTADriverEntry ExtSTADriverEntry --calls--> ExtSTAReceivePacket ExtSTAReceivePacket --calls--> ExtSTAReceiveDataPacket ExtSTAReceiveDataPacket --calls--> Dot11Translate80211ToEthernetNdisPacket Dot11DriverEntry --calls--> ExtAPDriverEntry ExtAPDriverEntry --calls--> ExtAPReceivePacket ExtAPReceivePacket --calls--> APPktRcvHandlePackets APPktRcvHandlePackets --calls--> APPkUtilsPerformPacketAction APPkUtilsPerformPacketAction --calls--> Dot11PacketConverterReceivePacket Dot11PacketConverterReceivePacket --calls--> Dot11Translate80211ToEthernetNdisPacket

Analysing the Dot11Translate80211ToEthernetNdisPacket Function
#

Based on its name, the Dot11Translate80211ToEthernetNdisPacket function appears to be responsible for translating 802.11 wireless frames to Ethernet NDIS packets. From my research, I found that Microsoft, along with 3COM Corporation, developed the Network Driver Interface Specification (NDIS) API for network interface controllers (NICs). This API provides a standard interface between protocol drivers and network adapter drivers, allowing various network devices to communicate. This enables existing applications and network protocols to work with Wi-Fi interfaces.

Next, we need to try reverse engineer the function to gain a deeper understanding of how it works. This involves carefully examining the function’s code and logic to identify areas where improper handling of 802.11 wireless packets might occur. Looking at some of the return codes, it seems that the vulnerability is related to the handling of the 802.11 wireless packet/frame.

For example, the following code snippet from the patched function checks if certain conditions related to packet size and bounds are met. If the check fails, the function exits with error code 0xc001000f, indicating a failure in processing the packet.

undefined8 Dot11Translate80211ToEthernetNdisPacket (longlong param_1,longlong param_2,undefined (**param_3) [16]) {
  # <-- code omitted for brevity -->
  # if this check fails then, exit/return with error code 0xc001000f
  if ((uVar18 + 8 < 8) || ((ulonglong)uVar4 < uVar18 + 8)) { 
    LAB_1c0004725:
    uVar11 = 0xc001000f;
  }
  else {
    # <-- code omitted for brevity -->
  }
  return uVar11; # exit/return with error code 0xc001000f
}

Looking at the status.h file, you will see it resolves to error code NDIS_STATUS_INVALID_PACKET. The compiler must have replaced this macro with its corresponding value, 0xc001000f.

admin@Research-PC MINGW64 /c/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0
$ cat ./shared/ndis/status.h | grep " NDIS_STATUS_INVALID_PACKET"
#define NDIS_STATUS_INVALID_PACKET              ((NDIS_STATUS)0xC001000FL)

Breakdown of Changes in the Dot11Translate80211ToEthernetNdisPacket Function
#

To identify the differences between unpatched and patched functions, we need to use Ghidra’s export feature (File –> Export Program) to export them in the C/C++ format. This will give us two .c files: one for the unpatched/vulnerable function and another for the patched function. We can then use an IDE such as Visual Studio Code to compare these files and highlight the differences.

The following screenshot shows excerpts from Visual Studio Code diff:

diff in the visual studio code

While the screenshot does not show the full diff, you can see significant code changes have been made. There are nine changes that I think we should be reviewing. For example, several if...else blocks have been removed or updated. Additionally, the newly added functions we discussed earlier are being called from this function.

As previously mentioned, we need to reverse engineer this function (or at least make the code a bit readable) to understand the changes that were made and their purpose. To achieve this, we should:

  • Review the arguments being passed to Windows APIs such as MmMapLockedPagesSpecifyCache() and NdisAdvanceNetBufferListDataStart()
  • Look at other functions in the incoming call chains that we identified earlier. Some of these functions may be documented on MSDN or declared in Windows Driver header files

By understanding the variable data types and structures involved, we can update and comment on the code in Ghidra to make it more readable and comprehensible. This will give us a better understanding of the code flow, helping us pinpoint the exact nature of the changes and identify the root cause of the vulnerability.

I will continue working on this analysis and aim to provide a detailed breakdown in part 2 of this blog series soon.