Adversaries may be using a rootkit to hide outbound network connections, evading detection by Microsoft Defender for Endpoint. SOC teams should proactively hunt for this discrepancy in Azure Sentinel to identify potential persistent threats and exfiltration activities.
KQL Query
// DETECTION STRATEGY: Rootkit & BYOVD Ring-0 Network Bypass Detection via Firewall-to-EDR Telemetry Delta.
// THE MECHANIC: Advanced adversaries operating in Ring-0 via BYOVD can unlink WFP callouts, blinding MDE to outbound network telemetry while the host otherwise appears healthy.
// THE RESILIENCE: By comparing "Network Truth" (out-of-band firewall logs) against "Host Truth" (EDR telemetry), we trap the adversary. Endpoint tampering cannot hide the physical packet leaving the perimeter.
// Disclaimer: This query can be computationally expensive if run too frequently.
// NOTE ON ANALYTICS RULE CONVERSION, SCOPING & COMPUTE:
// If converting this Hunting Query into a scheduled Analytics Rule, you MUST do the following to preserve compute and prevent false positives:
// 1. Scope 'activeMdeNodes' strictly to "Crown Jewel" critical servers (e.g., Domain Controllers, PKI) to limit the left-anti join compute footprint.
// 2. Introduce an ingestion offset using "ago()" (e.g., | where TimeGenerated between (ago(24h) .. ago(1h))) to account for MDE telemetry batching delays and prevent "Ghost Deltas".
// Time framing: Hunting blade passes the selected time automatically via StartTimeISO and EndTimeISO
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
// Thresholds: Avoid micro-session noise, DHCP churn, or transient TCP handshakes.
let byteThreshold = 50000;
let connThreshold = 5;
// Tier 1: Firewall Perimeter Identification
// ADAPTATION: Analysts MUST adjust these arrays to match their specific perimeter security stack.
let firewallVendors = dynamic(["Palo Alto Networks", "Fortinet", "Check Point", "Cisco", "Zscaler"]);
let firewallProducts = dynamic(["Firewall", "PAN-OS", "FortiGate"]);
// STEP 1: Identify Active MDE Endpoints (Base Query)
// Why: We must ensure the source IP belongs to a machine actively running MDE.
// Note: Without this, unmanaged devices (IoT, BYOD) will trigger massive false positives.
let activeMdeNodes = DeviceNetworkEvents
| where Timestamp between (starttime .. endtime)
| where isnotempty(LocalIP)
| distinct LocalIP;
// STEP 2: Establish "Network Truth" via Perimeter Appliances
let firewallTruth = ASimNetworkSessionLogs
| where TimeGenerated between (starttime .. endtime)
| where isnotempty(SrcIpAddr) and isnotempty(DstIpAddr)
| where NetworkProtocol in~ ("TCP")
// CONDITION A: Verify the log comes from a perimeter hardware/virtual appliance
| where EventVendor has_any (firewallVendors) or EventProduct has_any (firewallProducts)
// CONDITION B: Filter out internal-to-internal traffic to focus exclusively on external C2/Exfiltration
| where ipv4_is_private(DstIpAddr) == false
and ipv4_is_private(SrcIpAddr) == true
// CONDITION C: Ensure the source IP is an active MDE node (Filter early for compute efficiency)
| where SrcIpAddr in (activeMdeNodes)
// Preserving Context: Use make_set to keep analyst context without blowing up row counts
| summarize firewallConnCount = count(),
totalBytes = sum(tolong(NetworkBytes)),
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
destinationPorts = make_set(DstPortNumber, 100),
appProtocols = make_set(NetworkApplicationProtocol, 100)
by SrcIpAddr, DstIpAddr
| where totalBytes >= byteThreshold or firewallConnCount >= connThreshold;
// STEP 3: Establish "Host Truth" via EDR Telemetry
let mdeTruth = DeviceNetworkEvents
| where Timestamp between (starttime .. endtime)
| where isnotempty(LocalIP) and isnotempty(RemoteIP)
| where Protocol in~ ("TCP")
| where ipv4_is_private(RemoteIP) == false
// Join Optimization: Use distinct to drastically reduce memory footprint before the anti-join
| distinct LocalIP, RemoteIP
| project SrcIpAddr = LocalIP, DstIpAddr = RemoteIP;
// STEP 4: Capture Endpoint Context & Strict Schema Parsing
let deviceMapping = DeviceNetworkInfo
| where Timestamp between (starttime .. endtime)
| where isnotempty(IPAddresses) and isnotempty(DeviceName)
// Expand the JSON array of IP addresses so each IP gets its own row
| extend parsedIPs = todynamic(IPAddresses)
| mv-expand parsedIPs
| extend LocalIP = tostring(parsedIPs.IPAddress)
| where isnotempty(LocalIP)
// Get the most recent heartbeat for each IP address to account for DHCP churn
| summarize arg_max(Timestamp, DeviceName, DeviceId) by LocalIP
| project SrcIpAddr = LocalIP, RawDeviceName = DeviceName, DeviceId = tostring(DeviceId);
// STEP 5: Execute the Delta Anti-Join
firewallTruth
// Left Anti-Join: Keep ONLY the connections seen by the Firewall that are completely missing from MDE
| join hint.strategy=shuffle kind=leftanti (
mdeTruth
) on SrcIpAddr, DstIpAddr
// Enrich with Device Details
| join kind=leftouter (
deviceMapping
) on SrcIpAddr
// EXCLUSION: Built-in tuning guidance for org-specific baselines.
// | where DstIpAddr !in~ ("8.8.8.8", "1.1.1.1") // Example: Exclude common DNS bypasses
// Data Sanitization
// Rename generic schema columns into distinct, narrative evidence
| extend timestamp = StartTime,
InternalIP = tostring(SrcIpAddr),
DestinationIP = tostring(DstIpAddr),
EvasiveConnectionCount = firewallConnCount,
TotalBytesSent = totalBytes,
DestinationPorts = destinationPorts,
HostName = tostring(split(RawDeviceName, ".")[0]),
DnsDomain = iff(RawDeviceName contains ".", substring(RawDeviceName, indexof(RawDeviceName, ".") + 1), "")
// ANALYST ACTION: Check 'DestinationIP' against Threat Intel.
// If the IP is a known SaaS/Cloud provider, verify if frequency matches beaconing patterns or if the 'TotalBytesSent' aligns with
// anomalous Rclone/exfiltration activity. Run a live response memory scan for hidden drivers.
// Ordering Visual Hierarchy
// Drop raw artifacts and display the chronology left-to-right
| project timestamp,
StartTime,
EndTime,
HostName,
DnsDomain,
DeviceId,
InternalIP,
DestinationIP,
EvasiveConnectionCount,
TotalBytesSent,
DestinationPorts,
appProtocols
| project-reorder timestamp,
HostName,
InternalIP,
DestinationIP,
EvasiveConnectionCount,
TotalBytesSent,
DestinationPorts,
appProtocols
| order by TotalBytesSent desc
id: 564bf64a-bada-4c6b-8821-53138d660f78
name: Potential rootkit network activity missing from MDE
description: |
Identifies outbound network connections logged by perimeter firewalls that are entirely missing from Microsoft Defender for Endpoint (MDE) telemetry. This discrepancy strongly indicates a threat actor operating in kernel space, to hide C2 traffic.
description-detailed: |
Advanced adversaries operating in Ring-0 via BYOVD can unlink WFP (Windows Filtering Platform) callouts or inject raw frames into NDIS. This completely blinds EDR sensors like MDE to outbound network telemetry, while the host otherwise appears healthy. By comparing "Network Truth" (out-of-band firewall appliance logs) against "Host Truth" (EDR telemetry), we trap the adversary in a paradox. Kernel-level tampering on the endpoint cannot hide the physical packet leaving the network boundary.
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceNetworkEvents
- DeviceNetworkInfo
- connectorId: PaloAltoNetworks
dataTypes:
- CommonSecurityLog
- connectorId: Fortinet
dataTypes:
- CommonSecurityLog
- connectorId: Cisco
dataTypes:
- CommonSecurityLog
- connectorId: CheckPoint
dataTypes:
- CommonSecurityLog
tactics:
- DefenseEvasion
- CommandAndControl
- Exfiltration
relevantTechniques:
- T1562.001
- T1562.004
- T1011
query: |
// DETECTION STRATEGY: Rootkit & BYOVD Ring-0 Network Bypass Detection via Firewall-to-EDR Telemetry Delta.
// THE MECHANIC: Advanced adversaries operating in Ring-0 via BYOVD can unlink WFP callouts, blinding MDE to outbound network telemetry while the host otherwise appears healthy.
// THE RESILIENCE: By comparing "Network Truth" (out-of-band firewall logs) against "Host Truth" (EDR telemetry), we trap the adversary. Endpoint tampering cannot hide the physical packet leaving the perimeter.
// Disclaimer: This query can be computationally expensive if run too frequently.
// NOTE ON ANALYTICS RULE CONVERSION, SCOPING & COMPUTE:
// If converting this Hunting Query into a scheduled Analytics Rule, you MUST do the following to preserve compute and prevent false positives:
// 1. Scope 'activeMdeNodes' strictly to "Crown Jewel" critical servers (e.g., Domain Controllers, PKI) to limit the left-anti join compute footprint.
// 2. Introduce an ingestion offset using "ago()" (e.g., | where TimeGenerated between (ago(24h) .. ago(1h))) to account for MDE telemetry batching delays and prevent "Ghost Deltas".
// Time framing: Hunting blade passes the selected time automatically via StartTimeISO and EndTimeISO
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
// Thresholds: Avoid micro-session noise, DHCP churn, or transient TCP handshakes.
let byteThreshold = 50000;
let connThreshold = 5;
// Tier 1: Firewall Perimeter Identification
// ADAPTATION: Analysts MUST adju
| Sentinel Table | Notes |
|---|---|
ASimNetworkSessionLogs | Ensure this data connector is enabled |
DeviceNetworkEvents | Ensure this data connector is enabled |
imNetworkSession | Ensure this data connector is enabled |
Scenario: Scheduled System Maintenance Task
Description: A legitimate scheduled task (e.g., schtasks.exe) is configured to make outbound network connections to a known internal server for system updates or patching.
Filter/Exclusion: Exclude connections initiated by schtasks.exe to internal IP ranges or known update servers (e.g., 10.0.0.0/8).
Scenario: Microsoft Endpoint Configuration Manager (MECM) Client Communication
Description: The MECM client communicates with the management server to sync policies, inventory, and software updates. These connections may not be logged by MDE.
Filter/Exclusion: Exclude connections to MECM management servers (e.g., 10.10.0.100) or to known MECM ports (e.g., port 80, 443, 8530).
Scenario: PowerShell Script for Log Collection
Description: A PowerShell script (e.g., PowerShell.exe) is used to collect logs or send data to a central log server. This activity may not be captured by MDE.
Filter/Exclusion: Exclude connections initiated by PowerShell.exe to internal log servers (e.g., 10.20.0.50) or to known log aggregation tools (e.g., Splunk, ELK).
Scenario: Remote Desktop Services (RDP) Session
Description: An RDP session is established from an internal user to a remote server, and the connection is not captured by MDE due to network segmentation or proxy settings.
Filter/Exclusion: Exclude connections from known internal IP ranges (e.g., 192.168.0.0/16) to internal servers (e.g., `10.