关于 analyzing-typosquatting-domains-with-dnstwist
面向安全分析人员的 DNSTwist 域名仿冒排查技能,指导生成相似域名、检查 DNS 和网页相似度,并筛选潜在恶意域名。
主要功能
- 01生成多种相似域名变体
- 02查询 DNS 记录
- 03比较网页内容与截图
- 04按风险筛选域名
- 05监控新注册仿冒域名
- 06导出阻止列表和报告
适用场景
- 01调查针对品牌的仿冒域名
- 02开展域名威胁狩猎
- 03验证安全监控覆盖情况
- 04整理高风险域名供处置
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill analyzing-typosquatting-domains-with-dnstwist安装后在 Claude Code 中输入/analyzing-typosquatting-domains-with-dnstwist确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSEreferences/api-reference.mdscripts/agent.py详细介绍
使用 DNSTwist 分析仿冒域名
概述
DNSTwist 是一个域名排列引擎,可生成外观相似的域名,用于检测仿冒域名、同形异义钓鱼攻击和品牌冒充。它通过字符替换、转置、插入、遗漏和同形字符替换等技术生成数千个域名排列,然后检查 DNS 记录(A、AAAA、NS、MX),使用模糊哈希(ssdeep)和感知哈希(pHash)计算网页相似度,并识别可能恶意的已注册域名。
何时使用
- 在调查需要使用 dnstwist 分析仿冒域名的安全事件时
- 在为该领域构建检测规则或威胁狩猎查询时
- 当 SOC 分析师需要针对此类分析的结构化流程时
- 在验证相关攻击技术的安全监控覆盖范围时
前置条件
- Python 3.9+ 并已安装
dnstwist(pip install dnstwist[full]) - 可选:用于 IP 地理定位的 GeoIP 数据库
- 可选:用于数据增强的 Shodan API 密钥
- 执行 DNS 查询所需的网络访问
- 了解 DNS 记录类型和域名注册
关键概念
域名排列技术
DNSTwist 使用以下方式生成排列:添加(追加字符)、位翻转(位翻转错误)、同形字符(视觉相似的 Unicode 字符,如 rn 与 m)、连字符化(添加连字符)、插入(插入字符)、遗漏(删除字符)、重复(重复字符)、替换(替换为相邻键盘按键)、子域名(插入点)、转置(交换相邻字符)、元音交换(交换元音)以及基于字典(追加常见单词)。
模糊哈希与视觉相似度
DNSTwist 使用 ssdeep(局部敏感哈希)比较 HTML 内容,并使用 pHash(感知哈希)比较网页截图。这有助于识别在视觉上模仿合法网站的克隆钓鱼网站。较高的相似度分数表明很可能是钓鱼页面。
检测工作流
典型工作流为:生成域名排列 -> 解析 DNS 记录 -> 检查已注册域名 -> 比较网页相似度 -> 标记可疑域名 -> 通知安全团队 -> 请求下架。对于典型的企业域名,dnstwist 会生成 5,000-10,000 个排列。
工作流
步骤 1:基础域名排列扫描
import subprocess
import json
import csv
from datetime import datetime
def run_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:
with open(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")
步骤 2:分析结果并确定优先级
def analyze_results(results, legitimate_ips=None):
"""Analyze dnstwist results and prioritize threats."""
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 = []
# High similarity to legitimate site
if 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 risk
if 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 infrastructure
if dns_a and not set(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"})
步骤 3:持续监控流水线
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):
"""Scan all monitored domains for new typosquats."""
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" [NEW] {domain_key} ({entry.get('fuzzer', '')})")
self._save_known()
print(f"\n[+] New typosquatting domains found: {len(new_findings)}")
return new_findings
def generate_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)
步骤 4:导出用于阻止列表和下架
def export_blocklist(analysis, output_file="blocklist.txt"):
"""Export high-risk domains as blocklist for 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 generated {datetime.now().isoformat()}\n")
for d in sorted(set(domains)):
f.write(f"{d}\n")
print(f"[+] Blocklist saved: {len(domains)} domains -> {output_file}")
return domains
def generate_takedown_report(high_risk_domains):
"""Generate takedown request report."""
report = f"""# Domain Takedown Request
Generated: {datetime.now().isoformat()}
## Summary
{len(high_risk_domains)} domains identified as potential typosquatting/phishing.
## Domains Requiring Takedown
"""
for entry in high_risk_domains:
report += f"""
### {entry['domain']}
- **Permutation Type**: {entry.get('fuzzer', 'unknown')}
- **IP Address**: {', '.join(entry.get('dns_a', ['N/A']))}
- **MX Records**: {', '.join(entry.get('dns_mx', ['N/A']))}
- **Risk Score**: {entry.get('risk_score', 0)}
- **Risk Factors**: {'; '.join(entry.get('risk_factors', []))}
- **Web Similarity**: {entry.get('ssdeep_score', 'N/A')}%
"""
with open("takedown_report.md", "w") as f:
f.write(report)
print("[+] Takedown report generated: takedown_report.md")
export_blocklist(analysis)
generate_takedown_report(analysis["high"])
验证标准
- DNSTwist 为目标域名生成域名排列
- DNS 解析识别出已注册的相似域名
- 网页相似度评分检测出克隆钓鱼页面
- 风险评分按威胁等级对域名排序
- 持续监控检测出新注册的仿冒域名
- 阻止列表和下架报告正确生成
参考资料
评论
支持 Markdown