
What is a DNS sinkhole?
A DNS sinkhole is a DNS server, or a set of rules on one, that gives false answers for specific domains on purpose, so that traffic meant for a malicious or unwanted destination goes somewhere harmless instead. Typically the sinkhole returns the IP address of a server you control, or a non-routable address, in place of the domain's real address. Malware trying to reach its command server, or a user clicking a phishing link, ends up talking to the sinkhole, which means the connection fails safely and, just as importantly, you get a record of which device tried to make it.
Sinkholes are used in two quite different settings: inside organisations, as a defensive control and detection tool, and on the wider internet, where security researchers and law enforcement take over malicious domains to disrupt botnets. In this article you'll learn how a sinkhole works, how it differs from simply blocking a domain, the famous cases where sinkholing stopped major outbreaks, and how to build a small internal sinkhole with Unbound and a logging server.
How a DNS Sinkhole Works
Every connection to a domain starts with a DNS lookup. A sinkhole takes advantage of that by sitting in the resolution path and changing the answer for selected names.
- A device asks for a domain: For example, malware on a laptop looks up
update-check.example.net, which threat intelligence has linked to a botnet. - The resolver checks its sinkhole list: The domain matches.
- The resolver returns the sinkhole address: Instead of the attacker's real server, it answers with
192.0.2.66, an internal server you run. - The device connects to the sinkhole: The malware sends its HTTP request, or opens a raw TCP connection, to your server.
- The sinkhole logs it: It records the source IP, the time, the port and any request details, then returns nothing useful.
The attacker never receives the connection, so the malware can't download instructions or send stolen data. Meanwhile your security team has a precise list of devices that need investigating.
Sinkholing Versus Simple Blocking
The terms overlap and people use them loosely, but there's a useful distinction:
| Approach | Answer returned | Connection attempt | What you learn |
|---|---|---|---|
Block with NXDOMAIN | "Domain doesn't exist" | Never made | Only that a lookup happened, from DNS logs |
Block with 0.0.0.0 | Null address | Fails locally | Only that a lookup happened |
| Sinkhole to a server | Address of your sinkhole | Made to your server | Lookup, plus port, protocol and request contents |
Returning NXDOMAIN or 0.0.0.0 is strictly a form of sinkholing too, because the traffic disappears into nowhere. Pointing the domain at a real listening server is the more powerful form, because the connection itself tells you more. Many malware families retry endlessly when DNS fails, which only shows up as repeated lookups. When they reach a server, they often reveal their protocol, a bot ID or the kind of data they're trying to send, all of which helps incident responders.
Why DNS Logs Alone Aren't Always Enough
In many networks, workstations don't query the main resolver directly. They go through a local DNS server, such as a Windows domain controller, which forwards to the filtering resolver. The filtering resolver then sees every query as coming from the domain controller, not from the infected laptop. A sinkhole server gets the connection directly from the laptop, so it identifies the real source even when DNS logs can't.
Internet-Scale Sinkholes
Sinkholing became well known through large takedown operations, where researchers or law enforcement take control of domains that malware depends on.
How a Takedown Sinkhole Works
When a botnet's command servers use domain names, whoever controls those domains controls where bots connect. Researchers and authorities can gain control in a few ways:
- Registering unregistered domains: If the malware uses a domain the attacker hasn't registered yet, a researcher can register it first.
- Court orders and registry cooperation: Authorities can have registries transfer or redirect malicious domains.
- Predicting generated domains: Malware using a domain generation algorithm (DGA) produces a predictable list of domains each day. Once the algorithm is reverse-engineered, defenders can register or block upcoming domains before the attacker does.
The domain is then pointed at a sinkhole server run by the researchers. Bots connect to it, and the operators collect the IP addresses of infected machines. They share those lists with ISPs and national CERTs, who notify the owners. Organisations such as The Shadowserver Foundation distribute this kind of data for free to network owners.
Notable Examples
- Conficker (2009): The Conficker worm generated thousands of candidate domains a day. An industry group known as the Conficker Working Group worked with registries to block or register those domains in advance, cutting the botnet off from its controllers.
- WannaCry (2017): The WannaCry ransomware checked whether a particular unregistered domain resolved before encrypting files. A researcher registered the domain and pointed it at a sinkhole, which acted as a kill switch and stopped the malware spreading further. The sinkhole logs also showed where infections were.
- Avalanche (2016): A coordinated international operation took down the Avalanche infrastructure used by many malware families, sinkholing several hundred thousand domains in the process.
These operations show both sides of a sinkhole: it disrupts the attack and produces a map of who's infected.
Building an Internal Sinkhole
For your own network, a sinkhole needs two parts: a resolver that redirects selected domains, and a server that receives and logs the redirected connections.
Step 1: Pick a Sinkhole Address
Choose an internal IP address that isn't used for anything else, and that's only reachable from inside your network. In these examples, the sinkhole server is 192.0.2.66; in a real network you'd use an address from your own private range.
Step 2: Redirect Domains in Unbound
Unbound's redirect local zone type sends every name in a zone, including all subdomains, to the data you specify. Create /etc/unbound/sinkhole.conf:
server:
local-zone: "update-check.example.net." redirect
local-data: "update-check.example.net. 60 IN A 192.0.2.66"
local-zone: "c2-panel.example.org." redirect
local-data: "c2-panel.example.org. 60 IN A 192.0.2.66"
log-local-actions: yes
With redirect, a lookup for x7.update-check.example.net also returns 192.0.2.66, which matters because malware often uses random subdomains. The short 60-second TTL means you can remove an entry quickly if you make a mistake.
Include the file from unbound.conf, then check and reload:
sudo unbound-checkconf
sudo systemctl reload unbound
Confirm the redirect from a client:
dig @192.168.1.53 abc.update-check.example.net A +noall +answer
abc.update-check.example.net. 60 IN A 192.0.2.66
If you run BIND or another resolver that supports Response Policy Zones, you can achieve the same with an RPZ rule that rewrites the answer to the sinkhole address, which also lets you subscribe to commercial feeds.
Step 3: Run a Logging Server
The sinkhole server's job is to accept connections, log them and give nothing back that could help the malware. Many malware families talk HTTP, so a small HTTP listener catches a lot. This Python script logs each request as a JSON line:
#!/usr/bin/env python3
"""Minimal HTTP sinkhole logger. Run on the sinkhole host only."""
import json
from datetime import datetime, timezone
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
LOG_FILE = "/var/log/sinkhole/http.jsonl"
class SinkholeHandler(BaseHTTPRequestHandler):
def _record(self) -> None:
entry = {
"time": datetime.now(timezone.utc).isoformat(),
"client": self.client_address[0],
"method": self.command,
"host": self.headers.get("Host", ""),
"path": self.path,
"user_agent": self.headers.get("User-Agent", ""),
}
with open(LOG_FILE, "a") as log:
log.write(json.dumps(entry) + "\n")
self.send_response(404)
self.send_header("Content-Length", "0")
self.end_headers()
do_GET = do_POST = do_PUT = do_HEAD = _record
def log_message(self, format, *args) -> None:
pass # silence default stderr logging
if __name__ == "__main__":
ThreadingHTTPServer(("0.0.0.0", 80), SinkholeHandler).serve_forever()
It deliberately never reads or stores request bodies, which could contain sensitive data that malware is trying to exfiltrate, and it answers every request with an empty 404.
Not all malware uses HTTP, and HTTPS connections will fail at the TLS handshake. To record every connection attempt on any port, capture packets on the sinkhole host as well:
sudo tcpdump -ni eth0 -w /var/log/sinkhole/conn-%Y%m%d.pcap -G 86400 \
'dst host 192.0.2.66 and (tcp[tcpflags] & tcp-syn != 0 or udp)'
This writes a new capture file every day containing the first packet of each TCP connection and all UDP traffic to the sinkhole. For HTTPS, the TLS Client Hello in the capture usually still reveals the hostname the device was trying to reach.
Step 4: Turn Logs Into Action
The logs are only useful if someone looks at them. A quick summary of which clients hit the sinkhole most often:
jq -r '[.client, .host] | @tsv' /var/log/sinkhole/http.jsonl \
| sort | uniq -c | sort -rn | head -20
412 10.20.4.118 update-check.example.net
37 10.20.7.52 c2-panel.example.org
3 10.20.2.9 c2-panel.example.org
A device making hundreds of beacon-like requests is a priority for investigation. Match the internal IP to a hostname or user through DHCP leases or your endpoint management tool, isolate the device and start your incident response process. In a larger environment, ship these logs to your SIEM and alert on any sinkhole hit.
Sinkhole Best Practices
- Keep the sinkhole internal: The sinkhole address must not be reachable from the internet, and the server should only accept traffic from your own networks.
- Return nothing useful: Never serve content that could be mistaken for real command server responses. An empty error is safest.
- Don't store payloads: Requests can include stolen credentials or personal data. Log metadata, not bodies.
- Use short TTLs: Low TTLs on sinkhole records let you change or remove entries quickly.
- Force DNS through your resolver: Block outbound DNS (TCP and UDP 53, and TCP 853) from everything except your resolver, otherwise malware can simply ask a public resolver for the real answer.
- Consider encrypted DNS: Malware and browsers using DNS over HTTPS to an outside provider bypass your sinkhole. Control DoH on managed devices and watch for traffic to known DoH endpoints.
- Review and expire entries: Domains change hands. Remove entries that are no longer malicious so you don't break a legitimate site months later.
- Monitor the sinkhole itself: If the sinkhole server is down, you lose visibility silently. Health-check it like any other production service.
Limitations
A sinkhole can only redirect traffic that goes through DNS and through your resolver. Malware that connects directly to hardcoded IP addresses, or that uses legitimate cloud services for command and control, won't be caught by domain-based rules. Sinkholing also relies on knowing which domains are bad, so it's only as good as the threat intelligence behind it. Treat it as a detection and disruption tool that complements endpoint protection and network monitoring, not as a replacement for them.
FAQ: DNS Sinkholes
They're closely related. DNS filtering is the broad practice of refusing to resolve unwanted domains. A sinkhole is a specific technique where blocked domains are pointed at an address you control, often a logging server, so you can see which devices tried to connect.
Yes. Pi-hole describes itself as a DNS sinkhole. By default it answers blocked domains with a null address such as 0.0.0.0, which sinks the traffic without a logging server, and its query log shows which device asked for each blocked domain.
For a simple block, 0.0.0.0 and :: are common. For a detection sinkhole, use a dedicated internal address for a server you run, which is reachable only from inside your network.
Some can. Malware may notice that its command server never sends valid responses, or that the domain resolves to a private address. That's one reason to treat sinkholing as a way to disrupt and detect infections, followed by proper clean-up of the device.
Running a sinkhole on your own network for your own devices is a normal security control. Taking over domains on the public internet is different and is done by researchers, registries and law enforcement under legal processes or registry agreements.
Treat it as a possible infection. Identify the device and user, isolate it from the network if the activity looks like malware, run an investigation with your endpoint tools, and check whether other devices show the same pattern.
Conclusion
A DNS sinkhole deliberately answers lookups for malicious domains with an address you control. That cuts malware off from its command servers and stops users reaching harmful sites, and when the sinkhole address points to a logging server, it also tells you exactly which devices are infected. The same idea, applied on a much larger scale, has helped researchers and authorities disrupt some of the biggest botnets and ransomware outbreaks.
You can build a useful internal sinkhole with a resolver redirect and a small logging server. Keep it internal, log metadata rather than payloads, make sure every device has to use your resolver, and route sinkhole hits into your incident response process so each one is investigated.


