Manuel Feifel
2298 words
11 minutes
EDR Part 1: Intro & Security Analysis of EDR Drivers

This blog post is part of a series analyzing attack surface for Endpoint Detection and Response (EDR) solutions. Part 1-3 are linked to each other. Final notes are included in Part 3.

  • Part 1 (this article) gives an overview of the attack surface of EDR software and describes the process for analysing drivers from the perspective of a low-privileged user.
  • Part 2 describes the results of the EDR driver security analysis. A minor authentication issue in the Windows driver of the Cortex XDR agent was identified (CVE-2024-5905). Additionally a PPL-”bypass” as well as a detection bypass by early startup. Furthermore, snapshot fuzzing was applied to a Mini-Filter Communication Port of the Sophos Intercept X Windows Mini-Filter driver.
  • Part 3 describes a DoS vulnerability affecting most Windows EDR agents. This vulnerability allows a low-privileged user to crash/stop the agent permanently by exploiting an issue in how the agent handles preexisting objects in the Object Manager’s namespace. Only some vendors assigned a CVE (CVE-2023-3280, CVE-2024-5909, CVE-2024-20671).
  • Part 4 will be about fuzzing the scanning/emulation engine of the antivirus (AV) component included in some EDRs.

1. Introduction#

In today’s organizations, Endpoint Detection and Response (EDR) solutions have become an essential part of the cybersecurity landscape. Despite their prevalence, there remains a notable lack of published research focusing on attacks against EDR applications themselves, even though they present compelling targets for privilege escalation, corporate network compromise, or drive-by attacks. This could have several reasons: limited security research in this specific area, unpublished research held back for red-teaming advantages, or a lack of fruitful findings. Futhermore, there are only a few vendors which have a Bug Bounty program and if they have one, the Windows agents are not in scope (except Kaspersky).
In recent years, most security research about EDRs focused on techniques to bypass the security measures such as DLL unhooking. Recognizing this gap, a research project of IG Labs aimed to analyze the attack surface of drivers for widely-used EDRs on Windows. However, after starting the work and before publication of this post, some research into directly attacking EDRs has emerged (1, 2, 3, 4, 5, …).

Due to the extensive attack surface of EDRs, we decided to limit the scope of our investigation to driver-interfaces accessible by a low privileged user as an entry point.
EDRs typically incorporate self-protection mechanisms that prevent even local administrators from disabling or uninstalling the product. However, the transition from local administrator to SYSTEM to kernel in Windows, while restricted by security features, does not represent an absolute security boundary. Known methods exist to deactivate or bypass EDRs, including bring-your-own-vulnerable-driver (BYOVD) attacks, Windows Defender Application Control (WDAC), WPF filtering, and other techniques. Consequently, this research focused on low-privileged users, even though attacks from a local administrator can also be valuable.

Additionally, the focus is directed towards the most used EDRs in the market.
However, obtaining access to these EDR solutions for research purposes is challenging. Only a very limited number of vendors offer trial products without prior vetting, further narrowing the range of EDRs that can be analyzed.

Typical “trial” (which you will never get without having the right business)

Although several EDR drivers permit low-privileged users to access certain functions, it was not possible to detect any serious security issues. Nevertheless, the blog post describes the analysis process including research paths that yielded no findings to help other security researchers. However, after some time a path led to the ALPC communication in PaloAlto Cortex which was not the initial focus. This revealed a DoS vulnerability that is described in Part 3.

The following products were initially examined for the driver analysis:

  • Palo Alto Cortex XDR
  • Sophos Intercept X
  • Microsoft Defender for Endpoint
  • Cynet 360
  • Tanium (not really an EDR but the functionality and architecture is similar)

1.1 Attack Surface of EDR solutions#

EDR solutions have a comparably large attack surface for applications. These solutions typically consist of multiple components on the agents themselves and cloud services, while some vendors also offer on-premise servers. EDRs are designed to detect and respond to threats at the endpoint level, but they also inherently create a new attack surface that can be exploited by malicious actors. The agents are a complex part and consequently more prone to vulnerabilities. The frontend of the servers-side part, in contrast has a smaller and usually more explored attack surface (at least the part which is directly accessible by a user such as the web interfaces). Even backend web APIs have basic flaws such as missing authentication as shown here. Some products also use a custom protocol for the agent-to-cloud communication which could be interesting, too. Furthermore, certain products also implement P2P-communication among the agents (e.g. Tanium) which are interesting from an attacker point of perspective.

These blog posts focuses solely on the agent side.

Agents typically consist of user-space and kernel-space (driver) components:

  • Filter Driver
  • Network Drivers
  • Software Driver
  • Userland Applications

These components primarily communicate with each other using the following protocols and methods:

  • Kernel-to-Kernel:
    • Exported functions
    • IOCTLs
  • User-to-Kernel:
    • IOCTLs
    • FilterConnectionPorts
      • specifically used by minifilter drivers
      • uses IOCTLs under the hood as well
    • ALPC
  • User-to-User
    • ALPC
    • Named Pipes
    • Files
    • Registry
    • more…

The following list outlines potential vulnerabilities within these components and their communication. Note that this list is not exhaustive and excludes all server-side attack surface:

This article focuses on user-to-driver authorization issues and, to some extent, classic driver vulnerabilities. Part 3 addresses user-mode IPC.

Impact#

The main impact of potential vulnerabilities on the client side is Local Privilege Escalation or hiding the execution of malicious software. Another possibility is spoofing data about the agent sent to the backend. Some vulnerabilities, such as in the scanning engine could also lead to Remote Code Execution. Additionally, a compromised agent also leads to a larger attack surface for the backend, as the attacker can abuse the authenticated session of the agent.
In case of P2P-based communication, serious design flaws could lead to the compromise of other agents.

2. Driver Attack Surface for Low-Priviledged Users#

This section provides a basic overview of how to conduct a security analysis of EDR drivers. It primarily focuses on identifying exposed driver functionalities that could constitute an attack surface for low-privileged users. It does not delve into the specifics of driver-related vulnerabilities or their exploitation.

For a testing environment we recommend using a virtual machine with a WinDBG Kernel Debugger attached. The easiest way to set it up is KDNET (see the following documentation).

2.1 Which drivers are loaded?#

One method for viewing loaded drivers is using DriverView which is further aided by the fact that there is a feature to filter drivers from Microsoft.

DriverView with Cortex installed

2.2 What interface does each driver expose to user-land?#

There are two approaches to check this: static analysis and dynamic analysis. We suggest to use both methods in combination to catch all cases. For example, an EDR driver may only initialize one device object during startup but might be creating more device objects during the course of the EDRs execution.

Windows Driver Model (WDM) or Kernel-Mode Driver Framework (KMDF) drivers can expose a device interface. This interface can be opened from user space to interact with the driver.

Mini-Filter Drivers can expose a FilterCommunicationPort which can be used from user-mode to interact with the driver.

2.2.1 Static Analysis#

To establish a communication interface, a driver must invoke specific Windows APIs. These calls can be statically analyzed using reverse engineering software such as IDA. The following functions, as specified in Microsoft’s documentation, are commonly used:

  • WDM Device Driver:

    NTSTATUS IoCreateDevice(
      [in]           PDRIVER_OBJECT  DriverObject,
      [in]           ULONG           DeviceExtensionSize,
      [in, optional] PUNICODE_STRING DeviceName,
      [in]           DEVICE_TYPE     DeviceType,
      [in]           ULONG           DeviceCharacteristics,
      [in]           BOOLEAN         Exclusive,
      [out]          PDEVICE_OBJECT  *DeviceObject
    );

    Alternatively, IoCreateDeviceSecure can be used which applies a default SDDL String.

  • KMDF Device Driver:

    NTSTATUS WdfDeviceCreateDeviceInterface(
      [in]           WDFDEVICE        Device,
      [in]           const GUID       *InterfaceClassGUID,
      [in, optional] PCUNICODE_STRING ReferenceString
    );
  • Mini-Filter Driver:

    NTSTATUS FLTAPI FltCreateCommunicationPort(
      [in]           PFLT_FILTER            Filter,
      [out]          PFLT_PORT              *ServerPort,
      [in]           POBJECT_ATTRIBUTES     ObjectAttributes,
      [in, optional] PVOID                  ServerPortCookie,
      [in]           PFLT_CONNECT_NOTIFY    ConnectNotifyCallback,
      [in]           PFLT_DISCONNECT_NOTIFY DisconnectNotifyCallback,
      [in, optional] PFLT_MESSAGE_NOTIFY    MessageNotifyCallback,
      [in]           LONG                   MaxConnections
    );

2.2.2 Dynamic Analysis#

Registered devices and port names can be identified using WinObj from Sysinternals (although the names are not always obvious) or by setting breakpoints in a kernel debugger.

Device Driver: They are listed in WinObj under “GLOBAL??” as Symbolic Link pointing towards the device object. This makes the device available through \\.\NAME when trying to interact with the driver from another application. Another option is to use the discontinued tool DeviceTree from OSR which shows the devices in a hierachy for each driver and addtional information.

WinObj with Cortex Devices

DeviceTree with Cortex Devices

Mini-Filter Driver: They are listed in WinObj named “FilterConnectionPort”

FilterConnectionPorts in WinObj (Cortex)

2.3 What are the access permissions of each interface?#

This step is more easily accomplished dynamically.

Device Driver: To our knowledge there is no modern tool to view the correct access permissions of a device interface. Try to get a copy of OSR DeviceTree which directly shows the ACLs and other flags. An alternative is to use a Kernel Debugger.

Mini-Filter Driver: One way to test if a certain user can access a FilterConnectionPort is to use the NtObjectManager tooling from James Forshaw.

Get-FilterConnectionPort -Path "\CyvrFsfd"
Exception: "(0x80070005) - Access is denied."

To retrieve the security descriptor, use WinDbg as a kernel debugger:

0: kd> !object \CyvrFsfd
Object: ffffc2861e32f550  Type: (ffffc2861bc7b220) FilterConnectionPort
    ObjectHeader: ffffc2861e32f520 (new version)
    HandleCount: 1  PointerCount: 6
    Directory Object: ffffd9099503e9f0  Name: CyvrFsfd
0: kd> dx (((nt!_OBJECT_HEADER*)0xffffc2861e32f520)->SecurityDescriptor & ~0xa)
(((nt!_OBJECT_HEADER*)0xffffc2861e32f520)->SecurityDescriptor & ~0xa) :
0xffffd909950257a0
0: kd> !sd 0xffffd909950257a0 1
->Revision: 0x1
->Sbz1    : 0x0
->Control : 0x8004
            SE_DACL_PRESENT
            SE_SELF_RELATIVE
->Owner   : S-1-5-32-544 (Alias: BUILTIN\Administrators)
->Group   : S-1-5-18 (Well Known Group: NT AUTHORITY\SYSTEM)
->Dacl    :
->Dacl    : ->AclRevision: 0x2
->Dacl    : ->Sbz1       : 0x0
->Dacl    : ->AclSize    : 0x1c
->Dacl    : ->AceCount   : 0x1
->Dacl    : ->Sbz2       : 0x0
->Dacl    : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl    : ->Ace[0]: ->AceFlags: 0x0
->Dacl    : ->Ace[0]: ->AceSize: 0x14
->Dacl    : ->Ace[0]: ->Mask : 0x001f0001
->Dacl    : ->Ace[0]: ->SID: S-1-5-18 (Well Known Group: NT AUTHORITY\SYSTEM)

->Sacl    :  is NULL

This port only allows access from NT AUTHORITY\SYSTEM.

2.4 What can you do if an interface is accessible?#

First of all, if the Windows ACLs allow access to the device interface or FilterConnectionPort, that doesn’t mean that you can actually access all the functionality offered by these interfaces. The driver itself can perform any arbitrary access checks such as verifying the security token of the calling process or checking the file path of the process.

This topic usually requires to reverse engineer a driver’s interesting functions and figuring out what they do in the absence of function names in the driver. In the following segment, we give some advice on how to start.

2.4.1 Device Driver#

The most relevant method for interacting with these devices is Device Input/Output Control (IOCTL) using major function code 0xe (14) which allows for communication between 2 partners while offering a channel for (bi)directional data exchange. While read (IRP_MJ_READ) and write (IRP_MJ_WRITE) operations could also be relevant, the primary functionality in this context resides within IRP_MJ_DEVICE_CONTROL.

These dispatch functions can be triggered from user-mode with the following functions:

  • CreateFile() => IRP_MJ_CREATE_
  • ReadFile() => IRP_MJ_READ
  • DeviceIoControl() => IRP_MJ_CONTROL

Source: Enrique Nissim, IOActive

The next step involves reverse engineering to analyze the functionality of these dispatch functions. The functionality is located in DRIVER_DISPATCH callback functions which are registered during the initialization of the driver. The following screenshot from IDA shows the tedrdrv.sys driver from Cortex. In this example the callback functions are the same for most of the functions.

Driver Dispatch Functions in Cortex

The function sub_1233A0 differentiates between MajorFunction codes. If 0xe (DEVICE_CONTROL) is called the real IO Control Dispatch function is called which looks like the following:

Device IO Control Dispatch Function in Cortex

The different cases correspond to I/O control codes (IOCTLs). This section of the code reveals an interesting aspect: the process ID of the caller is retrieved and then either compared or passed to other functions. This suggests an authorization mechanism needs to be further analyzed. This is part of the next chapter.

2.4.2 FilterConnectionPorts:#

Filter connection ports are similar to I/O control codes. During the registration of a filter connection port, callback functions can be specified in the FltCreateCommunicationPort function to handle connection, disconnection, and messages.

FltCreateCommunicationPort() in cyvrfsfd.sys from Cortex

The MessageNotifyCallback implements different cases similar to the IO Control Dispatch function above.

2.5 Why are some of the EDR driver interfaces accessible for low privileged users at all?#

Verifying the access control lists (ACLs) of driver interfaces is a fundamental check in penetration testing, one that, ideally, all vendors should conduct at some stage. It is very likely that, in some instances, ACLs are intentionally set to be broadly accessible. Windows APIs typically offer functionalities to restrict privilege levels for specific device objects. One such example is using IoCreateDeviceSecure with an explicit security identifier as an argument, or alternatively using IoCreateDevice and specifying the security identifier within the INF file.

Why, then, are these ACLs not more restrictive? During kernel debugging of the driver, we observed that IOCTLs are invoked from various user-space processes. EDRs often employ an architecture where an injected DLL from the EDR agent communicates directly with the driver via IOCTLs on behalf of the injected process. Because these injected processes are frequently low-privileged (e.g., word.exe), the driver cannot enforce security restrictions based solely on the process’s privilege level. We’ve never implemented an EDR and we don’t have enough details to judge this, but we don’t think that this is the most secure approach. Indeed, several EDR solutions do not adopt this practice.

Following the process described in the previous sections, there are candidates with open ACLs for the driver interfaces:

  • PaloAlto Cortex XDR
  • Sophos Intercept X

The results are described in Part 2

EDR Part 1: Intro & Security Analysis of EDR Drivers
https://labs.infoguard.ch/posts/edr_part1_intro_-_security_analysis_of_edr_drivers/
Author
Manuel Feifel
Published at
2025-02-10