How to block outbound traffic by domain name in Windows Filtering Platform (WFP) from Kernel Mode?

Hi all,

I'm developing a Windows driver using Windows Filtering Platform (WFP).

My design idea is:

  • The user enters a domain name in User Mode (UM).

  • The domain is passed down to Kernel Mode (KM).

  • In KM, I maintain a blocklist of domain names.

  • If a connection matches a blocked domain, I return FWP_ACTION_BLOCK.

However, I’m not sure how to obtain the domain name inside Kernel Mode.

As far as I understand, WFP layers like FWPM_LAYER_ALE_AUTH_CONNECT_V4 only provide IP address and port information, not the original domain name.

My questions:

  1. Is there any WFP layer that provides the domain name directly in Kernel Mode?

  2. Is it possible to extract the domain from DNS traffic (e.g., by inspecting DNS queries)?

  3. What is the recommended architecture for domain-based blocking in WFP?

    • Should DNS be monitored separately and mapped (domain → IP)?

    • Should blocking be performed after DNS resolution using cached mappings?

  4. Is there any supported way to achieve domain-based filtering purely in KM?

Any guidance or best practice references would be greatly appreciated.

Thanks!

You do this by parsing the headers in the HTTP and HTTPS requests. The Host: header tells you the name of the server they’re trying to reach.

However, you can do that task much simpler by using a proxy. You don’t need any kernel programming at all.

And for DNS, you can do that without any programming at all, by setting up your DNS server to return garbage addresses (127.0.0.1, maybe) for all of the domains you want to block.

based on your questions, I think I can make some inferences. You are probably trying to monitor or block traffic towards websites. probably using the HTTP protocol. probably expecting TCP and either port 80 or 443.

and you probably expect that ‘normal’ programs like browsers will first use DNS to find an IP address and then attempt a TCP connection to that address

That represents only a tiny fraction of all possible kinds of network traffic. And even within that, modern HTTP traffic uses a hybrid of TCP and UDP (QUIC protocol), HTTP connections need not be made via DNS names. The can be made to IP addresses directly, or via other naming schemes.

If you want to poke around as a learning exercise, then absolutely. but as a commercial product there will be severe limitations.

if you can clarify your specific objectives, we can probably help you better

Regarding parsing the HTTP and HTTPS headers, I have already tried implementing that approach.

For DNS traffic, when protocol == IPPROTO_UDP and remotePort == 53, I parse the domain name from the DNS packet, and that works correctly.

For HTTPS traffic, I check protocol == IPPROTO_TCP and remotePort == 443. However, I’m currently unable to extract the domain name from the HTTPS packets.

Is my condition incorrect? If so, what would be the correct way to identify and parse the domain from HTTPS traffic?

From what I understand, HTTPS traffic is encrypted, so the HTTP headers (including the Host header) are not visible in plain text. Is the correct approach to parse the SNI (Server Name Indication) field from the TLS ClientHello message instead? If so, could you clarify how this should be properly detected and parsed at the packet level?

I am developing a WFP-based driver that already supports blocking and unblocking IPv4 and IPv6 traffic. Now I want to add a feature to block traffic by domain name.

At the moment, my goal is just to build a test driver to validate the planned functionality. Later, I may integrate this feature into an EDR solution.

You have the IP address. The IP address was almost certainly returned from a recent DNS request. Your DNS handler would need to create a “bad IP” list.

However, a single domain can resolve to multiple different IP addresses. If I block only one IP of that domain, I can still access it using the domain name, because DNS may return a different IP address when resolving or even when pinging the same domain.

Regarding the “bad IP list”, my driver already supports blocking both IPv4 and IPv6 addresses.

Yes, but you’re not thinking this through. HTTPS requests are not sent to a domain name. They are always sent to an IP address, just like ALL TCP requests. The body of the request CONTAINS a domain name, but the browser has to convert that to an IP address before it can write to TCP.

You will never see an HTTPS request with an IP address that did not come from a previous DNS request.

I understand that TCP connections are always made to an IP address. However, from a filtering perspective, if we extract the domain name from DNS resolution or from the TLS SNI field and then enforce a block policy, would that effectively prevent connections to that domain? Or are there scenarios where the connection could still succeed?

A connection can still be made with IP address even if DNS is blocked. You would just prevent name resolution.

Theoretically, a motivated user could do a DNS lookup on another computer and use a URL using that IP address. Or put an entry in etc/hosts using a fake domain name mapped to the real address. Not all web sites handle IP URLs correctly, although most do.

But what’s the alternative? How would you prevent that? A user who is willing to go that far could just install an ssh tunnel. There’s a serious cost/benefit analysis to be done here. You can easily handle the simple cases, and that will keep the honest man honest. If you’re facing a hacker, maybe that’s a job for HR rather than IT.

IMHO EDR is simply malware by another name. But I will give some basic advise

When trying to monitor / block HTTP requests to a DNS domain, you have an N:N problem. Multiple IP addresses can be used for a single DNS host, and a single IP address can host multiple DNS names.

There are name resolution protocols other than DNS. And there is a whole class of valid URL that does not use DNS names. A simple example is a login to a home user’s router via a URL like http://192.168.0.1. There are many others. Netbios names especially localhost, IPv6 addresses are also common.

And you are assuming that the application will even attempt name resolution before trying to make the connection.

So is there a practical way to handle the majority of real-world cases?
My current idea is: when a user specifies a domain to block, the system would continuously resolve that domain to obtain all associated IP addresses, then push that IP list down to the kernel to block outbound connections to those IPs.
Would that approach be considered reasonable for handling most scenarios?

The far easier method is for your tool to modify c:\windows\system32\drivers\etc\hosts and add a mapping from your blocked domains to 127.0.0.1. That way, they’ll never see a correct IP address. No kernel work required.

1 Like

Thank you all for answering my questions and providing suggestions and solutions. :blush: