mcpField
A
analyzing-typosquatting-domains-with-dnstwist

analyzing-typosquatting-domains-with-dnstwist

github.com/mukul975安全35.2k267 次安装8 周前更新
代码仓库

关于 analyzing-typosquatting-domains-with-dnstwist

面向安全分析人员的 DNSTwist 域名仿冒排查技能,指导生成相似域名、检查 DNS 和网页相似度,并筛选潜在恶意域名。

主要功能

  1. 01生成多种相似域名变体
  2. 02查询 DNS 记录
  3. 03比较网页内容与截图
  4. 04按风险筛选域名
  5. 05监控新注册仿冒域名
  6. 06导出阻止列表和报告

适用场景

  1. 01调查针对品牌的仿冒域名
  2. 02开展域名威胁狩猎
  3. 03验证安全监控覆盖情况
  4. 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 外,安装时一起下载的参考资料和脚本。

LICENSE
references/
api-reference.md
scripts/
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