Was macht Analyse von Typosquatting-Domains mit DNSTwist?
Überblick
DNSTwist ist eine Domainnamen-Permutations-Engine, die ähnlich aussehende Domainnamen generiert, um Typosquatting, homographische Phishing-Angriffe und Markenimitationen zu erkennen. Es erzeugt tausende von Domain-Permutationen mithilfe von Techniken wie Zeichensubstitution, Transposition, Einfügung, Auslassung und Homoglyphen-Ersetzung und überprüft anschließend DNS-Einträge (A, AAAA, NS, MX), berechnet die Ähnlichkeit von Webseiten anhand von Fuzzy-Hashing (ssdeep) und Wahrnehmungshashing (pHash) und identifiziert potenziell bösartige registrierte Domains.
Wann verwenden
- Bei der Untersuchung von Sicherheitsvorfällen, die eine Analyse von Typosquatting-Domains mit DNSTwist erfordern
- Beim Erstellen von Erkennungsregeln oder Threat-Hunting-Abfragen für diese Domains
- Wenn SOC-Analysten strukturierte Verfahren für diesen Analysetyp benötigen
- Bei der Validierung der Sicherheitsüberwachung für entsprechende Angriffstechniken
Voraussetzungen
- Python 3.9+ mit installiertem
dnstwist(pip install dnstwist[full]) - Optional: GeoIP-Datenbank für IP-Geolokalisierung
- Optional: Shodan-API-Schlüssel für Anreicherung
- Netzwerkzugriff für DNS-Abfragen
- Verständnis von DNS-Eintragstypen und Domain-Registrierung
Schlüsselkonzepte
Techniken zur Domain-Permutation
DNSTwist generiert Permutationen durch: Hinzufügen (Anhängen von Zeichen), Bitsquatting (Bit-Flip-Fehler), Homoglyph (visuell ähnliche Unicode-Zeichen wie rn vs m), Bindestrichsetzung (Hinzufügen von Bindestrichen), Einfügen (Einfügen von Zeichen), Auslassen (Entfernen von Zeichen), Wiederholen (Wiederholen von Zeichen), Ersetzen (Ersetzen durch benachbarte Tastaturtasten), Subdomain (Einfügen von Punkten), Transposition (Vertauschen benachbarter Zeichen), Vokaltausch (Vertauschen von Vokalen) und wörterbuchbasiert (Anhängen gebräuchlicher Wörter).
Fuzzy-Hashing und visuelle Ähnlichkeit
DNSTwist verwendet ssdeep (lokalitätssensitiven Hash) zum Vergleichen von HTML-Inhalten und pHash (Wahrnehmungshash) zum Vergleichen von Screenshots von Webseiten. Dies hilft, geklonte Phishing-Seiten zu identifizieren, die die legitime Seite visuell nachahmen. Ein hoher Ähnlichkeitswert deutet auf eine wahrscheinliche Phishing-Seite hin.
Erkennungsworkflow
Der typische Workflow ist: Domain-Permutationen generieren -> DNS-Einträge auflösen -> auf registrierte Domains prüfen -> Webseitenähnlichkeit vergleichen -> verdächtige Domains markieren -> Sicherheitsteam alarmieren -> Abschaltung beantragen. Für eine typische Unternehmensdomain generiert dnstwist 5.000-10.000 Permutationen.
Workflow
Schritt 1: Basis-Scan der Domain-Permutationen
import subprocess
import json
import csv
from datetime import datetime
def run_dnstwist_scan(domain, output_file=None):
"""Führt einen dnstwist-Scan für eine Ziel-Domain durch."""
cmd = [
"dnstwist",
"--registered", # Nur registrierte Domains anzeigen
"--format", "json", # Ausgabe im JSON-Format
"--nameservers", "8.8.8.8,1.1.1.1",
"--threads", "50",
"--mxcheck", # MX-Einträge prüfen
"--ssdeep", # Fuzzy-Hash-Vergleich
"--geoip", # GeoIP-Abfrage
domain,
]
print(f"[*] Scanne Permutationen für: {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"[+] {len(registered)} registrierte ähnliche Domains gefunden")
if output_file:
with open(output_file, "w") as f:
json.dump(registered, f, indent=2)
print(f"[+] Ergebnisse gespeichert unter {output_file}")
return registered
else:
print(f"[-] dnstwist-Fehler: {result.stderr}")
return []
results = run_dnstwist_scan("example.com", "typosquat_results.json")
Schritt 2: Ergebnisse analysieren und priorisieren
def analyze_results(results, legitimate_ips=None):
"""Analysiert dnstwist-Ergebnisse und priorisiert Bedrohungen."""
legitimate_ips = legitimate_ips or set()
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 = []
# Hohe Ähnlichkeit mit legitimer Site
if ssdeep_score and ssdeep_score > 50:
risk_score += 40
risk_factors.append(f"hohe Web-Ähnlichkeit ({ssdeep_score}%)")
# Hat MX-Einträge (kann E-Mails empfangen / Phishing)
if dns_mx:
risk_score += 20
risk_factors.append("hat MX-Einträge (E-Mail-fähig)")
# Kürzlich registriert (falls whois-Daten verfügbar)
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"kürzlich registriert ({age_days} Tage)")
elif age_days < 90:
risk_score += 15
risk_factors.append(f"vor {age_days} Tagen registriert")
except (ValueError, TypeError):
pass
# Homoglyph-Angriffe sind am riskantesten
if fuzzer == "homoglyph":
risk_score += 25
risk_factors.append("Homoglyph (visuell identisch)")
elif fuzzer in ("addition", "replacement", "transposition"):
risk_score += 10
risk_factors.append(f"Permutationstyp: {fuzzer}")
# Zeigt nicht auf legitime Infrastruktur
if dns_a and not set(dns_a).intersection(legitimate_ips):
risk_score += 10
risk_factors.append("andere IP als die legitime")
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-Analyse ===")
print(f"Hohes Risiko: {len(high_risk)}")
print(f"Mittleres Risiko: {len(medium_risk)}")
print(f"Niedriges Risiko: {len(low_risk)}")
if high_risk:
print(f"\n--- Domains mit hohem Risiko ---")
for entry in high_risk[:10]:
print(f" {entry['domain']} (Punktzahl: {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"})
Schritt 3: Kontinuierliche Überwachungspipeline
import time
import hashlib
class TyposquatMonitor:
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:
with open(self.known_file, "r") as f:
return json.load(f)
except FileNotFoundError:
return {}
def _save_known(self):
with open(self.known_file, "w") as f:
json.dump(self.known_domains, f, indent=2)
def scan_all_domains(self):
"""Durchsucht alle überwachten Domains nach neuen Typosquat-Domains."""
new_findings = []
for domain in self.domains:
results = run_dnstwist_scan(domain)
for entry in results:
domain_key = entry.get("domain", "")
if domain_key not in self.known_domains:
entry["first_seen"] = datetime.now().isoformat()
entry["monitored_domain"] = domain
self.known_domains[domain_key] = entry
new_findings.append(entry)
print(f" [NEU] {domain_key} ({entry.get('fuzzer', '')})")
self._save_known()
print(f"\n[+] Neue Typosquatting-Domains gefunden: {len(new_findings)}")
return new_findings
def generate_alert(self, findings):
"""Generiert einen Alarm für neue Typosquatting-Domains mit hohem Risiko."""
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)
Schritt 4: Export für Blocklist und Abschaltung
def export_blocklist(analysis, output_file="blocklist.txt"):
"""Exportiert Domains mit hohem Risiko als Blocklist für Firewall/Proxy."""
domains = []
for entry in analysis["high"] + analysis["medium"]:
domain = entry.get("domain", "")
if domain:
domains.append(domain)
with open(output_file, "w") as f:
f.write(f"# Typosquatting-Blocklist erstellt am {datetime.now().isoformat()}\n")
for d in sorted(set(domains)):
f.write(f"{d}\n")
print(f"[+] Blocklist gespeichert: {len(domains)} Domains -> {output_file}")
return domains
def generate_takedown_report(high_risk_domains):
"""Erstellt einen Bericht für Abschaltungsanfragen."""
report = f"""# Domain-Abschaltungsanfrage
Erstellt am: {datetime.now().isoformat()}
## Zusammenfassung
{len(high_risk_domains)} Domains als potenzielle Typosquatting-/Phishing-Domains identifiziert.
## Abschaltung erfordernde Domains
"""
for entry in high_risk_domains:
report += f"""
### {entry['domain']}
- **Permutationstyp**: {entry.get('fuzzer', 'unbekannt')}
- **IP-Adresse**: {', '.join(entry.get('dns_a', ['N/A']))}
- **MX-Einträge**: {', '.join(entry.get('dns_mx', ['N/A']))}
- **Risikopunktzahl**: {entry.get('risk_score', 0)}
- **Risikofaktoren**: {'; '.join(entry.get('risk_factors', []))}
- **Web-Ähnlichkeit**: {entry.get('ssdeep_score', 'N/A')}%
"""
with open("takedown_report.md", "w") as f:
f.write(report)
print("[+] Abschaltungsbericht erstellt: takedown_report.md")
export_blocklist(analysis)
generate_takedown_report(analysis["high"])
Validierungskriterien
- DNSTwist generiert Domain-Permutationen für die Ziel-Domain
- DNS-Auflösung identifiziert registrierte ähnliche Domains
- Bewertung der Web-Ähnlichkeit erkennt geklonte Phishing-Seiten
- Risikobewertung priorisiert Domains nach Bedrohungsstufe
- Kontinuierliche Überwachung erkennt neu registrierte Typosquat-Domains
- Blocklist- und Abschaltungsberichte werden korrekt erstellt