Detect typosquatting, homograph phishing, and brand impersonation domains using dnstwist to generate domain permutations and identify registered lookalike domains targeting your organization.
Instrucciones de origen · Vista previa de solo lectura
name
analyzing-typosquatting-domains-with-dnstwist
description
Detect typosquatting, homograph phishing, and brand impersonation domains using dnstwist to generate domain permutations and identify registered lookalike domains targeting your organization.
DNSTwist is a domain name permutation engine that generates similar-looking domain names to detect typosquatting, homograph phishing attacks, and brand impersonation. It creates thousands of domain permutations using techniques like character substitution, transposition, insertion, omission, and homoglyph replacement, then checks DNS records (A, AAAA, NS, MX), calculates web page similarity using fuzzy hashing (ssdeep) and perceptual hashing (pHash), and identifies potentially malicious registered domains.
When to Use
When investigating security incidents that require analyzing typosquatting domains with dnstwist
When building detection rules or threat hunting queries for this domain
When SOC analysts need structured procedures for this analysis type
When validating security monitoring coverage for related attack techniques
Detection Gaps & Validation
Permutation gaps: default dnstwist fuzzers miss target-specific tricks -- add --dictionary (brand words like "login", "secure", "support"), widen TLDs with --tld, and confirm --homoglyph/IDN coverage, since combosquats (mycompany-support.com) and new gTLDs fall outside the base algorithm.
--registered blind spot: filtering to registered-only hides parked and pre-registration domains; a domain with no A record today can be weaponized tomorrow. Periodically scan without the filter.
Resolver/rate issues: a single nameserver throttles or returns NXDOMAIN inconsistently -- use --nameservers 8.8.8.8,1.1.1.1 and re-run, or you record false "unregistered."
FP drivers: legitimate regional sites, partners, and CDNs match permutations; ssdeep_score is null when the page is JS-rendered or behind Cloudflare, so absence of similarity is not absence of phishing.
To validate: seed a known lookalike you control and confirm it surfaces with the expected fuzzer label and risk tier; verify legitimate_ips is populated so your own infrastructure is not flagged. Confirm a high-risk hit out-of-band -- resolve MX (email-capable), fetch and visually compare the page, and check WHOIS creation date -- before issuing a takedown, since risk score alone over-reports.
Prerequisites
Python 3.9+ with dnstwist installed (pip install dnstwist[full])
Optional: GeoIP database for IP geolocation
Optional: Shodan API key for enrichment
Network access to perform DNS queries
Understanding of DNS record types and domain registration
Key Concepts
Domain Permutation Techniques
DNSTwist generates permutations using: addition (appending characters), bitsquatting (bit-flip errors), homoglyph (visually similar Unicode characters like rn vs m), hyphenation (adding hyphens), insertion (inserting characters), omission (removing characters), repetition (repeating characters), replacement (replacing with adjacent keyboard keys), subdomain (inserting dots), transposition (swapping adjacent characters), vowel-swap (swapping vowels), and dictionary-based (appending common words).
Fuzzy Hashing and Visual Similarity
DNSTwist uses ssdeep (locality-sensitive hash) to compare HTML content and pHash (perceptual hash) to compare screenshots of web pages. This helps identify cloned phishing sites that visually mimic the legitimate site. A high similarity score indicates a likely phishing page.
Detection Workflow
The typical workflow is: generate domain permutations -> resolve DNS records -> check for registered domains -> compare web page similarity -> flag suspicious domains -> alert security team -> request takedown. For a typical corporate domain, dnstwist generates 5,000-10,000 permutations.
Workflow
Step 1: Basic Domain Permutation Scan
import subprocess
import json
import csv
from datetime import datetime
defrun_dnstwist_scan(domain, output_file=None):
"""Run dnstwist scan against a target domain."""
cmd = [
"dnstwist",
"--registered", # Only show registered domains"--format", "json", # Output in JSON"--nameservers", "8.8.8.8,1.1.1.1",
"--threads", "50",
"--mxcheck", # Check MX records"--ssdeep", # Fuzzy hash comparison"--geoip", # GeoIP lookup
domain,
]
print(f"[*] Scanning permutations for: {domain}")
result = subprocess.run(cmd, capture_output=True, text=True, timeout=600)
if result.returncode == 0:
results = json.loads(result.stdout)
registered = [r for r in results if r.get("dns_a") or r.get("dns_aaaa")]
print(f"[+] Found {len(registered)} registered lookalike domains")
if output_file:
withopen(output_file, "w") as f:
json.dump(registered, f, indent=2)
print(f"[+] Results saved to {output_file}")
return registered
else:
print(f"[-] dnstwist error: {result.stderr}")
return []
results = run_dnstwist_scan("example.com", "typosquat_results.json")
Step 2: Analyze and Prioritize Results
defanalyze_results(results, legitimate_ips=None):
"""Analyze dnstwist results and prioritize threats."""
legitimate_ips = legitimate_ips orset()
high_risk = []
medium_risk = []
low_risk = []
for entry in results:
domain = entry.get("domain", "")
fuzzer = entry.get("fuzzer", "")
dns_a = entry.get("dns_a", [])
dns_mx = entry.get("dns_mx", [])
ssdeep_score = entry.get("ssdeep_score", 0)
risk_score = 0
risk_factors = []
# High similarity to legitimate siteif ssdeep_score and ssdeep_score > 50:
risk_score += 40
risk_factors.append(f"high web similarity ({ssdeep_score}%)")
# Has MX records (can receive email / phishing)if dns_mx:
risk_score += 20
risk_factors.append("has MX records (email capable)")
# Recently registered (if whois data available)
whois_created = entry.get("whois_created", "")
if whois_created:
try:
created = datetime.fromisoformat(whois_created.replace("Z", "+00:00"))
age_days = (datetime.now(created.tzinfo) - created).days
if age_days < 30:
risk_score += 30
risk_factors.append(f"recently registered ({age_days} days)")
elif age_days < 90:
risk_score += 15
risk_factors.append(f"registered {age_days} days ago")
except (ValueError, TypeError):
pass# Homoglyph attacks are highest riskif fuzzer == "homoglyph":
risk_score += 25
risk_factors.append("homoglyph (visually identical)")
elif fuzzer in ("addition", "replacement", "transposition"):
risk_score += 10
risk_factors.append(f"permutation type: {fuzzer}")
# Not pointing to legitimate infrastructureif dns_a andnotset(dns_a).intersection(legitimate_ips):
risk_score += 10
risk_factors.append("different IP from legitimate")
entry["risk_score"] = risk_score
entry["risk_factors"] = risk_factors
if risk_score >= 50:
high_risk.append(entry)
elif risk_score >= 25:
medium_risk.append(entry)
else:
low_risk.append(entry)
high_risk.sort(key=lambda x: x["risk_score"], reverse=True)
medium_risk.sort(key=lambda x: x["risk_score"], reverse=True)
print(f"\n=== Typosquatting Analysis ===")
print(f"High Risk: {len(high_risk)}")
print(f"Medium Risk: {len(medium_risk)}")
print(f"Low Risk: {len(low_risk)}")
if high_risk:
print(f"\n--- High Risk Domains ---")
for entry in high_risk[:10]:
print(f" {entry['domain']} (score: {entry['risk_score']})")
for factor in entry['risk_factors']:
print(f" - {factor}")
return {"high": high_risk, "medium": medium_risk, "low": low_risk}
analysis = analyze_results(results, legitimate_ips={"93.184.216.34"})
Step 3: Continuous Monitoring Pipeline
import time
import hashlib
classTyposquatMonitor:
def__init__(self, domains, known_domains_file="known_typosquats.json"):
self.domains = domains
self.known_file = known_domains_file
self.known_domains = self._load_known()
def_load_known(self):
try:
withopen(self.known_file, "r") as f:
return json.load(f)
except FileNotFoundError:
return {}
def_save_known(self):
withopen(self.known_file, "w") as f:
json.dump(self.known_domains, f, indent=2)
defscan_all_domains(self):
"""Scan all monitored domains for new typosquats."""
new_findings = []
for domain inself.domains:
results = run_dnstwist_scan(domain)
for entry in results:
domain_key = entry.get("domain", "")
if domain_key notinself.known_domains:
entry["first_seen"] = datetime.now().isoformat()
entry["monitored_domain"] = domain
self.known_domains[domain_key] = entry
new_findings.append(entry)
print(f" [NEW] {domain_key} ({entry.get('fuzzer', '')})")
self._save_known()
print(f"\n[+] New typosquatting domains found: {len(new_findings)}")
return new_findings
defgenerate_alert(self, findings):
"""Generate alert for new high-risk typosquatting domains."""
analysis = analyze_results(findings)
alerts = []
for entry in analysis["high"]:
alerts.append({
"severity": "HIGH",
"domain": entry["domain"],
"target": entry.get("monitored_domain", ""),
"risk_score": entry["risk_score"],
"risk_factors": entry["risk_factors"],
"dns_a": entry.get("dns_a", []),
"dns_mx": entry.get("dns_mx", []),
"timestamp": datetime.now().isoformat(),
})
return alerts
monitor = TyposquatMonitor(["mycompany.com", "mycompany.org"])
new_findings = monitor.scan_all_domains()
alerts = monitor.generate_alert(new_findings)