Defender's Remote Connections Blind Spot: FourToSixMapping

Why filtering on RemoteIPType == "Public" causes Internet connections to go under the radar.

If you work with Microsoft Defender (XDR) and Sentinel, chances are you already leveraged the juicy log events from DeviceNetworkEvents table.

In that table, besides the usual network artifacts such as IP addresses, ports and URLs, one can also find the processes and the accounts tied to network communications which turns that into a golden pot for defenders.

Whether you've used it for writing hunting or detection queries, you surely don't want to miss the opportunity to inspect all external network communications.

This article is about exposing a blind spot I'm sure many defenders haven't noticed yet as a public service since this use case is utterly important.

Over-constrained queries: the recipe for FNs

As in any code, a simple typo is enough to ruin the whole logic of a program. Writing in SPL or KQL is no different, every query constraint or filter might introduce detection gaps.

A false-negative (FN) is the technical term used in this scenario: an attack happens and there's a dedicated detection for it, however, the alert does not fire since the query logic doesn't capture the logs related to the attack.

And here's another tradeoff in detection engineering:

Knowing how to design a detection so that it avoids picking on too many legitimate scenarios (false-positives) while making sure most, if not, all events tied to real threats (true-positives) are captured.

Learning It — The Hard Way

I had recently participated in a Purple Team (PT) exercise where part of the simulation involved an attacker deploying a binary that establishes a covert command-and-control (C2) channel to bypass web proxy controls.

Tracking external communications in a large deployment can be really challenging, so one of the strategies is about splitting the detections by traffic or services, for instance:

  • Remote Management Services (SSH, RDP, VNC)
  • HTTPS (TCP 443)
  • DNS and so on…

That not only makes it a bit easier to design baselines but also speeds up triage and response since the scoped is more specific.

Back to the PT exercise. One of the objectives was to map not only the logs generated during the simulation, but also the alerts triggered.

As it turned out, one of the alerts I expected to fire for that stage of the attack chain actually never did! 

The Culprit: RemoteIPType

The main issue was that the query contained the following constraint which was filtering out any other RemoteIPType values:

| where RemoteIPType == “Public”

So how was the external communication logged if not by presenting a "Public" record since the IP address was a public one?

The answer lies in the way Defender platform handles IP addresses format.

FourToSixMapping

Modern Windows applications often create dual-stack sockets, allowing them to communicate over both IPv4 and IPv6.

FourToSixMapping is a value you may encounter in Microsoft Defender XDR (DeviceNetworkEvents.RemoteIPType) that indicates that the IP address is an IPv4-mapped IPv6 address.

That's actually backed by the IPv6 addressing RFC:

Are the Robots aware of that?

If you follow this blog, you've probably checked how the top LLM chatbots approached a hunting scenario I was faced with, if not, go check it out!

So why not checking in this scenario too? So here is the prompt:

In Defender (XDR), there’s a table called DeviceNetworkEvents where events related to network activity are logged. Using KQL, how can I check for ALL events related to external communication towards public destination IP addresses?

And here's the answer:

DeviceNetworkEvents
| where isnotempty(RemoteIP)
| where not(ipv4_is_private(RemoteIP))
| where RemoteIP != "127.0.0.1" and RemoteIP !startswith "fe80" and RemoteIP != "::1"
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteIP, RemotePort, RemoteUrl, LocalIP, LocalPort, Protocol, ActionType
| order by Timestamp desc

That doesn't seem to be correct since the ipv4_is_privateevaluates to null when used with FourToSixMapping values. That can be verified by inspecting the result of the query below:

datatable(IPAddress:string)
[
"192.168.0.1",
"::ffff:8.8.8.8"
]
| extend IsPrivate = ipv4_is_private(IPAddress)

Result:

In KQL, null is neither true nor false.

Therefore, the suggested query would have missed those events! 

The Fix

I have crafted the following KQL query for you to verify theFourToSixMapping events in your tenant by inspecting allRemoteIP values within the 100th last hour without a corresponding Public entry:

DeviceNetworkEvents
| where Timestamp > ago(101h)
| where Timestamp < ago(100h)
| where RemoteIPType in ("Public", "FourToSixMapping")
| extend IP =
iff(RemoteIPType == "FourToSixMapping",
replace_string(RemoteIP, "::ffff:", ""),
RemoteIP)
| where not(ipv4_is_private(IP) or ipv4_is_in_range(IP, "127.0.0.0/8"))
| where ActionType == "ConnectionSuccess"
| summarize TypeCount=dcount(RemoteIPType), arg_min(Timestamp, RemoteIPType, RemoteIP, Protocol, RemotePort, RemoteUrl, InitiatingProcessParentFileName, InitiatingProcessCommandLine) by IP
| where TypeCount==1 and RemoteIPType=="FourToSixMapping"
| project-reorder IP, RemoteIP

One way to make sure you cover thoseFourToSixMapping events in your hunting or detection queries is to normalize theRemoteIP values before consuming them by removing the '::ffff:' prefix:

| extend RemoteIP =
iff(RemoteIPType == "FourToSixMapping",
replace_string(RemoteIP, "::ffff:", ""),
RemoteIP)

Are you aware of any other gaps? Drop in the comments or reach out!

Written by Alex Teixeira, Detection Engineering & Security Analytics SME

Don’t forget to subscribe and follow me to get notified about new stories!

Defender's Remote Connections Blind Spot: FourToSixMapping was originally published in Detect FYI on Medium, where people are continuing the conversation by highlighting and responding to this story.

Introduction to Malware Binary Triage (IMBT) Course

Looking to level up your skills? Get 10% off using coupon code: MWNEWS10 for any flavor.

Enroll Now and Save 10%: Coupon Code MWNEWS10

Note: Affiliate link – your enrollment helps support this platform at no extra cost to you.

Article Link: Medium