Bevor ein Threat-Intelligence-Team überhaupt etwas scannt, kann es erstaunlich viel über eine Domain aus Aufzeichnungen erfahren, die andere Parteien bereits führen. Certificate-Logs listen ihre Hostnamen auf, Registrierungsdaten liefern ihre Daten und Nameserver, und Web-Archive zeigen, was sie früher veröffentlicht hat. Für Sicherheitsteams ist diese passive Phase der günstigste und unauffälligste Teil der Arbeit, und sie zeigt, was auch jeder Angreifer sehen kann.
Dieser Leitfaden behandelt die wichtigsten passiven Quellen, einen kleinen getesteten Collector und den Punkt, an dem “passiv” aufhört, wahr zu sein. Nutzen Sie ihn für Ihre eigene Organisation oder innerhalb eines autorisierten Umfangs.
Was passiv bedeutet
Passive Sammlung bedeutet, dass Ihre Anfragen an einen Dritten gehen, der die Daten bereits besitzt, und niemals an die Server des Ziels. MITRE ATT&CK beschreibt die Version des Angreifers unter Reconnaissance, einschließlich der Suche in “frei verfügbaren technischen Datenbanken” (T1596), mit Einträgen zu DNS und passivem DNS, WHOIS, digitalen Zertifikaten, CDNs und Scan-Datenbanken.
MITRE erklärt auch, warum Verteidiger das interessieren sollte. Es heißt, diese Aktivität könne außerhalb der Sichtbarkeit der Zielorganisation stattfinden, was sie schwer erkennbar macht, und sie lasse sich nicht leicht durch präventive Kontrollen eindämmen. Man kann jemanden nicht daran hindern, ein öffentliches Log zu lesen. Was man tun kann, ist zuerst selbst nachzusehen und zu verringern, was die Logs preisgeben.
Die Quellen, die sich zu prüfen lohnen
| Quelle | Was sie liefert | Ihre Grenzen |
|---|---|---|
| Certificate-Transparency-Logs | Hostnamen auf ausgestellten TLS-Zertifikaten | Zeigt Namen, aber nicht, ob sie live sind; Wildcards verbergen Subdomains |
| RDAP | Registrar, Daten, Nameserver | Persönliche Daten sind meist geschwärzt |
| Web-Archive | Alte Seiten, URLs und Hosts | Nur das, was erfasst wurde, oft Jahre alt |
| Passives DNS | Zu welchen IPs ein Name aufgelöst wurde, und wann | Meist kommerziell, und die Abdeckung variiert |
| Internet-Scan-Datenbanken | Offene Ports und Banner, die jemand anderes gesehen hat | Momentaufnahmen, können veraltet sein |
Certificate Transparency ist die ergiebigste kostenlose Quelle. RFC 9162 beschreibt ein Protokoll zur “öffentlichen Protokollierung der Existenz” von TLS-Zertifikaten, damit jeder Zertifizierungsstellen prüfen kann, und die Logs sind nur anhängbar (append-only). Das bedeutet auch, dass ein Zertifikat für staging.internal-tool.example.com sichtbar bleibt, selbst wenn der Host nur eine Woche existierte.
RDAP ist der strukturierte Ersatz für WHOIS. ICANN erklärt, dass seit dem 28. Januar 2025 generische Top-Level-Domain-Registrys und Registrare RDAP bereitstellen müssen und nicht mehr verpflichtet sind, WHOIS bereitzustellen, sodass neue Tools dort ansetzen sollten.
Unser Leitfaden zu was OSINT ist und wo seine rechtlichen Grenzen liegen behandelt die weitere Disziplin, einschließlich Quellen über Personen, die dieser Leitfaden ausklammert.
Ein kleiner passiver Collector
Das untenstehende Skript fragt drei Dienste von Drittanbietern zu einer Domain ab und verbindet sich niemals mit der Domain selbst. Es läuft auf Python 3.9 und neuer und benötigt nur requests. Es läuft weiter, wenn eine Quelle ausfällt, denn kostenlose öffentliche Dienste sind das oft: Beim Testen dafür lieferten crt.sh und die Wayback Machine jeweils über mehrere Minuten hinweg Fehler und Timeouts.
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'}")
Ausgabe für 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
Zwei Details in diesem Code stammen aus dem Testen. Certificate-Logs enthalten mehr als nur Hostnamen: Eine frühe Version nutzte eine einfache Suffixprüfung und ließ dadurch ein Zertifikat-Label mit Leerzeichen sowie ein Lookalike, m.testexample.com, durch. Deshalb prüft in_scope auf eine vollständige Subdomain. Das andere Detail ist, dass jede Quelle etwas anderes sieht, weshalb man sie vergleichen sollte, statt sich auf eine zu verlassen. Ein Hostname, der nur in alten Archiven auftaucht, könnte verschwunden sein, und einer, der nur in aktuellen Zertifikaten auftaucht, könnte neu sein.
Wo passiv aufhört, passiv zu sein
Die Grenze ist leichter zu überschreiten, als es scheint.
- Eine gefundene URL öffnen. Das Aufrufen eines live Hostnamens aus Ihren Ergebnissen sendet dem Ziel eine Anfrage von Ihrer IP.
- Archivierte Seiten mit Live-Ressourcen. Eine gespeicherte Seite kann trotzdem versuchen, Bilder oder Skripte von der Originalseite zu laden. Deaktivieren Sie das Laden externer Inhalte, oder lesen Sie die Rohantwort.
- DNS-Abfragen. Eine Abfrage über einen Resolver kann die autoritativen Nameserver des Ziels erreichen. Historische passive DNS-Daten tun das nicht.
- Ihre Abfragen sind für die Quelle sichtbar. Der Dienst, den Sie abfragen, erfährt, wonach Sie suchen. Bei sensiblen Untersuchungen sollte man bedenken, was das preisgibt, und die Nutzungsbedingungen und Ratenbegrenzungen lesen.
Behandeln Sie Ergebnisse als Hinweise. Aufzeichnungen veralten, Zertifikate überdauern Hosts, und Shared Hosting macht die IP-Historie irreführend.
Was mit den Ergebnissen zu tun ist
Für Verteidiger ergibt sich daraus eine Liste dessen, was die Außenwelt bereits sehen kann. Eine sinnvolle Routine:
- Mit Ihrem Asset-Inventar abgleichen. Ein Hostname, den Sie keinem Eigentümer zuordnen können, ist der Befund.
- Prüfen, was Ihre Namen preisgeben. Namen wie
vpn-oldoderjenkins-testin öffentlichen Logs helfen Angreifern. Erwägen Sie Wildcard-Zertifikate für interne Namensgebung, wo sie angemessen sind, und wägen Sie dabei deren eigene Risiken ab. - Wiederholen, und auf Neues achten. Unser Leitfaden zum kontinuierlichen Monitoring der externen Angriffsfläche zeigt, wie man daraus einen Diff mit Verantwortlichen macht.
- Bewusst zu aktiven Prüfungen übergehen. Verifizieren Sie Ihre eigenen Assets direkt. Für alles außerhalb Ihrer Organisation holen Sie zuerst eine Autorisierung ein. Wenn Threat-Research sehen muss, was eine verdächtige Seite echten Besuchern in einem bestimmten Land zeigt, ist das aktive Arbeit mit eigenen Regeln, behandelt in unserem Beitrag zu Residential Proxies für Threat Intelligence.
Fazit
Passive Sammlung liefert Ihnen ein nützliches Bild einer Domain nahezu kostenlos und ohne sie zu alarmieren. Beginnen Sie mit Certificate-Logs, RDAP und Archiven, vergleichen Sie sie, behandeln Sie Funde als Hinweise, und seien Sie ehrlich über den Moment, in dem Ihre Arbeit aufhört, passiv zu sein. Führen Sie es zuerst an sich selbst durch, denn jeder andere kann es auch.
Quellen und Referenzen
- 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