top of page
  • Writer's pictureZiyi Shen

EDRPrison: Borrow a Legitimate Driver to Mute EDR Agent

June 26th, 2024 By Ziyi Shen 

 


Download: EDRPrison











Hey friends, today I will share a technique that can be used to evade EDR products. I hesitate to call it a "new" technique, as I drew inspiration from existing projects and techniques. It is not a typical evasion method like sleep obfuscation, stack obfuscation, or syscall manipulation. Instead, it exploits oversights in detection mechanisms.


The principle behind the technique is neither groundbreaking nor complex. However, I encountered multiple rabbit holes and thought I had reached dead-ends several times during my research. Therefore, I will also share my struggles and failed attempts in this article.


Network Connection Based Evasion:

Beyond directly battling with EDR using advanced malware techniques, there are alternative ways to achieve evasion, such as exploiting EDR misconfigurations or vulnerabilities, uninstalling the EDR agent, or using Bring Your Own Vulnerable Driver (BYOVD) to kill EDR processes. Among these indirect evasion techniques, I am particularly interested in network connection-based evasion.


The concept is not entirely new. EDR agents regularly communicate with central or cloud servers by sending telemetry, which includes host information, diagnostic alerts, detections, and other data. For more complex malware, the EDR agent may not terminate it immediately. Instead, the agent monitors the malware's behavior and leverages machine learning on the cloud. Once sufficient evidence is collected, the execution of the malware will be terminated. This indicates that EDR heavily relies on the cloud. Of course, the agent can still detect classic malware and techniques, such as a vanilla Mimikatz. However, without an internet connection, the EDR loses much of its effectiveness, and SOC teams cannot monitor the endpoint through the EDR management panel.


I have installed Microsoft Defender for Business and Elastic Endpoint on my test physical server. Based on the following screenshots, these sensors communicate with various servers, each serving different roles. Some servers collect telemetry for further analysis, while others gather malware samples, and so on.

To observe plaintext telemetry more conveniently, I used mitmproxy to inspect the following HTTP packets after intentionally running some vanilla malware. The data did indeed contain detection and alert information.

Aside from experiencing an internet outage or blackout, what about intentionally making the endpoint offline? The principle is not complex. A few years ago, an article demonstrated this by utilizing Windows Defender Firewall rules. Setting rules to prevent EDR processes from sending data to the cloud sounds straightforward. However, aside from requiring administrator privileges, there are other shortcomings and mitigations.


A short article provided two quick mitigations. By enabling Tamper Protection, attempts to leverage firewall rules to silence MDE processes will be blocked. Though Tamper Protection specifically protects MDE processes, other EDR vendors could adopt similar protection mechanisms. Disabling firewall rule merging is another effective countermeasure. Particularly for organizations using Active Directory, Group Policy (GPO) can override endpoint local firewall settings.


Windows Defender Firewall supports rules based on remote addresses and ports. Unfortunately, some EDR products communicate with hundreds or even thousands of contact servers, making it difficult to include every contact server while remaining stealthy. For Microsoft Defender for Endpoint/Business, we can refer to the documentation on configuring device connectivity and Azure IP ranges. The data could be sent to the cloud among thousands of cloud servers, and this complexity applies to a single EDR, let alone other EDR products.


WFP And EDRSilencer:

Since changes to the Windows Defender Firewall are easy to observe, tampering at a lower level may be a better approach. This brings the Windows Filtering Platform (WFP) to our attention.


According to Microsoft documentation, WFP is a set of APIs and system services that offer developers flexibility and capability to interact with and manipulate packet processing at various layers. WFP allows users to filter connections by application, user, address, network interface, etc. WFP's capabilities are utilized for content filtering, parental control, censorship circumvention, deep packet inspection, and more. Consequently, WFP is widely used by security products such as IDS/IPS, ad blockers, firewalls, EDRs, and VPNs. Windows Defender Firewall is also based on WFP. The following screenshot shows that AdGuard uses a WFP driver to extend its capabilities.

WFP is very complex, and it is impossible to cover every feature here. Microsoft has extensive documentation on WFP, which you can explore for detailed information. However, I will explain some key concepts in a simplified manner to avoid confusion later.


Callouts

A callout is a set of functions exposed by a driver and used for specialized filtering. WFP includes some built-in callout functions, each identified by a GUID.


Filter Engine

The filter engine consists of a user-mode component, the Base Filtering Engine (BFE), and a kernel-mode component, the Generic Filter Engine. These components work together to perform filtering operations on packets. The kernel-mode component performs filtering at the network and transport layers. During the process of applying filters to network traffic, the kernel-mode component calls the available callout functions.


Filters

Filters are rules matched against packets, telling the filter engine how to handle the traffic. For instance, a rule could be "block all outbound packets to TCP port 9200". Filters can be either boot-time or run-time. Boot-time filters are enforced as tcpip.sys starts during boot, while run-time filters are more flexible.


Callout Driver

To achieve more specific goals, such as DPI or website parental controls, a custom callout driver is needed to extend WFP capabilities. Microsoft provides a sample WFP callout driver project, which can be found here. The sample callout driver is used by WFPSampler.exe to define policies, as explained here.


We can use the netsh.exe program or WFPExplorer to view Windows Filtering Platform objects.

netsh wfp show filters

With WFPExplorer, it becomes more convenient for us to view callouts, WFP providers, layers, filters, net events, and more. From the screenshot, we can see that many security products and built-in applications use WFP.

Several tools have utilized WFP to block EDR processes from sending telemetry, including FireBlock from MdSec NightHawk C2, Shutter, and EDRSilencer. Shutter appears to be the earliest project that uses WFP to silence EDR processes (correct me if I am wrong), and it can also block traffic based on IP addresses. However, as mentioned earlier, it is not practical to include all possible IPs for all possible EDRs. Since FireBlock is closed-source, let's analyze code snippets from EDRSilencer.


EDRSilencer hardcodes a list of common EDR and AV processes. Each of these products may have multiple running processes with different functionalities, including sending telemetry and uploading malware samples. 

char* edrProcess[] = {
// Microsoft Defender for Endpoint and Microsoft Defender Antivirus
    "MsMpEng.exe",
    "MsSense.exe",
    "SenseIR.exe",
    "SenseNdr.exe",
    "SenseCncProxy.exe",
    "SenseSampleUploader.exe",
// Elastic EDR
    "winlogbeat.exe",
    "elastic-agent.exe",
    "elastic-endpoint.exe",
    "filebeat.exe",
// Trellix EDR
    "xagt.exe",
// Qualys EDR
    "QualysAgent.exe",
...
//TrendMicro Apex One
    "CETASvc.exe",
    "WSCommunicator.exe",
    "EndpointBasecamp.exe",
    "TmListen.exe",
    "Ntrtscan.exe",
    "TmWSCSvc.exe",
    "PccNTMon.exe",
    "TMBMSRV.exe",
    "CNTAoSMgr.exe",
    "TmCCSF.exe"
};

The FwpmEngineOpen0 function opens a session to the filter engine, which is a critical step in setting up the WFP filters. EDRSilencer relies on the built-in WFP functionalities.

result = FwpmEngineOpen0(NULL, RPC_C_AUTHN_DEFAULT, NULL, NULL, &hEngine);

Using the kernel-mode filtering engine requires elevated privileges, thus high integrity is mandatory. EDRSilencer ensures it operates with the necessary privileges to modify the filtering engine.


Then, EDRSilencer retrieves the full path of the executable of the running process using OpenProcess. Opening a handle to the EDR process could be risky, as it might trigger security alerts or detections.

HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe32.th32ProcessID);
        if (hProcess) {
                WCHAR fullPath[MAX_PATH] = {0};
                DWORD size = MAX_PATH;
                FWPM_FILTER_CONDITION0 cond = {0};
                FWPM_FILTER0 filter = {0};
                FWPM_PROVIDER0 provider = {0};
                GUID providerGuid = {0};
                FWP_BYTE_BLOB* appId = NULL;
                UINT64 filterId = 0;
                ErrorCode errorCode = CUSTOM_SUCCESS;
               
                QueryFullProcessImageNameW(hProcess, 0, fullPath, &size);
                ......

Next, EDRSilencer sets up the WFP filter and condition. The filter layer used is FWPM_LAYER_ALE_AUTH_CONNECT_V4. According to Microsoft, "This filtering layer allows for authorizing connect requests for outgoing TCP connections, as well as authorizing outgoing non-TCP traffic based on the first packet sent." The filter is persistent, as defined in the documentation, and the classification action is set to BLOCK.

The condition identifier is FWPM_CONDITION_ALE_APP_ID, which is an application identifier derived from a file name, populated by the FwpmGetAppIdFromFileName0 function. To avoid internal calls to CreateFileW, which could be intercepted by minifilter drivers and block access to the EDR executable, EDRSilencer implements a custom version of this function. The match type is FWP_MATCH_EQUAL, ensuring that the application identifier exactly matches the specified value.

			  // Set up WFP filter and condition
			  filter.displayData.name = filterName;
                filter.flags = FWPM_FILTER_FLAG_PERSISTENT;
                filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
                filter.action.type = FWP_ACTION_BLOCK;
                cond.fieldKey = FWPM_CONDITION_ALE_APP_ID;
                cond.matchType = FWP_MATCH_EQUAL;
                cond.conditionValue.type = FWP_BYTE_BLOB_TYPE;
                cond.conditionValue.byteBlob = appId;
                filter.filterCondition = &cond;
                filter.numFilterConditions = 1;
                 // Add WFP provider for the filter
                if (GetProviderGUIDByDescription(providerDescription, &providerGuid)) {
                    filter.providerKey = &providerGuid;
                } else {
                    provider.displayData.name = providerName;
                    provider.displayData.description = providerDescription;
                    provider.flags = FWPM_PROVIDER_FLAG_PERSISTENT;
                    result = FwpmProviderAdd0(hEngine, &provider, NULL);
                    if (result != ERROR_SUCCESS) {
                        printf("    [-] FwpmProviderAdd0 failed with error code: 0x%x.\n", result);
                    } else {
                        if (GetProviderGUIDByDescription(providerDescription, &providerGuid)) {
                            filter.providerKey = &providerGuid;
                        }
                    }
                }
                // Add filter to both IPv4 and IPv6 layers
                result = FwpmFilterAdd0(hEngine, &filter, NULL, &filterId);
                ......

Finally, the filter is added to both IPv4 and IPv6 layers to ensure comprehensive coverage.

result = FwpmFilterAdd0(hEngine, &filter, NULL, &filterId);                

Possible Variations of Network Connection Based Evasion:

Tools like Fireblock, Shutter, and EDRSilencer work by adding filters to EDR executables. Consequently, there are tools designed to detect such filters. For example, the project EDRNoiseMaker can detect abnormal WFP filters added to a predefined list of EDR executables and remove them accordingly.


So, are there any other possible variations of network connection-based evasion? Here are two potential approaches. The first variation involves tampering with the hosts file on the system. This idea was also discussed in this article. By binding EDR contact servers to an intended false address, such as 127.0.0.1, telemetry would be sent to incorrect destinations. While this approach sounds straightforward, there are several practical issues to consider:


  1. Comprehensive Mapping: We need to bind all possible contact servers to a false IP address, which would require adding hundreds or thousands of entries.

  2. Lack of Stealth: Modifying the hosts file is not stealthy and can be easily detected by monitoring tools.

  3. Direct IP Usage: Some EDRs may use IP addresses instead of domain names, bypassing the hosts file modifications.

  4. Cached Addresses: Unless the EDR service or the OS is restarted, correct IP addresses may still be cached in memory, rendering the hosts file modification ineffective.


The second variation involves setting a rogue proxy for EDR agents. This proxy can be an attacker-controlled server or an invalid proxy, such as 127.0.0.1:8080. In my experimentation with the rogue proxy approach, I encountered several challenges that led to a rabbit hole.


Rogue Proxy: A Path to The Rabbit Hole:

I spent several days exploring the rogue proxy approach, but encountered multiple dead-ends, likely due to unfamiliarity with some concepts. Our goal is to force the EDR agent to send telemetry to a rogue proxy server, which could be either attacker-controlled or invalid. Either way, the data will not reach the cloud servers.


Proxies can be quite tricky to apply, especially since different programs prioritize proxy settings differently. Some programs do not enable proxies by default, while others align their proxy selection with the operating system's global proxy configuration. Various programs prioritize different settings:


  1. Global Proxy Settings: Some programs follow the global proxy configuration set by the operating system.

  2. Environment Variables: Others may prioritize proxy settings specified in environment variables like HTTPS_PROXY or HTTP_PROXY.

  3. Application-Specific Settings: Some programs have their own proxy configuration settings independent of the system or environment variables.


There are several methods for configuring proxies in Windows:

  1. Netsh Command: Configuring the HTTP proxy via the netsh command.

  2. Network & internet > Proxy: Setting the proxy through the Network & Internet > Proxy page in the Windows settings, which syncs with the registry at HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings.

  3. Environment Variables: Using environment variables such as HTTPS_PROXY and HTTP_PROXY to set the proxy

Despite these methods, it is unlikely that we can directly modify the proxy settings of a running EDR service.


Additionally, since the EDR service usually runs as NT AUTHORITY\SYSTEM, the proxy setting needs to be system-wide.

According to the Elastic documentation, in a corporate environment, proxy settings are often enabled for monitoring and logging purposes. Elastic Endpoint supports server-end specified proxy settings and environment variables. If the proxy setting is specified on the server-end, it will ignore all proxy settings on the endpoint. Even if the proxy setting is based on environment variables, we would need to restart the service to apply the new settings, which is risky and may require privileges we do not have. This led me to a rabbit hole: can I change the service's environment variables in real-time?


We can use the command dir env: to list all environment variables in a shell session.

By using WinDBG to attach to the PowerShell process, we can locate environment variables via PEB walking. The current size of the environment block is 0x1340.

Unfortunately, there is no extra space for adding additional environment variables.

In PowerShell, adding new environment variables is straightforward.

However, when we add new variables, the address for the Environment element changes instead of appending the new variables to the list.

To understand how the address of the Environment element is modified, we might need to reverse engineer powershell.exe, which complicates the process further. Additionally, even if we decipher the pattern for how PowerShell adds environment variables, more challenges await:


  1. Program-Specific Logic: The logic for handling environment variables may differ between programs. While adding or modifying an environment variable in a PowerShell session is simple, this is not necessarily true for other processes.

  2. Remote Process Modification: Adding environment variables for remote processes, especially EDR processes, is more difficult and risky.

  3. Static Variable Storage: Depending on the program's code, some applications might read and store environment variable values in static variables at initial runtime. Even if environment variable values are added or changed mid-execution, the values in the code's static variables will not be updated.


Furthermore, Elastic Endpoint's proxy settings can be specified on the server, thereby overriding any system settings on the endpoint. Additionally, according to Microsoft's documentation on configuring MDE proxy settings, MDE's proxy configuration has multiple sources with different priorities. Therefore, at least in user mode, there is no universal and effective method to enforce a rogue proxy.


Borrow A Legitimate Driver to Disguise:

EDRSilencer uses built-in callout functions, eliminating the need to load an external driver. While WFP allows for filtering and basic manipulation of packets, its higher-level abstraction might not be suitable for all low-level network manipulations. To achieve greater flexibility, an external WFP callout driver can be very helpful.


As previously mentioned, Microsoft has provided a Windows Filtering Platform Sample. The main program is a sample firewall, and the driver exposes callout functions for injection, basic actions, proxying, and inspection. We discussed the possibility of implementing a rogue proxy approach in user land and encountered a dead-end. However, with a custom callout driver, achieving this in kernel mode becomes feasible. The WFP Sampler is an excellent example.


Unfortunately, Microsoft only provides the source code for the WFP Sampler, without a compiled driver and program. While the project is a valuable learning resource for WFP programming, it implies that we need to sign the driver ourselves. Our goal is to rely on an existing legitimate WFP callout driver that adds more flexibility, such as packet interception, packet injection, deep packet inspection, etc. Since WFP is widely adopted by many legitimate software applications, particularly security products, finding a suitable driver is not difficult.

However, for closed-source software, we do not have access to their source code. While reverse engineering closed-source callout drivers is an option, is there any powerful WFP callout driver that is signed and open-source? WinDivert is one such driver.


WinDivert is a user-mode packet capture and network packet manipulation utility designed for Windows. It provides a powerful and flexible framework for intercepting, modifying, injecting, and dropping network packets at the network stack level. It operates as a lightweight, high-performance driver that interfaces directly with the network stack, allowing for detailed packet inspection and manipulation in real time.


WinDivert can be used to implement packet filters, sniffers, firewalls, IDSs, VPNs, tunneling applications, and more, due to the following key features:


  • Packet Interception: Captures packets from the network stack, allowing for real-time monitoring and analysis.

  • Packet Injection: Supports injecting modified or new packets back into the network stack.

  • Packet Modification: Enables detailed manipulation of packet contents, including headers and payloads.

  • Protocol Support: Supports a wide range of network protocols, including IPv4, IPv6, TCP, UDP, ICMP, and more.

  • Filtering: Uses a flexible and powerful filter language to specify criteria for packet interception and modification.


Some security software utilizes WinDivert, giving the callout driver a generally positive reputation.

WinDivert operates as a kernel-mode driver that hooks into the Windows network stack. It provides a user-mode API for applications to interact with the driver, enabling packet interception, modification, and injection. The driver uses a custom filtering engine to determine which packets to intercept based on user-defined rules.

WinDivert has the following advantages over the built-in WFP:


  1. Packet Injection: Built-in callouts do not natively support injecting packets back into the network stack. While modifications can be made to packets, reinjection requires additional mechanisms not provided by standard WFP callouts. WinDivert provides straightforward support for packet injection, enabling applications to reinject modified or newly created packets directly into the network stack.

  2. Extended Functionality: Built-in WFP callouts are confined to the features and functionalities provided by the WFP API, which can limit the scope of customization beyond these predefined capabilities. In contrast, WinDivert extends functionality significantly, offering support for a broader range of protocols, more flexible filtering criteria, and advanced packet manipulation options.

  3. Direct Network Stack Interaction: Built-in callouts are part of the WFP framework, which abstracts many low-level details, reducing flexibility in terms of direct network stack interaction. As a standalone driver, WinDivert provides direct access to network packets, enabling more control and customization, including direct interaction with hardware-level packet processing.

  4. Ease of Use: Configuration and use of the built-in WFP can be complex, requiring detailed knowledge of the WFP API and its intricacies. WinDivert provides a simpler and more intuitive API for packet interception, modification, and injection, making it easier for developers to implement complex network processing tasks.


The WinDivert official repository includes example programs such as a network flow tracking application, a simple packet capture and dump application, a simple firewall application, a multi-threaded skeleton application, a socket operations dump program, a TCP stream redirection to a local proxy server program, and a simple URL blacklist filter program. Among these examples, steamdump serves as a good reference for implementing a rogue proxy in kernel mode, netfilter can be used to develop an improved solution for silent EDR, and passthru demonstrates the effective use of multi-threading to enhance performance.  Additionally, I noticed the webfilter example, which can serve as an excellent template for implementing an HTTP packet inspection program. I had an idea: by leveraging WinDivert to create a transparent proxy between the EDR agent and cloud servers, we could decrypt their communication and inspect the telemetry. If the telemetry contains detection or alert information, we could drop the packet to prevent new alerts on the EDR management panel. Otherwise, we could allow the packet to pass through safely. This approach would selectively block only the packets that trigger alerts, allowing the agent to appear online and healthy on the management panel.


Different EDR solutions have varying criteria for being considered unhealthy or offline. For example, base on my observation, Elastic Endpoint and Microsoft Defender for Businese have the following patterns:


  1. If elastic-agent.exe can reach the server but elastic-endpoint.exe cannot, or if the telemetry is corrupted, the agent will be labeled as unhealthy after about 5 minutes.

  2. If both elastic-agent.exe and elastic-endpoint.exe cannot reach the server, the agent will be labeled as offline after about 7 minutes.

  3. For MDE, even if the sensor cannot reach any cloud servers, the endpoint is still labeled as Active. However, the last device update timestamp may reveal the disconnection, at the time of writing, the timestamp is 2 days behind.

By understanding these criteria, we can tailor our transparent proxy solution to ensure the EDR agent maintains an appearance of being online and healthy, while selectively filtering out telemetry that could trigger alerts.


Rogue Proxy Round 2

Considering that telemetry is usually contained in HTTPS packets, it is ideal to inspect them to identify which packets contain alert or detection information. By filtering these specific packets, we can avoid new alerts on the management panel. For implementing a TLS transparent proxy, HttpFilteringEngine and its successor CitadelCore provide excellent solutions. They offer powerful capabilities, including:


  1. Packet Capture and Diversion: Utilize the WinDivert driver and library to automatically capture and divert packets, enabling a transparent TLS-capable proxy.

  2. Trusted Root Certificate Management: Efficiently manage trusted root certificates.

  3. Automatic OS Trust Establishment: Automatically establish trust with the operating system using a one-time root CA and keypair.

  4. Non-HTTP Packet Handling: Allow non-HTTP packets to pass through unimpeded.

  5. Payload Management: Hold back the entire payload of a request or response as needed for detailed inspection and manipulation.

  6. Response Manipulation: Modify the response payload to suit specific requirements.


In theory, these tools could effectively inspect packets containing telemetry for some EDRs. However, some EDR vendors are aware of potential MITM attacks and have implemented countermeasures. For instance, Elastic allows administrators to specify trusted certificates, making it impossible to establish a TLS connection without the correct certificate.

In summary, while inspecting the agent's outbound packets and selectively filtering those containing alert or detection information can be an ideal approach to avoid alerts on the management panel while maintaining the agent's online and healthy status, this approach is not universally applicable. Some vendors have implemented measures to prevent MITM attacks, which limits the effectiveness of this strategy. Therefore, while promising, this approach requires careful consideration of the specific EDR solution in use and its security mechanisms.


I also attempted a side-channel approach to determine the baseline packet size that could contain alert or detection information. However, I couldn't observe any obvious patterns. Sometimes, a small packet might contain alert information, while other times, a large packet might contain only host OS information with no alert or detection data at all. Moreover, the workload required to determine the correct baseline size for every EDR would be substantial. Hence, while identifying and selectively filtering packets that contain alert information is theoretically possible, it is impractical due to the lack of consistent packet size patterns and the heavy workload involved. Each EDR has its own way of packaging telemetry data, making it challenging to establish a universal baseline. 


Improved Telemetry Silence Approach

Although I attempted a more innovative approach—using a TLS transparent proxy to selectively filter packets in order to intercept alert data while maintaining the endpoint's online and healthy status—this method can be rendered ineffective due to possible MITM attack mitigation. Therefore, if we can enhance the evasiveness of EDRSilencer while maintaining its functionality, it would still be considered an improvement. Next, I will elaborate on the features and improvements of the tool EDRPrison.


EDRSilencer hardcodes a list of possible EDR process names, then searches for these processes on the host and retrieves their PIDs. It proceeds by opening handles to these processes using OpenProcess and obtaining the executable addresses of these processes. Persistent WFP filters are then added to these EDR executables. These filters are not automatically removed when the program closes, unless manually cleared. This process has several points that may trigger detection:


  1. Calling OpenProcess to obtain handles to EDR processes.

  2. Persistent WFP filters.


My tool, EDRPrison, similarly hardcodes a list of possible EDR process names, then searches for these processes on the host and retrieves their PIDs.  After obtaining the PIDs, EDRPrison continuously intercepts and retrieves associated PIDs from the packets. It is convenient that the WINDIVERT_ADDRESS structure contains detailed information about a packet, including the remote address, remote port, protocol, PID, and more. In this way, we do not have to call GetExtendedTcpTable frequently to associate a process with its network activity, which is very performance-intensive. By leveraging the information provided by WINDIVERT_ADDRESS, we can directly correlate packets to their originating processes,  reducing the overhead and improving the efficiency of our monitoring and filtering operations. This approach allows us to maintain high performance while ensuring effective packet interception and inspection.

typedef struct
{
    UINT32 IfIdx;
    UINT32 SubIfIdx;
} WINDIVERT_DATA_NETWORK, *PWINDIVERT_DATA_NETWORK;

typedef struct
{
    UINT64 Endpoint;
    UINT64 ParentEndpoint;
    UINT32 ProcessId;
    UINT32 LocalAddr[4];
    UINT32 RemoteAddr[4];
    UINT16 LocalPort;
    UINT16 RemotePort;
    UINT8  Protocol;
} WINDIVERT_DATA_FLOW, *PWINDIVERT_DATA_FLOW;

typedef struct
{
    UINT64 Endpoint;
    UINT64 ParentEndpoint;
    UINT32 ProcessId;
    UINT32 LocalAddr[4];
    UINT32 RemoteAddr[4];
    UINT16 LocalPort;
    UINT16 RemotePort;
    UINT8  Protocol;
} WINDIVERT_DATA_SOCKET, *PWINDIVERT_DATA_SOCKET;

typedef struct
{
    INT64  Timestamp;
    UINT32 ProcessId;
    WINDIVERT_LAYER Layer;
    UINT64 Flags;
    INT16  Priority;
} WINDIVERT_DATA_REFLECT, *PWINDIVERT_DATA_REFLECT;

typedef struct
{
    INT64  Timestamp;
    UINT64 Layer:8;
    UINT64 Event:8;
    UINT64 Sniffed:1;
    UINT64 Outbound:1;
    UINT64 Loopback:1;
    UINT64 Impostor:1;
    UINT64 IPv6:1;
    UINT64 IPChecksum:1;
    UINT64 TCPChecksum:1;
    UINT64 UDPChecksum:1;
    union
    {
        WINDIVERT_DATA_NETWORK Network;
        WINDIVERT_DATA_FLOW    Flow;
        WINDIVERT_DATA_SOCKET  Socket;
        WINDIVERT_DATA_REFLECT Reflect;
    };
} WINDIVERT_ADDRESS, *PWINDIVERT_ADDRESS;

If the retrieved PID is in the list of identified PIDs, it indicates that the packet was initiated by an EDR process and should be blocked or dropped. To achieve better performance, it is advisable to set proper filters, utilize multi-threading techniques, and adopt batch processing as needed to optimize performance.


Tests Against Various EDR Products:

Armed with our theory, we tested our tool against a few EDR products. Limited by the resources available to me, I tested EDRPrison against Elastic Endpoint and MDE on my physical server.


I hardcoded all relevant processes for Elastic Endpoint and MDE, compiled, and ran the program. The program, along with WinDivert, was not detected at all. This is expected, considering they could be perceived as firewall programs from the viewpoint of the EDR.


Next, I ran a few classic malware samples, such as vanilla Mimikatz, Rubeus, etc. These samples were still blocked because, even when the EDR agent is offline, it retains basic functionalities like hash-based signature detection. Additionally, within a few seconds, the number of blocked packets increased significantly, indicating they contained alert data.

Fortunately, we did not see any new alerts on either the Elastic or MDE panels. Even if there are a few local detections, we do not need to worry about them since the SOC folks will not see them on the panel. Additionally, if we run more complicated malware, they may not be prevented due to the lack of machine learning and analysis on the cloud.


Detection and Mitigation:

We have already covered the advantages of EDRPrison, so how can one detect or mitigate this attack? Here are some approaches to consider:


Driver Load Event

If the WinDivert driver is not installed on the system, EDRPrison will install the callout driver upon first execution. Both the OS and the telemetry will log this event. However, driver load events are not rated as high risk by default, so administrators may dismiss them. Additionally, some legitimate software also loads this driver.


Existence of WinDivert64.sys and WinDivert.dll

EDRPrison and other WinDivert-dependent programs require WinDivert64.sys and WinDivert.dll to be present on the disk. Typically, these files are not detected as malware, but some vendors recognize that WinDivert can be used for malicious purposes. For example, in 2019, the Divergent malware used NodeJS and WinDivert in fileless attacks, details of which can be found here. Additionally, according to an issue, some anti-cheat systems refuse to run games if they detect that WinDivert is installed.


WinDivert Usage Detection Tool

The author of WinDivert also wrote a program, WinDivertTool, to detect processes currently using WFP. The output is very verbose. WinDivertTool can even terminate relevant processes or uninstall WinDivert.

Packet drop/block actions against EDR processes

Elastic has a detection rule for WFP filter abuse, which can be found here. It detects packet drop or block actions targeting any security product process. Windows provides event logs based on this action, detailed here.


Review Registered WFP Provider, Filters, and Callout.

The tool WFPExplorer can also be used for forensic purposes. It provides a GUI for users to review active WFP sessions, registered callouts, providers, and filters. This tool can help in identifying and analyzing any suspicious WFP configurations.

Adminless

In a conversation with jdu2600, it was mentioned that future protections could further secure the installation of new drivers, potentially addressed under the Adminless feature. More details on this feature can be found in this presentation. Benefits of Adminless can be used to limit the installation and execution of unauthorized drivers, making it harder for offensive drivers, such as those used by EDRPrison, to operate. As Adminless becomes widely adopted, it could significantly restrict the ability to install and leverage such drivers, thereby mitigating this vector of attack.


Red Team's Revenge: Subvert The Above Detection:

From a red teamer's perspective, how can we subvert some of the above detections? Depending on the security configurations of the target organization, there are potential workarounds. However, administrator privilege is still required at a minimum.


Avoiding WinDivert

If WinDivert is flagged as malicious within an organization, we could seek alternatives that meet the following criteria:


  1. The callout driver is signed.

  2. It allows packet interception, reinjection, and other manipulation.

  3. It is open-source.

  4. It is less known for potential malicious use.

  5. It has excellent documentation.


I have already identified a suitable alternative. Can you find it?


Avoiding External Driver

If all external drivers are considered unauthorized unless approved, it is challenging but possible to reverse engineer an installed or built-in WFP callout driver and reuse its callout functions. Many security software solutions, such as Elastic Endpoint, Norton, and ESET, have their WFP callout drivers for traffic logging, DPI, parental control, and other purposes. This article provides an excellent example of reverse engineering a closed-source driver.


Evading Elastic Rule

The Elastic rule we discussed can identify packet block/drop actions targeting a security product's process.

sequence by winlog.computer_name with maxspan=1m
 [network where host.os.type == "windows" and
  event.action : ("windows-firewall-packet-block", "windows-firewall-packet-drop") and
  process.name : (
        "bdagent.exe", "bdreinit.exe", "pdscan.exe", ...... "taniumclient.exe"
    )] with runs=5

However, if we do not block or drop a packet but instead redirect or proxy it to a dummy proxy server, the criteria are not met. The streamdump example provides a relevant implementation: streamdump.c.


Summary:

Utilizing WFP in evasion is not a novel tactic, but in the above sections, we explained the necessary background and relevant concepts, revisited a few exemplary projects, and shared my attempts that led me into a rabbit hole. We introduced WinDivert and my tool, EDRPrison, highlighting the improvements EDRPrison brings, such as more flexible filters, reduced reliance on sensitive actions, and fewer detection triggers. We conducted a simple test and analyzed potential detections against EDRPrison. Additionally, we discussed how, as red teamers, we could subvert some of these detections. By leveraging these advanced strategies and tools, we can enhance our evasion techniques to maintain operational stealth and effectiveness.


References:

The following resources inspired and helped me a lot during my research. I extend my thanks to all the authors:


5,812 views0 comments

Comments


bottom of page