O que Analisando Transparência de Certificados para Phishing faz?
Visão Geral
A Transparência de Certificados (CT) é um padrão de segurança da Internet que cria um registro público, somente de anexação, de todos os certificados SSL/TLS emitidos. O monitoramento de logs CT permite a detecção precoce de domínios de phishing que registram certificados imitando marcas legítimas, emissão não autorizada de certificados para domínios próprios e infraestrutura de ataque baseada em certificados. Esta skill abrange consultas a logs CT via crt.sh, monitoramento em tempo real com Certstream, construção de alertas automatizados para certificados suspeitos e integração dos resultados em fluxos de inteligência de ameaças.
Quando Usar
- Ao investigar incidentes de segurança que exigem análise de transparência de certificados para phishing
- Ao construir regras de detecção ou consultas de caça a ameaças para este domínio
- Quando analistas de SOC precisam de procedimentos estruturados para este tipo de análise
- Ao validar a cobertura de monitoramento de segurança para técnicas de ataque relacionadas
Pré-requisitos
- Python 3.9+ com as bibliotecas
requests,certstream,tldextract,Levenshtein - Acesso ao crt.sh (https://crt.sh/) para consultas históricas de logs CT
- Certstream (https://certstream.calidog.io/) para monitoramento em tempo real
- Lista de domínios da organização e palavras-chave de marca a monitorar
- Compreensão da estrutura de certificados SSL/TLS e do processo de emissão
Conceitos Principais
Logs de Transparência de Certificados
Os logs CT são registros criptograficamente garantidos, publicamente auditáveis e somente de anexação da emissão de certificados TLS. As principais ACs (Let's Encrypt, DigiCert, Sectigo, Google Trust Services) submetem todos os certificados emitidos a vários logs CT. A partir de 2025, Chrome e Safari exigem CT para todos os certificados publicamente confiáveis.
Detecção de Phishing via CT
Atacantes registram domínios semelhantes e obtêm certificados gratuitos (frequentemente da Let's Encrypt) para fazer sites de phishing parecerem legítimos com HTTPS. O monitoramento CT detecta isso precocemente porque o certificado aparece nos logs antes do lançamento da campanha de phishing, fornecendo uma janela para bloqueio proativo.
Banco de Dados crt.sh
crt.sh é uma interface web gratuita e um banco de dados PostgreSQL operado pela Sectigo que indexa logs CT. Ele suporta pesquisas com curinga (%.example.com), consultas SQL diretas e respostas de API JSON. Ele rastreia a emissão, expiração e revogação de certificados em todos os principais logs CT.
Fluxo de Trabalho
Passo 1: Consultar crt.sh para Histórico de Certificados
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):
"""Consultar crt.sh por certificados correspondentes a um domínio."""
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: {len(certs)} certificados para *.{domain}")
return certs
return []
def find_suspicious_certs(self, domain):
"""Encontrar certificados que podem ser tentativas de phishing."""
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", "")
# Verificar correspondências exatas de domínio (legítimas)
extracted = tldextract.extract(common_name)
cert_domain = f"{extracted.domain}.{extracted.suffix}"
if cert_domain == domain:
continue # Certificado legítimo
# Sinalizar padrões suspeitos
flags = []
if domain.replace(".", "") in common_name.replace(".", ""):
flags.append("contém string do domínio alvo")
if any(kw in common_name for kw in self.brand_keywords):
flags.append("contém palavra-chave da marca")
if "let's encrypt" in issuer.lower():
flags.append("AC gratuita (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"[+] Encontrados {len(suspicious)} certificados suspeitos")
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']}] Flags: {cert['flags']}")
Passo 2: Monitoramento em Tempo Real com 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):
"""Iniciar monitoramento de logs CT em tempo real."""
print("[*] Iniciando monitoramento Certstream...")
print(f" Observando: {self.watched_domains}")
print(f" Palavras-chave: {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" [ALERTA] {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[+] Monitoramento interrompido. {len(self.alerts)} alertas coletados.")
return self.alerts
def _is_suspicious(self, domain):
"""Verificar se o domínio é suspeito em relação aos domínios observados."""
for watched in self.watched_domains:
# Correspondência exata de palavra-chave
watched_base = watched.split(".")[0]
if watched_base in domain and domain != watched:
return True
# Distância Levenshtein (detecção de typosquatting)
domain_base = tldextract.extract(domain).domain
similarity = Levenshtein.ratio(watched_base, domain_base)
if similarity >= self.threshold and domain_base != watched_base:
return True
# Correspondência de palavra-chave da marca
for keyword in self.brand_keywords:
if keyword in domain:
return True
return False
def _get_reason(self, domain):
"""Determinar por que o domínio foi sinalizado."""
reasons = []
for watched in self.watched_domains:
watched_base = watched.split(".")[0]
if watched_base in domain:
reasons.append(f"contém '{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"semelhante a '{watched}' ({similarity:.0%})")
for kw in self.brand_keywords:
if kw in domain:
reasons.append(f"palavra-chave da marca '{kw}'")
return "; ".join(reasons) if reasons else "desconhecido"
cs_monitor = CertstreamMonitor(
watched_domains=["mycompany.com"],
brand_keywords=["mycompany", "mybrand"],
similarity_threshold=0.75,
)
alerts = cs_monitor.start_monitoring(max_alerts=50)
Passo 3: Enumerar Subdomínios a partir de Logs CT
def enumerate_subdomains_ct(domain):
"""Descobrir todos os subdomínios a partir de logs de Transparência de Certificados."""
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"[+] Enumeração de subdomínios CT para {domain}: {len(sorted_subs)} subdomínios")
return sorted_subs
subdomains = enumerate_subdomains_ct("example.com")
for sub in subdomains[:20]:
print(f" {sub}")
Passo 4: Gerar Relatório de Inteligência CT
def generate_ct_report(suspicious_certs, certstream_alerts, domain):
report = f"""# Relatório de Inteligência de Transparência de Certificados
## Domínio Alvo: {domain}
## Gerado em: {datetime.now().isoformat()}
## Resumo
- Certificados suspeitos encontrados: {len(suspicious_certs)}
- Alertas em tempo real disparados: {len(certstream_alerts)}
## Certificados Suspeitos (crt.sh)
| Nome Comum | Emissor | Flags | Link crt.sh |
|------------|--------|-------|-------------|
"""
for cert in suspicious_certs[:20]:
flags = "; ".join(cert.get("flags", []))
report += (f"| {cert['common_name']} | {cert['issuer'][:30]} "
f"| {flags} | [Ver]({cert['crt_sh_url']}) |\n")
report += f"""
## Alertas em Tempo Real do Certstream
| Domínio | Emissor | Motivo | Detectado em |
|--------|--------|--------|----------|
"""
for alert in certstream_alerts[:20]:
report += (f"| {alert['domain']} | {alert['issuer']} "
f"| {alert['reason']} | {alert['detected_at'][:19]} |\n")
report += """
## Recomendações
1. Adicione domínios sinalizados ao sumidouro DNS / lista de bloqueio do proxy web
2. Envie solicitações de remoção para domínios de phishing confirmados
3. Monitore continuamente os logs CT para novos registros de certificados
4. Implemente registros DNS CAA para restringir a emissão de certificados para seus domínios
5. Implante DMARC para evitar falsificação de e-mail a partir de domínios semelhantes
"""
with open(f"ct_report_{domain.replace('.','_')}.md", "w") as f:
f.write(report)
print(f"[+] Relatório CT salvo")
return report
generate_ct_report(suspicious, alerts if 'alerts' in dir() else [], "mycompany.com")
Critérios de Validação
- Consultas ao crt.sh retornam dados de certificados para os domínios alvo
- Certificados suspeitos identificados com base em padrões de semelhança
- Monitoramento em tempo real do Certstream detecta novos certificados de phishing
- Enumeração de subdomínios produz uma lista abrangente a partir de logs CT
- Alertas gerados com classificação de motivo
- Relatório de inteligência CT criado com recomendações acionáveis