| name | analyzing-typosquatting-domains-with-dnstwist |
| description | 使用 dnstwist 生成域名置换变体并识别针对您所在组织已注册的仿冒域名,从而检测域名抢注(Typosquatting)、同形字符钓鱼和品牌冒充域名。 |
| domain | cybersecurity |
| subdomain | threat-intelligence |
| tags | ["dnstwist","typosquatting","phishing","domain-monitoring","brand-protection","homograph","dns","threat-intelligence"] |
| version | 1.0 |
| author | mahipal |
| license | Apache-2.0 |
使用 dnstwist 分析域名抢注
概述
dnstwist 是一款域名置换引擎,用于生成外观相似的域名,以检测域名抢注、同形字符钓鱼(Phishing)攻击和品牌冒充。它使用字符替换、字符转置、字符插入、字符删除和同形字符替换等技术生成数千个域名变体,然后检查 DNS 记录(A、AAAA、NS、MX),使用模糊哈希(ssdeep)和感知哈希(pHash)计算网页相似度,并识别潜在恶意的已注册域名。
前置条件
- Python 3.9+,安装
dnstwist(pip install dnstwist[full])
- 可选:用于 IP 地理位置的 GeoIP 数据库
- 可选:用于富化的 Shodan API 密钥
- 执行 DNS 查询的网络访问权限
- 了解 DNS 记录类型和域名注册流程
核心概念
域名置换技术
dnstwist 使用以下方式生成变体:addition(附加字符)、bitsquatting(位翻转错误)、homoglyph(视觉相似的 Unicode 字符,如 rn vs m)、hyphenation(添加连字符)、insertion(插入字符)、omission(删除字符)、repetition(重复字符)、replacement(替换为相邻键盘按键)、subdomain(插入点)、transposition(交换相邻字符)、vowel-swap(交换元音)和基于字典的(附加常用词)。
模糊哈希和视觉相似度
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):
"""对目标域名执行 dnstwist 扫描。"""
cmd = [
"dnstwist",
"--registered",
"--format", "json",
"--nameservers", "8.8.8.8,1.1.1.1",
"--threads", "50",
"--mxcheck",
"--ssdeep",
"--geoip",
domain,
]
print(f"[*] 正在扫描 {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)} 个已注册的仿冒域名")
if output_file:
with open(output_file, "w") as f:
json.dump(registered, f, indent=)
()
registered
:
()
[]
results = run_dnstwist_scan(, )
步骤 2:分析并优先排序结果
def analyze_results(results, legitimate_ips=None):
"""分析 dnstwist 结果并按威胁优先排序。"""
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 = []
if ssdeep_score and ssdeep_score > 50:
risk_score += 40
risk_factors.append(f"高度网页相似性 ({ssdeep_score}%)")
if dns_mx:
risk_score += 20
risk_factors.append("有 MX 记录(具备邮件能力)")
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"近期注册({age_days} 天前)")
age_days < :
risk_score +=
risk_factors.append()
(ValueError, TypeError):
fuzzer == :
risk_score +=
risk_factors.append()
fuzzer (, , ):
risk_score +=
risk_factors.append()
dns_a (dns_a).intersection(legitimate_ips):
risk_score +=
risk_factors.append()
entry[] = risk_score
entry[] = risk_factors
risk_score >= :
high_risk.append(entry)
risk_score >= :
medium_risk.append(entry)
:
low_risk.append(entry)
high_risk.sort(key= x: x[], reverse=)
medium_risk.sort(key= x: x[], reverse=)
()
()
()
()
high_risk:
()
entry high_risk[:]:
()
factor entry[]:
()
{: high_risk, : medium_risk, : low_risk}
analysis = analyze_results(results, legitimate_ips={})
步骤 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):
"""扫描所有监控域名,发现新的域名抢注。"""
new_findings = []
for domain in self.domains:
results = run_dnstwist_scan(domain)
for entry in results:
domain_key = entry.get("domain", "")
if domain_key .known_domains:
entry[] = datetime.now().isoformat()
entry[] = domain
.known_domains[domain_key] = entry
new_findings.append(entry)
()
._save_known()
()
new_findings
():
analysis = analyze_results(findings)
alerts = []
entry analysis[]:
alerts.append({
: ,
: entry[],
: entry.get(, ),
: entry[],
: entry[],
: entry.get(, []),
: entry.get(, []),
: datetime.now().isoformat(),
})
alerts
monitor = TyposquatMonitor([, ])
new_findings = monitor.scan_all_domains()
alerts = monitor.generate_alert(new_findings)
步骤 4:导出封锁列表和撤销报告
def export_blocklist(analysis, output_file="blocklist.txt"):
"""将高风险域名导出为防火墙/代理的封锁列表。"""
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"# 域名抢注封锁列表,生成时间 {datetime.now().isoformat()}\n")
for d in sorted(set(domains)):
f.write(f"{d}\n")
print(f"[+] 封锁列表已保存: {len(domains)} 个域名 -> {output_file}")
return domains
def generate_takedown_report(high_risk_domains):
"""生成域名撤销申请报告。"""
report = f"""# 域名撤销申请
生成时间: {datetime.now().isoformat()}
## 摘要
识别到 {len(high_risk_domains)} 个潜在的域名抢注/钓鱼域名。
## 需要撤销的域名
"""
for entry in high_risk_domains:
report += f"""
### {entry['domain']}
- **置换类型**: {entry.get('fuzzer', 'unknown')}
- **IP 地址**: {', '.join(entry.get('dns_a', []))}
- **MX 记录**:
- **风险分数**:
- **风险因素**:
- **网页相似度**: %
"""
(, ) f:
f.write(report)
()
export_blocklist(analysis)
generate_takedown_report(analysis[])
验收标准
- dnstwist 为目标域名生成域名置换变体
- DNS 解析识别已注册的仿冒域名
- 网页相似度评分检测出克隆钓鱼页面
- 风险评分按威胁级别对域名进行优先排序
- 持续监控检测新注册的域名抢注
- 正确生成封锁列表和撤销报告
参考资料