Antes que uma equipe de inteligência de ameaças escaneie qualquer coisa, ela pode aprender uma quantidade surpreendente sobre um domínio a partir de registros que outras partes já mantêm. Logs de certificados listam seus hostnames, dados de registro fornecem suas datas e nameservers, e arquivos web mostram o que ele costumava publicar. Para equipes de segurança, esse estágio passivo é a parte mais barata e silenciosa do trabalho, e mostra o que qualquer atacante também pode ver.
Este guia cobre as principais fontes passivas, um pequeno coletor testado, e onde “passivo” deixa de ser verdade. Use-o na sua própria organização ou dentro de um escopo autorizado.
O que significa passivo
Coleta passiva significa que suas requisições vão para um terceiro que já possui os dados, e nunca para os servidores do alvo. A MITRE ATT&CK descreve a versão do atacante em Reconnaissance, incluindo a busca em “bancos de dados técnicos livremente disponíveis” (T1596), com entradas para DNS e DNS passivo, WHOIS, certificados digitais, CDNs e bancos de dados de varredura.
A MITRE também explica por que os defensores devem se preocupar. Ela diz que essa atividade pode ocorrer fora da visibilidade da organização alvo, o que dificulta a detecção, e que não pode ser facilmente mitigada com controles preventivos. Você não pode bloquear alguém lendo um log público. O que você pode fazer é olhar primeiro e reduzir o que os logs revelam.
As fontes que vale a pena verificar
| Fonte | O que ela fornece | Seus limites |
|---|---|---|
| Logs de Certificate Transparency | Hostnames em certificados TLS emitidos | Mostra nomes, não se estão ativos; wildcards escondem subdomínios |
| RDAP | Registrador, datas, nameservers | Dados pessoais geralmente são redigidos |
| Arquivos web | Páginas, URLs e hosts antigos | Apenas o que foi capturado, muitas vezes anos atrás |
| DNS passivo | Para quais IPs um nome resolveu, e quando | Majoritariamente comercial, e a cobertura varia |
| Bancos de dados de varredura da internet | Portas abertas e banners vistos por outra pessoa | Instantâneos, e podem estar desatualizados |
Certificate Transparency é a fonte gratuita mais rica. A RFC 9162 descreve um protocolo para “registrar publicamente a existência” de certificados TLS, de modo que qualquer um possa auditar autoridades certificadoras, e os logs são somente para anexação. Isso também significa que um certificado para staging.internal-tool.example.com permanece visível, mesmo que o host tenha durado apenas uma semana.
RDAP é o substituto estruturado do WHOIS. A ICANN diz que, desde 28 de janeiro de 2025, os registros e registradores de domínios genéricos de primeiro nível devem fornecer RDAP e não são mais obrigados a fornecer WHOIS, então novas ferramentas devem começar por aí.
Nosso guia sobre o que é OSINT e onde estão seus limites legais cobre a disciplina mais ampla, incluindo fontes sobre pessoas, que este guia deixa de fora.
Um pequeno coletor passivo
O script abaixo consulta três serviços de terceiros sobre um domínio e nunca se conecta ao próprio domínio. Ele roda em Python 3.9 ou superior e precisa apenas de requests. Ele continua funcionando quando uma fonte está fora do ar, porque serviços públicos gratuitos costumam estar: durante o teste disso, crt.sh e o Wayback Machine retornaram erros e timeouts por vários minutos seguidos.
from __future__ import annotations
import sys
import time
import requests
TIMEOUT = (10, 60)
HEADERS = {"User-Agent": "passive-recon-demo/1.0"}
def get_json(url, params=None):
"""Every request goes to a third-party source, never to the target itself."""
for attempt in range(3):
try:
r = requests.get(url, params=params, headers=HEADERS, timeout=TIMEOUT)
r.raise_for_status()
return r.json()
except requests.RequestException:
if attempt == 2:
raise
time.sleep(10 * (attempt + 1)) # free public services go down often
def in_scope(host, domain):
"""The domain or a real subdomain of it; CT names also hold labels and lookalikes."""
return " " not in host and (host == domain or host.endswith("." + domain))
def certificate_names(domain):
"""Hostnames from Certificate Transparency logs, through crt.sh."""
rows = get_json("https://crt.sh/", {"q": f"%.{domain}", "output": "json"})
names = {n.strip().lower() for row in rows for n in row["name_value"].splitlines()}
return sorted(n for n in names if "*" not in n and in_scope(n, domain))
def archived_hosts(domain, limit=500):
"""Hostnames the Wayback Machine has captured, without loading any live page."""
rows = get_json("https://web.archive.org/cdx/search/cdx", {
"url": domain, "matchType": "domain", "output": "json",
"fl": "original", "limit": limit,
})
urls = [row[0] for row in rows[1:]] # the first row is the header
hosts = {u.split("//")[-1].split("/")[0].split(":")[0].lower() for u in urls}
return sorted(h for h in hosts if in_scope(h, domain))
def registration(domain):
"""Registrar and key dates from RDAP, the structured successor to WHOIS."""
data = get_json(f"https://rdap.org/domain/{domain}")
dates = {e["eventAction"]: e["eventDate"][:10] for e in data.get("events", [])}
return {"registered": dates.get("registration"), "expires": dates.get("expiration"),
"nameservers": sorted(n["ldhName"].lower() for n in data.get("nameservers", []))}
def collect(domain):
sources = {"certificates": certificate_names, "archive": archived_hosts,
"rdap": registration}
found, failed = {}, {}
for name, fetch in sources.items():
try:
found[name] = fetch(domain)
except (requests.RequestException, ValueError, KeyError) as exc:
failed[name] = type(exc).__name__ # a source being down is a result too
return found, failed
if __name__ == "__main__":
domain = sys.argv[1] if len(sys.argv) > 1 else "example.com"
found, failed = collect(domain)
hosts = set(found.get("certificates", [])) | set(found.get("archive", []))
only_ct = set(found.get("certificates", [])) - set(found.get("archive", []))
only_archive = set(found.get("archive", [])) - set(found.get("certificates", []))
print(f"{domain}: {len(hosts)} hostnames seen by third parties")
print(f" in certificates only: {sorted(only_ct)[:6]}")
print(f" in the archive only: {sorted(only_archive)[:6]}")
print(f" registration: {found.get('rdap')}")
print(f" sources unavailable: {failed or 'none'}")
Saída para example.com:
example.com: 7 hostnames seen by third parties
in certificates only: ['dev.example.com', 'emudhra-lab.example.com', 'm.example.com', 'products.example.com', 'support.example.com']
in the archive only: []
registration: {'registered': '1995-08-14', 'expires': '2027-08-13', 'nameservers': ['elliott.ns.cloudflare.com', 'hera.ns.cloudflare.com']}
sources unavailable: none
Dois detalhes nesse código vieram dos testes. Logs de certificados contêm mais do que hostnames: uma versão inicial usava uma verificação simples de sufixo e deixava passar um rótulo de certificado com espaços nele e um lookalike, m.testexample.com. É por isso que in_scope verifica um subdomínio completo. O outro detalhe é que cada fonte vê coisas diferentes, então compare-as em vez de confiar em apenas uma. Um hostname presente apenas em arquivos antigos pode já não existir, e um presente apenas em certificados recentes pode ser novo.
Onde o passivo deixa de ser passivo
A linha é mais fácil de cruzar do que parece.
- Abrir uma URL que você encontrou. Visitar um hostname ativo dos seus resultados envia ao alvo uma requisição vinda do seu IP.
- Páginas arquivadas com recursos ativos. Uma página salva ainda pode tentar carregar imagens ou scripts do site original. Desative o carregamento externo, ou leia a resposta bruta.
- Consultas DNS. Uma consulta através de um resolver pode alcançar os nameservers autoritativos do alvo. Dados históricos de DNS passivo não.
- Suas consultas são visíveis para a fonte. O serviço que você consulta sabe o que você está olhando. Para investigações sensíveis, considere o que isso revela, e leia seus termos e limites de taxa.
Trate os resultados como pistas. Registros ficam desatualizados, certificados sobrevivem aos hosts, e hospedagem compartilhada torna o histórico de IP enganoso.
O que fazer com os resultados
Para os defensores, o resultado é uma lista do que o mundo externo já consegue ver. Uma rotina sensata:
- Compare com seu inventário de ativos. Um hostname que você não consegue associar a um dono é o achado.
- Verifique o que seus nomes revelam. Nomes como
vpn-oldoujenkins-testem logs públicos ajudam atacantes. Considere certificados wildcard para nomenclatura interna onde forem apropriados, pesando seus próprios riscos. - Repita, e alerte sobre o que for novo. Nosso guia sobre monitoramento contínuo de superfície de ataque externa mostra como transformar isso em um diff que tem donos.
- Avance para verificações ativas de forma deliberada. Verifique seus próprios ativos diretamente. Para qualquer coisa fora da sua organização, obtenha autorização primeiro. Quando a pesquisa de ameaças precisa ver o que um site suspeito mostra a visitantes reais em determinado país, isso é trabalho ativo com suas próprias regras, abordado em nosso artigo sobre proxies residenciais para inteligência de ameaças.
Conclusão
A coleta passiva fornece uma visão útil de um domínio por quase nenhum custo e sem alertá-lo. Comece com logs de certificados, RDAP e arquivos, compare-os, trate o que encontrar como pistas, e seja honesto sobre o momento em que seu trabalho deixa de ser passivo. Teste em você mesmo primeiro, porque qualquer outra pessoa pode fazer o mesmo.
Fontes e referências
- MITRE ATT&CK, Reconnaissance (TA0043): attack.mitre.org
- MITRE ATT&CK, Search Open Technical Databases (T1596): attack.mitre.org
- RFC 9162, Certificate Transparency Version 2.0: rfc-editor.org
- Certificate Transparency, How CT works: certificate.transparency.dev
- RFC 9083, JSON Responses for RDAP: rfc-editor.org
- ICANN, Registration Data Access Protocol: icann.org
- Internet Archive, Wayback CDX Server API: github.com/internetarchive/wayback