Communitygithub.com

证书透明度分析(检测钓鱼)

通过 crt.sh 和 Certstream 监控证书透明度日志,及早发现仿冒品牌的钓鱼域名和未经授权的证书签发。

证书透明度分析(检测钓鱼) 是什么?

该技能用于监控 SSL/TLS 证书透明度(CT)日志,以检测钓鱼域名、未经授权的证书签发以及基于证书的攻击基础设施。它支持通过 crt.sh 进行历史查询,以及通过 Certstream 进行实时流式监控,并提供了从数据获取、可疑证书识别到自动化告警和生成威胁情报报告的完整工作流程。技能内置了基于关键词、编辑距离(Levenshtein)和域名相似度的检测算法,可识别域名抢注、品牌关键词滥用等可疑模式。适用于安全分析师、SOC 团队以及任何需要构建钓鱼检测规则或进行威胁狩猎的开发者。支持 Python 环境,兼容 crt.sh 的 REST API 和 Certstream 的 WebSocket 端点。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/mukul975/Anthropic-Cybersecurity-Skills/tree/main/skills/analyzing-certificate-transparency-for-phishing

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

分析证书透明度以检测钓鱼

概述

证书透明度(Certificate Transparency,CT)是一项互联网安全标准,它创建了一个公开的、只可追加的 SSL/TLS 证书签发日志。监控 CT 日志能够及早发现:注册模仿合法品牌的钓鱼域名、针对自有域名的未授权证书签发,以及基于证书的攻击基础设施。本技能涵盖通过 crt.sh 查询 CT 日志、使用 Certstream 进行实时监控、构建针对可疑证书的自动告警,以及将发现集成到威胁情报工作流中。

何时使用

  • 调查需要分析证书透明度以检测钓鱼的安全事件时
  • 为此领域构建检测规则或威胁狩猎查询时
  • SOC 分析师需要此类分析的结构化流程时
  • 验证相关攻击技术的安全监控覆盖范围时

前提条件

  • Python 3.9+,并安装 requests、certstream、tldextract、Levenshtein 库
  • 能访问 crt.sh (https://crt.sh/) 以查询历史 CT 日志
  • 能连接 Certstream (https://certstream.calidog.io/) 以进行实时监控
  • 准备好要监控的组织域名和品牌关键词列表
  • 理解 SSL/TLS 证书结构和签发流程

关键概念

证书透明度日志

CT 日志是经过密码学保障、公开可审计、只可追加的 TLS 证书签发记录。主流 CA(Let's Encrypt、DigiCert、Sectigo、Google Trust Services)会将所有已签发证书提交到多个 CT 日志。截至 2025 年,Chrome 和 Safari 要求所有公开信任的证书必须包含 CT 信息。

基于 CT 的钓鱼检测

攻击者会注册近似域名,并获取免费证书(通常来自 Let's Encrypt),使钓鱼网站通过 HTTPS 显得合法。CT 监控能及早发现这些行为,因为证书在钓鱼活动启动之前就会出现在日志中,从而为主动拦截提供时间窗口。

crt.sh 数据库

crt.sh 是由 Sectigo 运营的免费 Web 界面和 PostgreSQL 数据库,它索引了所有 CT 日志。它支持通配符搜索(%.example.com)、直接 SQL 查询以及 JSON API 响应。它追踪所有主流 CT 日志中的证书签发、过期和吊销信息。

工作流

步骤 1:查询 crt.sh 获取证书历史

import requests
import json
from datetime import datetime
import tldextract

class CTLogMonitor:
    CRT_SH_URL = "https://crt.sh"

    def __init__(self, monitored_domains, brand_keywords):
        self.monitored_domains = monitored_domains
        self.brand_keywords = [k.lower() for k in brand_keywords]

    def query_crt_sh(self, domain, include_expired=False):
        """查询 crt.sh 获取匹配某个域名的证书。"""
        params = {
            "q": f"%.{domain}",
            "output": "json",
        }
        if not include_expired:
            params["exclude"] = "expired"

        resp = requests.get(self.CRT_SH_URL, params=params, timeout=30)
        if resp.status_code == 200:
            certs = resp.json()
            print(f"[+] crt.sh: *.{domain} 有 {len(certs)} 个证书")
            return certs
        return []

    def find_suspicious_certs(self, domain):
        """查找可能是钓鱼尝试的证书。"""
        certs = self.query_crt_sh(domain)
        suspicious = []

        for cert in certs:
            common_name = cert.get("common_name", "").lower()
            name_value = cert.get("name_value", "").lower()
            issuer = cert.get("issuer_name", "")
            not_before = cert.get("not_before", "")
            not_after = cert.get("not_after", "")

            # 检查是否为目标域名本身(合法)
            extracted = tldextract.extract(common_name)
            cert_domain = f"{extracted.domain}.{extracted.suffix}"
            if cert_domain == domain:
                continue  # 合法证书

            # 标记可疑模式
            flags = []
            if domain.replace(".", "") in common_name.replace(".", ""):
                flags.append("包含目标域名字符串")
            if any(kw in common_name for kw in self.brand_keywords):
                flags.append("包含品牌关键词")
            if "let's encrypt" in issuer.lower():
                flags.append("免费CA (Let's Encrypt)")

            if flags:
                suspicious.append({
                    "common_name": cert.get("common_name", ""),
                    "name_value": cert.get("name_value", ""),
                    "issuer": issuer,
                    "not_before": not_before,
                    "not_after": not_after,
                    "serial": cert.get("serial_number", ""),
                    "flags": flags,
                    "crt_sh_id": cert.get("id", ""),
                    "crt_sh_url": f"https://crt.sh/?id={cert.get('id', '')}",
                })

        print(f"[+] 发现 {len(suspicious)} 个可疑证书")
        return suspicious

monitor = CTLogMonitor(
    monitored_domains=["mycompany.com", "mycompany.org"],
    brand_keywords=["mycompany", "mybrand", "myproduct"],
)
suspicious = monitor.find_suspicious_certs("mycompany.com")
for cert in suspicious[:5]:
    print(f"  [{cert['common_name']}] 标记: {cert['flags']}")

步骤 2:使用 Certstream 进行实时监控

import certstream
import Levenshtein
import re
from datetime import datetime

class CertstreamMonitor:
    def __init__(self, watched_domains, brand_keywords, similarity_threshold=0.8):
        self.watched_domains = [d.lower() for d in watched_domains]
        self.brand_keywords = [k.lower() for k in brand_keywords]
        self.threshold = similarity_threshold
        self.alerts = []

    def start_monitoring(self, max_alerts=100):
        """开始实时 CT 日志监控。"""
        print("[*] 启动 Certstream 监控...")
        print(f"    监控域名: {self.watched_domains}")
        print(f"    品牌关键词: {self.brand_keywords}")

        def callback(message, context):
            if message["message_type"] == "certificate_update":
                data = message["data"]
                leaf = data.get("leaf_cert", {})
                all_domains = leaf.get("all_domains", [])

                for domain in all_domains:
                    domain_lower = domain.lower().strip("*.")
                    if self._is_suspicious(domain_lower):
                        alert = {
                            "domain": domain,
                            "all_domains": all_domains,
                            "issuer": leaf.get("issuer", {}).get("O", ""),
                            "fingerprint": leaf.get("fingerprint", ""),
                            "not_before": leaf.get("not_before", ""),
                            "detected_at": datetime.now().isoformat(),
                            "reason": self._get_reason(domain_lower),
                        }
                        self.alerts.append(alert)
                        print(f"  [告警] {domain} - {alert['reason']}")

                        if len(self.alerts) >= max_alerts:
                            raise KeyboardInterrupt

        try:
            certstream.listen_for_events(callback, url="wss://certstream.calidog.io/")
        except KeyboardInterrupt:
            print(f"\n[+] 监控已停止。共收集 {len(self.alerts)} 条告警。")
        return self.alerts

    def _is_suspicious(self, domain):
        """检查域名是否相对于监控域名是可疑的。"""
        for watched in self.watched_domains:
            # 精确关键词匹配
            watched_base = watched.split(".")[0]
            if watched_base in domain and domain != watched:
                return True

            # Levenshtein 距离(域名抢注检测)
            domain_base = tldextract.extract(domain).domain
            similarity = Levenshtein.ratio(watched_base, domain_base)
            if similarity >= self.threshold and domain_base != watched_base:
                return True

        # 品牌关键词匹配
        for keyword in self.brand_keywords:
            if keyword in domain:
                return True

        return False

    def _get_reason(self, domain):
        """确定域名被标记的原因。"""
        reasons = []
        for watched in self.watched_domains:
            watched_base = watched.split(".")[0]
            if watched_base in domain:
                reasons.append(f"包含 '{watched_base}'")
            domain_base = tldextract.extract(domain).domain
            similarity = Levenshtein.ratio(watched_base, domain_base)
            if similarity >= self.threshold and domain_base != watched_base:
                reasons.append(f"与 '{watched}' 相似 ({similarity:.0%})")
        for kw in self.brand_keywords:
            if kw in domain:
                reasons.append(f"品牌关键词 '{kw}'")
        return "; ".join(reasons) if reasons else "未知"

cs_monitor = CertstreamMonitor(
    watched_domains=["mycompany.com"],
    brand_keywords=["mycompany", "mybrand"],
    similarity_threshold=0.75,
)
alerts = cs_monitor.start_monitoring(max_alerts=50)

步骤 3:从 CT 日志枚举子域名

def enumerate_subdomains_ct(domain):
    """从证书透明度日志中发现所有子域名。"""
    params = {"q": f"%.{domain}", "output": "json"}
    resp = requests.get("https://crt.sh", params=params, timeout=30)

    if resp.status_code != 200:
        return []

    certs = resp.json()
    subdomains = set()
    for cert in certs:
        name_value = cert.get("name_value", "")
        for name in name_value.split("\n"):
            name = name.strip().lower()
            if name.endswith(f".{domain}") or name == domain:
                name = name.lstrip("*.")
                subdomains.add(name)

    sorted_subs = sorted(subdomains)
    print(f"[+] CT 子域名枚举 {domain}: 共 {len(sorted_subs)} 个子域名")
    return sorted_subs

subdomains = enumerate_subdomains_ct("example.com")
for sub in subdomains[:20]:
    print(f"  {sub}")

步骤 4:生成 CT 情报报告

def generate_ct_report(suspicious_certs, certstream_alerts, domain):
    report = f"""# 证书透明度情报报告
## 目标域名: {domain}
## 生成时间: {datetime.now().isoformat()}

## 摘要
- 发现的可疑证书数: {len(suspicious_certs)}
- 触发的实时告警数: {len(certstream_alerts)}

## 可疑证书 (crt.sh)
| 通用名称 | 签发者 | 标记 | crt.sh 链接 |
|------------|--------|-------|-------------|
"""
    for cert in suspicious_certs[:20]:
        flags = "; ".join(cert.get("flags", []))
        report += (f"| {cert['common_name']} | {cert['issuer'][:30]} "
                   f"| {flags} | [查看]({cert['crt_sh_url']}) |\n")

    report += f"""
## 实时 Certstream 告警
| 域名 | 签发者 | 原因 | 检测时间 |
|--------|--------|--------|----------|
"""
    for alert in certstream_alerts[:20]:
        report += (f"| {alert['domain']} | {alert['issuer']} "
                   f"| {alert['reason']} | {alert['detected_at'][:19]} |\n")

    report += """
## 建议
1. 将标记的域名添加到 DNS 黑洞 / Web 代理黑名单
2. 对确认为钓鱼的域名提交下架请求
3. 持续监控 CT 日志以发现新证书注册
4. 实施 CAA DNS 记录以限制对您域名的证书签发
5. 部署 DMARC 以防止来自近似域名的电子邮件欺骗
"""
    with open(f"ct_report_{domain.replace('.','_')}.md", "w") as f:
        f.write(report)
    print(f"[+] CT 报告已保存")
    return report

generate_ct_report(suspicious, alerts if 'alerts' in dir() else [], "mycompany.com")

验证标准

  • crt.sh 查询返回目标域名的证书数据
  • 能基于近似模式识别出可疑证书
  • Certstream 实时监控能检测到新的钓鱼证书
  • 子域名枚举能从 CT 日志中生成完整的子域名列表
  • 生成的告警带有原因分类
  • CT 情报报告包含可操作的建议

参考资料

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

abusing-dpapi-for-credential-access

Extract and decrypt Windows DPAPI-protected secrets (Credential Manager, browser logins/cookies, Wi-Fi credentials, KeePass keys) online or offline using SharpDPAPI, SharpChrome, Mimikatz, or Impacket

abusing-shadow-credentials-for-privesc

Take over Active Directory accounts by writing attacker-controlled public keys to msDS-KeyCredentialLink (Shadow Credentials) with pyWhisker, Whisker, or Certipy, then authenticate via PKINIT to recover the target

achieving-cmmc-level-2-compliance

>-

acquiring-disk-image-with-dd-and-dcfldd

Create forensically sound bit-for-bit disk images with dd or dcfldd on a Linux forensic workstation, preserving evidence integrity through hash verification (MD5/SHA) during acquisition. Use when imaging a suspect drive, USB device, or memory card for investigation, preserving volatile disk evidence during incident response, or producing a verified copy for legal or law-enforcement proceedings before any destructive analysis.

analyzing-active-directory-acl-abuse

Detect dangerous ACL misconfigurations in Active Directory using ldap3

analyzing-android-malware-with-apktool

Perform static analysis of Android APK malware using apktool for resource decompilation, jadx for Java source recovery, and androguard for manifest inspection, dangerous permission-combination detection, and identification of obfuscated code, dynamic code loading, and reflection-based API calls. Use to statically triage a suspicious APK without executing it or to build mobile malware detection rules.

analyzing-api-gateway-access-logs

Parses API Gateway access logs (AWS API Gateway, Kong, Nginx) to detect

analyzing-apt-group-with-mitre-navigator

Query ATT&CK data with attackcti, mitreattack-python, and stix2, then build MITRE ATT&CK Navigator layers and multi-layer heatmap overlays mapping one or more APT groups

analyzing-azure-activity-logs-for-threats

Queries Azure Monitor activity logs and sign-in logs via azure-monitor-query

analyzing-bootkit-and-rootkit-samples

Analyzes bootkit and advanced rootkit malware infecting the Master

analyzing-browser-forensics-with-hindsight

Parse Chromium-based browser databases with Hindsight to extract and correlate browsing history, downloads, cookies, cached content, autofill data, saved passwords, and extensions from Chrome, Edge, Brave, Opera, and Vivaldi into a unified timeline (XLSX, JSON, or SQLite output). Use during incident response, insider-threat investigations, or criminal cases when you need to reconstruct a user

analyzing-campaign-attribution-evidence

Systematically evaluate cyber-campaign evidence to attribute an operation to a threat actor, using the Diamond Model and Analysis of Competing Hypotheses (ACH) to weigh infrastructure overlaps, TTP consistency, malware code similarity, and timing/language artifacts into confidence-weighted attribution assessments. Use when an incident investigation needs a defensible attribution confidence level.

analyzing-cloud-storage-access-patterns

Detect abnormal access in AWS S3, GCS, and Azure Blob Storage by analyzing CloudTrail Data Events, GCS audit logs, and Azure Storage Analytics for after-hours bulk downloads, new-IP access, and API-call spikes (e.g. GetObject) via statistical baselines and time-series anomaly detection. Use when investigating suspected cloud data exfiltration or building related detection rules.

analyzing-cobalt-strike-beacon-configuration

Extract and analyze Cobalt Strike beacon configuration from PE files

analyzing-cobaltstrike-malleable-c2-profiles

Parse and analyze Cobalt Strike Malleable C2 profiles with dissect.cobaltstrike (profiles and beacon-payload configs) and pyMalleableC2 (AST parsing) to extract HTTP/DNS transforms, URIs, headers, sleep/jitter, and injection behavior, then generate network detection signatures. Use when reverse-engineering a captured malleable profile or building detections against Cobalt Strike Beacon traffic.

analyzing-command-and-control-communication

Analyzes malware C2 communication over HTTP, HTTPS, DNS, and custom

analyzing-cyber-kill-chain

Analyzes intrusion activity against the Lockheed Martin Cyber Kill Chain

analyzing-disk-image-with-autopsy

Perform comprehensive forensic analysis of raw (dd), E01, or AFF disk images with Autopsy and The Sleuth Kit, recovering deleted files, examining metadata and embedded artifacts, keyword searching, and building investigation timelines with visual reports. Use for structured analysis of a forensic disk image or when stakeholders need visual reports from evidence.

analyzing-dns-logs-for-exfiltration

Analyzes DNS query logs to detect data exfiltration via DNS tunneling,

analyzing-docker-container-forensics

Investigate compromised Docker containers by analyzing images, layers,

相关技能