A hands-on, code-first guide to fingerprinting fake DNS infrastructure across Red Team, Blue Team, and CTI eyes.
TL;DR
Attackers and threat analysts don’t waste time hunting for
Server: FakeDNSsoftware strings. They test how a server handles broken state, illegal OPCODES, and reserved domains like.invalid. If two different query IDs return a bitwise-identical UDP payload, your decoy isn't deceiving anyone—it's just a tape recorder.Below are some of the strategies that we implemented while building the Honeypot Auditor , 8-step protocol verification sequence with Scapy code and
digcommands followed by how Red, Blue, and CTI teams translate those wire results into actionable operations.
Press enter or click to view image in full size
For start, you can query an unknown DNS listener for a nonsense hostname under .invalid. If it instantly answers with an A record, a NOERROR status, and a bitwise-identical payload when you repeat the query under a new Transaction ID, the costume hasn't just slipped—it fell off on the first frame.
Shallow DNS honeypots (most of them) rarely fail because someone matched a string in a banner list. They fail because low-interaction codebases almost never model full RFC edge-case behavior. Transaction IDs don’t echo. Questions vanish. Illegal opcodes get cheerful QUERY responses instead of dropping or returning FORMERR.
Here is the exact 8-probe sequence on the wire, how to execute it in code, and how three distinct security disciplines use the exact same data.
A QUERY hPaUdIt-<nonce>.iNvAlId
│
├── 1. Header Verification ─► Is this a valid DNS response (QR=1)?
├── 2. State Mirroring ─► Does the 16-bit Transaction ID (txid) echo?
├── 3. Question Echo ─► Does QNAME echo with 0x20 case intact?
├── 4. Reserved TLD ─► Does .invalid return NXDOMAIN (not NOERROR)?
├── 5. Opcode Validation ─► Does an illegal OPCODE return FORMERR/Drop?
├── 6. Clone Detection ─► Do unique txids return identical UDP bytes?
├── 7. Option Handling ─► Does EDNS0 OPT payload trigger a crash/FORMERR?
└── 8. Lure Inspection ─► Do TXT/SOA fields contain default stock copy?Part 1: The Hands-On Protocol Audit Ladder
Every check below includes the exact protocol logic, the byte-level target, and executable code so you can run this sequence in your lab.
Step 1. Header Verification (QR Bit Check)
Check if the port returns a valid DNS response header rather than echoing raw text or returning web server HTML. Byte 2, Bit 7 of a DNS payload is the QR (Query/Response) flag. It must be 0 for a query and 1 for a response.
Python
from scapy.all import IP, UDP, DNS, DNSQR, sr1# Send a standard query
pkt = IP(dst="192.168.1.50")/UDP(dport=53)/DNS(rd=1, qd=DNSQR(qname="test.invalid"))
reply = sr1(pkt, timeout=2, verbose=0)
if reply and reply.haslayer(DNS):
if reply[DNS].qr == 1:
print("[+] PASS: Valid DNS Speaker (QR=1)")
else:
print("[-] FAIL: Server returned QR=0 (Sent a query back instead of a response)")
else:
print("[-] SKIP: No response / Port closed")
Step 2. Transaction ID (txid) Mirroring
RFC 1035 requires the server to echo the exact 16-bit Transaction ID back in the first two bytes of the header. Canned honeypots frequently return hardcoded IDs (e.g., 0x0001 or 0x1234).
import randomrandom_id = random.randint(1, 65535)
pkt = IP(dst="192.168.1.50")/UDP(dport=53)/DNS(id=random_id, qd=DNSQR(qname="test.invalid"))
reply = sr1(pkt, timeout=2, verbose=0)
if reply and reply[DNS].id == random_id:
print(f"[+] PASS: Transaction ID echoed correctly ({hex(random_id)})")
else:
print(f"[-] FAIL: TXID Mismatch! Sent {hex(random_id)}, got {hex(reply[DNS].id if reply else 0)}")
Step 3. Question Echo & 0x20 Case Preservation
The server must echo the Question Section back to the client. Modern resolvers use 0x20 mixed-case strings (hPaUdIt.iNvAlId) to validate responses. Real servers preserve case; shallow stubs lowercase everything or drop the question section entirely.
mixed_qname = "hPaUdIt.iNvAlId"
pkt = IP(dst="192.168.1.50")/UDP(dport=53)/DNS(qd=DNSQR(qname=mixed_qname))
reply = sr1(pkt, timeout=2, verbose=0)if reply and reply.haslayer(DNSQR):
returned_qname = reply[DNSQR].qname.decode('utf-8').rstrip('.')
if returned_qname == mixed_qname:
print("[+] PASS: Exact 0x20 mixed-case string echoed.")
elif returned_qname.lower() == mixed_qname.lower():
print("[!] WEAK TELL: Question echoed, but case was lowercased.")
else:
print("[-] FAIL: Question section was completely dropped in response.")
Step 4. Reserved TLD Routing (.invalid)
RFC 2606 reserves .invalid so it never resolves. Real DNS resolvers return RCODE 3 (NXDOMAIN). Catch-all honeypots blindly return a fake A record (NOERROR, RCODE 0).
Execute via CLI:
$ dig @192.168.1.50 random-domain-name.invalid +shortExpected outcomes:
- Real Resolver: Empty output, status
NXDOMAIN. - Shallow Decoy: Returns a synthetic IP address (e.g.,
1.2.3.4or127.0.0.1).
Step 5. Illegal Opcode Handling
Standard queries use OPCODE 0. Valid opcodes range up to 5. If you send an unassigned OPCODE 15, a real server drops the packet or returns RCODE 1 (FORMERR). Simple socket scripts ignore the opcode field and answer it like a standard query.
# Send a DNS packet with Opcode 15 (Reserved/Illegal)
pkt = IP(dst="192.168.1.50")/UDP(dport=53)/DNS(opcode=15, qd=DNSQR(qname="example.com"))
reply = sr1(pkt, timeout=2, verbose=0)if reply is None:
print("[+] PASS: Server dropped illegal opcode request (Timeout).")
elif reply[DNS].rcode == 1:
print("[+] PASS: Server returned FORMERR (Format Error) as expected.")
elif reply[DNS].ancount > 0:
print("[-] HIGH-CONFIDENCE FAÇADE: Server answered an illegal OPCODE with an IP address!")
Step 6. Canned Response / Clone Detection (The Decisive Test)
Minimal honeypots write a hardcoded hex string directly to the socket for any incoming UDP packet. Send two queries with different domain names and Transaction IDs. Strip the 2-byte Transaction ID from both responses; if the remaining bytes match, it is a static playback engine.
pkt1 = IP(dst="192.168.1.50")/UDP(dport=53)/DNS(id=0x1111, qd=DNSQR(qname="alpha.com"))
pkt2 = IP(dst="192.168.1.50")/UDP(dport=53)/DNS(id=0x9999, qd=DNSQR(qname="beta.org"))r1 = sr1(pkt1, timeout=2, verbose=0)
r2 = sr1(pkt2, timeout=2, verbose=0)
if r1 and r2:
# Strip first 2 bytes (TXID) and compare the remaining payload
bytes1 = bytes(r1[DNS])[2:]
bytes2 = bytes(r2[DNS])[2:]
if bytes1 == bytes2:
print("[!!!] CONFIRMED HONEYPOT: Bitwise-identical payloads returned for different requests!")
else:
print("[+] PASS: Responses differ dynamically based on request.")
Step 7. EDNS0 Option Handling
Modern DNS uses EDNS0 (RFC 6891) to pass extended parameters via an OPT pseudo-record. Minimal custom listeners fail to parse OPT records appended to requests, throwing unhandled exceptions or returning format errors.
Execute via CLI:
$ dig @192.168.1.50 example.com +edns=0Expected outcomes:
- Real Resolver: Includes an
OPT PSEUDOSECTIONin the answer. - Shallow Decoy: Crashes, times out, or returns
FORMERR.
Step 8. Metadata & Stock Lure Inspection
Inspect human-readable text strings inside TXT or SOA records only after verifying protocol behavior.
Execute via CLI:
dig @192.168.1.50 CHAOS TXT version.bindLook for default strings like Dionaea, OpenCanary, or FakeDNS. Treat these as supporting evidence, not primary proof.
Part 2: Three Operational Lenses
Now that we have the raw protocol output, here is how Red, Blue, and CTI teams apply the exact same findings.
[ UDP Wire Output ]
│
┌───────────────────────────┼───────────────────────────┐
▼ ▼ ▼
[ Red Team ] [ Blue Team ] [ CTI Analyst ]
Fix OPSEC & Decoys Detect Façades Score & ReportLens 1: Red Team Eye (OPSEC & Decoy Realism)
If you operate red team deception infrastructure or C2 redirector façades, shallow DNS stubs are an operational security disaster. Fingerprinting bots don’t need your software version — they rely on RFC non-compliance to burn your assets automatically.
How adversaries interpret raw wire behavior:
- Identical payload across unique TXIDs: “Static response generator or playback stub.”
- Illegal OPCODE returns standard
QUERYanswer: "Non-compliant stub; not a production name server." .invalidquery returnsNOERROR+ IP: "Wildcard honeypot catch-all."- Transaction ID mismatch or missing Question: “Socket framing error; custom code wrapper.”
- Default
honeypotstrings in TXT/SOA: "Explicitly labeled decoy asset."
Offensive Realism Insight: In protocol engineering, silence looks more realistic than helpfulness. Dropping an illegal opcode (timing out) mirrors hardened firewalls and real production DNS daemons. Answering an illegal opcode with a helpful query payload instantly burns the asset.
Infrastructure Hardening Checklist:
- Mirror State: Strictly echo transaction IDs, header flags, and full question sections.
- Dynamic Generation: Never replay pre-rendered byte streams across unique requests.
- Honor RFC 2606: Explicitly return
NXDOMAINfor reserved top-level domains (.invalid,.test). - Handle Opcodes Strictly: Drop or return
FORMERRfor unhandled or reserved opcodes. - Sanitize Metadata: Remove stock tool strings from SOA and TXT records.
Lens 2: Blue Team Eye (Detect the Façade, Ignore the Banner)
Defenders often fall into a bad habit: grepping for banner strings. Low-interaction DNS stubs rarely expose useful version strings, making signature-based detection fragile and prone to false negatives.
Get Whengomarket’s stories in your inbox
Join Medium for free to get updates from this writer.
Focus on structural anomalies instead:
- Speakership Verification: Confirm valid DNS header structure with
QR=1. - Request Fidelity: Verify transaction ID matching and
RCODErouting on reserved namespaces. - Façade Testing: Audit response cloning, illegal opcode processing, and EDNS0 parameter handling.
- Metadata Correlation: Treat stock text lures as low-confidence supporting context only.
Triage Breakdown (Skips vs. High-Confidence Hits):
- Timeout / ICMP Refused (Classification: Skip / No-Op): Closed or filtered port; not evidence of deception.
- Malformed Header (
QR=0) (Classification: Framing Anomaly): Non-standard service or broken socket wrapper. - Identical bytes on distinct TXIDs (Classification: Decisive Hit): Hardcoded response array (playback engine).
- QNAME lowercased on return (Classification: Gated / Weak): Implementation detail common in legacy forwarders.
- Valid EDNS OPT triggers
FORMERR(Classification: Strong Hit): Incomplete EDNS0 implementation stub.
Remediation Copy for Deception Engineering Tickets:
[RFC-1035]Ensure the 16-bitTXIDin the response header echoes the requestTXID.[RFC-2606]ImplementNXDOMAIN(RCODE 3) routing for non-existent and reserved namespaces.[RFC-6891]Safely parse or ignore valid EDNS0 OPT pseudo-section additions without throwingFORMERR.[STATE]Inject dynamic timestamp or TTL values to eliminate bitwise response cloning.
Lens 3: CTI Analyst Eye (Build Defensible Intel)
Threat Intelligence requires defensible claims. Misclassifying a misconfigured corporate forwarder as an adversary decoy destroys report credibility.
A single recursive resolver stripping case formatting is an implementation quirk; a host returning identical UDP responses across random transaction IDs is a deterministic low-interaction decoy.
[ Observed Anomaly ]
│
┌──────────────┴──────────────┐
│ │
Single Indicator Multi-Tell Sequence
│ │
▼ ▼
[ Gated Signal ] [ Decisive Façade ]
(Requires Corroboration) (High-Confidence Assessment)Signal Hierarchy & Analytic Weights:
- Decisive Weight (Bitwise response cloning across distinct queries): Standalone evidence for low-interaction decoy classification.
- Ungated High Weight (TXID mismatch, illegal OPCODE execution,
.invalidNOERRORresponse, EDNS0 failure): High confidence when two or more are observed together. - Gated / Low Weight (0x20 case normalization, standard stock lures in SOA/TXT): Secondary supporting evidence only; cannot lead a claim.
Standardized Reporting Language Rules:
- Single Ungated Tell: “Observed non-standard DNS protocol anomalies; insufficient to confirm a decoy.”
- 0x20 Normalization or Lures Only: “Secondary protocol artifacts; retained for host clustering only.”
- Timeout on Illegal Opcode: “Compliant protocol drop; non-hit.”
=== MINI INTEL NOTE ===
Target: 192.168.1.50:53 (UDP)
Method: Non-destructive UDP protocol audit (8 queries)
Observed: Bitwise response cloning on distinct TXIDs (Step 6); NOERROR on .invalid (Step 4).
Assessment: Low-interaction DNS decoy stub (High Confidence).
Not Tested: DoH/DoT transport, AXFR, stateful rate-limiting.Part 3: Master Cross-Role Comparison
Here is how each protocol audit finding translates across operational tasks:
Step 1:
QR=0in Response
- Red Team: Fix socket header packaging.
- Blue Team: Flag non-compliant DNS speaker.
- CTI Report: “Malformed DNS header anomaly.”
Step 2: Transaction ID Mismatch
- Red Team: Echo incoming 16-bit ID field.
- Blue Team: Mark request-tracking failure.
- CTI Report: “Ungated protocol façade anomaly.”
Step 3: Case Stripping / Lowercase QNAME
- Red Team: Benchmark against target network.
- Blue Team: Require secondary corroboration.
- CTI Report: “Supporting implementation detail.”
Step 4:
.invalidQuery YieldsNOERROR
- Red Team: Implement RFC 2606
NXDOMAINrouting. - Blue Team: Alert on catch-all resolution.
- CTI Report: “Ungated protocol façade anomaly.”
Step 5: Illegal OPCODE Answered
- Red Team: Return
FORMERRor drop packet. - Blue Team: Issue engineering bug ticket.
- CTI Report: “Ungated protocol façade anomaly.”
Step 6: Bitwise Response Clone
- Red Team: Critical: Inject dynamic TTLs/data.
- Blue Team: Mark as confirmed playback decoy.
- CTI Report: “Low-interaction DNS stub (High Confidence).”
Step 7: EDNS0 OPT Failure
- Red Team: Support or safely ignore OPT pseudo-sections.
- Blue Team: Flag EDNS parsing gap.
- CTI Report: “Incomplete EDNS0 implementation.”
- Step 8: Stock Honeypot String Found
- Red Team: Scrub configuration defaults.
- Blue Team: Optional supporting metric.
- CTI Report: “Low-confidence lure artifact.”
Key Takeaways & RFC References
- Protocol Mechanics > Banners: Version strings are easily faked in a config file. Mimicking true RFC state handling requires actual protocol support.
- Offensive Realism: Overly helpful response logic doxxes decoys faster than closed ports. Silence on invalid inputs is often the most realistic behavior.
- Defensive Rigor: Distinguish between communication timeouts (skips) and explicit RFC violations (hits) to eliminate SOC noise.
- Analytic Discipline: Base threat intelligence claims on decisive protocol flaws rather than single implementation quirks.
Specifications for Reference
- RFC 1035: Domain Names — Implementation and Specification (Header formats, ID/QR/OPCODE/RCODE fields)
- RFC 2606: Reserved Top Level DNS Names (
.invalid,.test,.example,.localhost) - RFC 6891: Extension Mechanisms for DNS (EDNS0)