Antes de que un equipo de inteligencia de amenazas escanee nada, puede aprender una cantidad sorprendente sobre un dominio a partir de registros que ya mantienen otras partes. Los registros de certificados listan sus nombres de host, los datos de registro dan sus fechas y servidores de nombres, y los archivos web muestran lo que solía publicar. Para los equipos de seguridad, esta fase pasiva es la parte más barata y silenciosa del trabajo, y muestra lo que cualquier atacante también puede ver.
Esta guía cubre las principales fuentes pasivas, un pequeño recolector probado, y dónde deja de ser cierto que algo es “pasivo”. Úsala en tu propia organización o dentro de un alcance autorizado.
Qué significa pasivo
La recolección pasiva significa que tus solicitudes van a un tercero que ya posee los datos, y nunca a los servidores del objetivo. MITRE ATT&CK describe la versión del atacante bajo Reconnaissance, incluyendo la búsqueda de “bases de datos técnicas de libre acceso” (T1596), con entradas para DNS y DNS pasivo, WHOIS, certificados digitales, CDNs y bases de datos de escaneo.
MITRE también explica por qué a los defensores les debería importar. Dice que esta actividad puede tener lugar fuera de la visibilidad de la organización objetivo, lo que la hace difícil de detectar, y que no se puede mitigar fácilmente con controles preventivos. No puedes bloquear a alguien que lee un registro público. Lo que sí puedes hacer es mirar primero y reducir lo que los registros revelan.
Las fuentes que vale la pena revisar
| Fuente | Qué te ofrece | Sus límites |
|---|---|---|
| Registros de Certificate Transparency | Nombres de host en certificados TLS emitidos | Muestra nombres, no si están activos; los comodines ocultan subdominios |
| RDAP | Registrador, fechas, servidores de nombres | Los datos personales suelen estar redactados |
| Archivos web | Páginas, URLs y hosts antiguos | Solo lo que se capturó, a menudo con años de antigüedad |
| DNS pasivo | A qué IPs resolvió un nombre, y cuándo | Mayormente comercial, y la cobertura varía |
| Bases de datos de escaneo en Internet | Puertos abiertos y banners vistos por otra persona | Instantáneas, y pueden estar desactualizadas |
Certificate Transparency es la fuente gratuita más rica. El RFC 9162 describe un protocolo para “registrar públicamente la existencia” de certificados TLS de modo que cualquiera pueda auditar a las autoridades de certificación, y los registros son de solo adición. Eso también significa que un certificado para staging.internal-tool.example.com sigue siendo visible, incluso si el host solo duró una semana.
RDAP es el reemplazo estructurado de WHOIS. ICANN dice que desde el 28 de enero de 2025, los registros y registradores de dominios genéricos de nivel superior deben proporcionar RDAP y ya no están obligados a proporcionar WHOIS, así que las herramientas nuevas deberían empezar por ahí.
Nuestra guía sobre qué es el OSINT y dónde están sus límites legales cubre la disciplina más amplia, incluyendo fuentes sobre personas, que esta guía deja fuera.
Un pequeño recolector pasivo
El script de abajo consulta a tres servicios de terceros sobre un dominio y nunca se conecta al dominio en sí. Funciona con Python 3.9 o posterior y solo necesita requests. Sigue funcionando cuando una fuente está caída, porque los servicios públicos gratuitos a menudo lo están: mientras probábamos esto, crt.sh y la Wayback Machine devolvieron errores y tiempos de espera agotados durante varios minutos seguidos cada uno.
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'}")
Salida 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
Dos detalles de ese código surgieron de las pruebas. Los registros de certificados contienen más que nombres de host: una versión temprana usaba una simple comprobación de sufijo y dejaba pasar una etiqueta de certificado con espacios y un imitador, m.testexample.com. Por eso in_scope comprueba un subdominio completo. El otro detalle es que cada fuente ve cosas distintas, así que hay que compararlas en lugar de confiar en una sola. Un nombre de host que solo aparece en archivos antiguos puede haber desaparecido, y uno que solo aparece en certificados recientes puede ser nuevo.
Dónde deja de ser pasivo lo pasivo
La línea es más fácil de cruzar de lo que parece.
- Abrir una URL que encontraste. Visitar un nombre de host activo de tus resultados envía al objetivo una solicitud desde tu IP.
- Páginas archivadas con recursos activos. Una página guardada puede seguir intentando cargar imágenes o scripts del sitio original. Desactiva la carga externa, o lee la respuesta en bruto.
- Consultas DNS. Una consulta a través de un resolutor puede llegar a los servidores de nombres autoritativos del objetivo. Los datos históricos de DNS pasivo no lo hacen.
- Tus consultas son visibles para la fuente. El servicio al que preguntas sabe qué estás mirando. Para investigaciones sensibles, considera lo que eso revela, y lee sus términos y límites de frecuencia.
Trata los resultados como pistas. Los registros se quedan obsoletos, los certificados sobreviven a los hosts, y el alojamiento compartido hace que el historial de IPs sea engañoso.
Qué hacer con los resultados
Para los defensores, el resultado es una lista de lo que el mundo exterior ya puede ver. Una rutina sensata:
- Compara con tu inventario de activos. Un nombre de host que no puedas asociar a un propietario es el hallazgo.
- Revisa qué revelan tus nombres. Nombres como
vpn-oldojenkins-testen registros públicos ayudan a los atacantes. Considera certificados comodín para la nomenclatura interna cuando sean apropiados, sopesando sus propios riesgos. - Repítelo, y alerta sobre lo que sea nuevo. Nuestra guía sobre monitorización continua de la superficie de ataque externa muestra cómo convertir esto en una comparación con propietarios asignados.
- Pasa a comprobaciones activas de forma deliberada. Verifica tus propios activos directamente. Para cualquier cosa fuera de tu organización, consigue autorización primero. Cuando la investigación de amenazas necesita ver qué muestra un sitio sospechoso a visitantes reales en un país determinado, eso es trabajo activo con sus propias reglas, tratado en nuestro artículo sobre proxies residenciales para inteligencia de amenazas.
La conclusión
La recolección pasiva te da una imagen útil de un dominio por casi ningún coste y sin alertarlo. Empieza con registros de certificados, RDAP y archivos, compáralos, trata lo que encuentres como pistas, y sé honesto sobre el momento en que tu trabajo deja de ser pasivo. Pruébalo primero contigo mismo, porque cualquier otro puede hacerlo.
Fuentes y referencias
- 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