Was macht Analyse von DNS-Protokollen auf Datenexfiltration?
Wann verwenden
Verwenden Sie diesen Skill, wenn:
- SOC-Teams Datenexfiltration durch DNS-Tunneling vermuten, das Proxy-/Firewall-Kontrollen umgeht
- Threat Intelligence auf DNS-basierte C2-Kanäle von Angreifern hinweist (z. B. Cobalt Strike DNS Beacon)
- UEBA anomale DNS-Abfragevolumina von bestimmten Hosts erkennt
- Malware-Analysen DNS-over-HTTPS (DoH) oder DNS-Tunneling-Fähigkeiten aufdecken
Nicht verwenden für standardmäßige DNS-Fehlerbehebung oder Verfügbarkeitsüberwachung – dieser Skill konzentriert sich auf sicherheitsrelevante DNS-Missbrauchserkennung.
Voraussetzungen
- DNS-Abfrageprotokollierung aktiviert (Windows DNS Server, Bind, Infoblox oder Cisco Umbrella)
- DNS-Protokolle in SIEM aufgenommen (Splunk mit
Stream:DNS,dnssourcetype oder Zeek DNS logs) - Passive DNS-Daten für historische Domainauflösungsanalyse
- Basislinie des normalen DNS-Verhaltens (Abfragevolumen, Domainverteilung, TXT-Record-Häufigkeit)
- Python mit
math- undcollections-Bibliotheken für Entropieberechnung
Workflow
Schritt 1: DNS-Tunneling durch Subdomain-Längenanalyse erkennen
DNS-Tunneling kodiert Daten in Subdomain-Labels und erzeugt ungewöhnlich lange Abfragen:
index=dns sourcetype="stream:dns" query_type IN ("A", "AAAA", "TXT", "CNAME", "MX")
| eval domain_parts = split(query, ".")
| eval subdomain = mvindex(domain_parts, 0, mvcount(domain_parts)-3)
| eval subdomain_str = mvjoin(subdomain, ".")
| eval subdomain_len = len(subdomain_str)
| eval tld = mvindex(domain_parts, -1)
| eval registered_domain = mvindex(domain_parts, -2).".".tld
| where subdomain_len > 50
| stats count AS queries, dc(query) AS unique_queries,
avg(subdomain_len) AS avg_subdomain_len,
max(subdomain_len) AS max_subdomain_len,
values(src_ip) AS sources
by registered_domain
| where queries > 20
| sort - avg_subdomain_len
| table registered_domain, queries, unique_queries, avg_subdomain_len, max_subdomain_len, sources
Schritt 2: Hoch-entrope Domainabfragen erkennen (DGA-Erkennung)
Domain-Generierungsalgorithmen erzeugen zufällig erscheinende Domains:
index=dns sourcetype="stream:dns"
| eval domain_parts = split(query, ".")
| eval sld = mvindex(domain_parts, -2)
| eval sld_len = len(sld)
| eval char_count = sld_len
| eval vowels = len(replace(sld, "[^aeiou]", ""))
| eval consonants = len(replace(sld, "[^bcdfghjklmnpqrstvwxyz]", ""))
| eval digits = len(replace(sld, "[^0-9]", ""))
| eval vowel_ratio = if(char_count > 0, vowels / char_count, 0)
| eval digit_ratio = if(char_count > 0, digits / char_count, 0)
| where sld_len > 12 AND (vowel_ratio < 0.2 OR digit_ratio > 0.3)
| stats count AS queries, dc(query) AS unique_domains, values(src_ip) AS sources
by query
| where unique_domains > 10
| sort - queries
Python-basierte Shannon-Entropieberechnung für DNS-Abfragen:
import math
from collections import Counter
def shannon_entropy(text):
"""Berechnet die Shannon-Entropie einer Zeichenkette"""
if not text:
return 0
counter = Counter(text.lower())
length = len(text)
entropy = -sum(
(count / length) * math.log2(count / length)
for count in counter.values()
)
return round(entropy, 4)
# Test mit Beispielen
normal_domain = "google" # Niedrige Entropie
dga_domain = "x8kj2m9p4qw7n" # Hohe Entropie
tunnel_subdomain = "aGVsbG8gd29ybGQ.evil.com" # Base64-kodierte Daten
print(f"Normal: {shannon_entropy(normal_domain)}") # ~2.25
print(f"DGA: {shannon_entropy(dga_domain)}") # ~3.70
print(f"Tunnel: {shannon_entropy(tunnel_subdomain)}") # ~3.50
# Schwellwert: Entropie > 3,5 für Subdomain = wahrscheinlich Tunneling/DGA
Splunk-Implementierung der Entropiebewertung:
index=dns sourcetype="stream:dns"
| eval domain_parts = split(query, ".")
| eval check_string = mvindex(domain_parts, 0)
| eval check_len = len(check_string)
| where check_len > 8
| eval chars = split(check_string, "")
| stats count AS total_chars, dc(chars) AS unique_chars by query, src_ip, check_string, check_len
| eval entropy_estimate = log(unique_chars, 2) * (unique_chars / check_len)
| where entropy_estimate > 3.5
| stats count AS high_entropy_queries, dc(query) AS unique_queries by src_ip
| where high_entropy_queries > 50
| sort - high_entropy_queries
Schritt 3: Anomales DNS-Abfragevolumen erkennen
Hosts identifizieren, die abnormalen DNS-Verkehr erzeugen:
index=dns sourcetype="stream:dns" earliest=-24h
| bin _time span=1h
| stats count AS queries, dc(query) AS unique_domains by src_ip, _time
| eventstats avg(queries) AS avg_queries, stdev(queries) AS stdev_queries by src_ip
| eval z_score = (queries - avg_queries) / stdev_queries
| where z_score > 3 OR queries > 5000
| sort - z_score
| table _time, src_ip, queries, unique_domains, avg_queries, z_score
Missbrauch von TXT-Records erkennen (häufige Tunneling-Methode):
index=dns sourcetype="stream:dns" query_type="TXT"
| stats count AS txt_queries, dc(query) AS unique_txt_domains,
values(query) AS domains by src_ip
| where txt_queries > 100
| eval suspicion = case(
txt_queries > 1000, "KRITISCH — Wahrscheinlich DNS-Tunneling",
txt_queries > 500, "HOCH — Mögliches DNS-Tunneling",
txt_queries > 100, "MITTEL — Ungewöhnliches TXT-Volumen"
)
| sort - txt_queries
| table src_ip, txt_queries, unique_txt_domains, suspicion
Schritt 4: Bekannte DNS-Tunneling-Tools erkennen
Nach Signaturen gängiger DNS-Tunneling-Tools suchen:
index=dns sourcetype="stream:dns"
| eval query_lower = lower(query)
| where (
match(query_lower, "\.dnscat\.") OR
match(query_lower, "\.dns2tcp\.") OR
match(query_lower, "\.iodine\.") OR
match(query_lower, "\.dnscapy\.") OR
match(query_lower, "\.cobalt.*\.beacon") OR
query_type="NULL" OR
(query_type="TXT" AND len(query) > 100)
)
| stats count by src_ip, query, query_type
| sort - count
DNS over HTTPS (DoH) erkennen, das lokales DNS umgeht:
index=proxy OR index=firewall
dest IN ("1.1.1.1", "1.0.0.1", "8.8.8.8", "8.8.4.4",
"9.9.9.9", "149.112.112.112", "208.67.222.222")
dest_port=443
| stats sum(bytes_out) AS total_bytes, count AS connections by src_ip, dest
| where connections > 100 OR total_bytes > 10485760
| eval alert = "Möglicher DoH-Bypass — DNS-Abfragen über HTTPS an öffentlichen Resolver gesendet"
| sort - total_bytes
Schritt 5: DNS-Ergebnisse mit Endpunktdaten korrelieren
Verdächtige DNS-Daten mit Prozessdaten abgleichen:
index=dns src_ip="192.168.1.105" query="*.evil-tunnel.com" earliest=-24h
| stats count AS dns_queries, earliest(_time) AS first_query, latest(_time) AS last_query
by src_ip, query
| join src_ip [
search index=sysmon EventCode=3 DestinationPort=53 Computer="WORKSTATION-042"
| stats count AS connections, values(Image) AS processes by SourceIp
| rename SourceIp AS src_ip
]
| table src_ip, query, dns_queries, first_query, last_query, processes
Schritt 6: Geschätztes Datenexfiltrationsvolumen berechnen
In DNS-Abfragen kodiertes Datenvolumen schätzen:
index=dns src_ip="192.168.1.105" query="*.evil-tunnel.com" earliest=-24h
| eval domain_parts = split(query, ".")
| eval encoded_data = mvindex(domain_parts, 0)
| eval encoded_bytes = len(encoded_data)
| eval decoded_bytes = encoded_bytes * 0.75 -- Base64-Decodierungsfaktor
| stats sum(decoded_bytes) AS total_bytes_estimated, count AS total_queries,
earliest(_time) AS first_seen, latest(_time) AS last_seen
| eval estimated_kb = round(total_bytes_estimated / 1024, 1)
| eval estimated_mb = round(total_bytes_estimated / 1048576, 2)
| eval duration_hours = round((last_seen - first_seen) / 3600, 1)
| eval rate_kbps = round(estimated_kb / (duration_hours * 3600) * 8, 2)
| table total_queries, estimated_mb, duration_hours, rate_kbps, first_seen, last_seen
Schlüsselkonzepte
| Begriff | Definition |
|---|---|
| DNS-Tunneling | Technik, die Daten in DNS-Abfragen/-Antworten kodiert, um Daten zu exfiltrieren oder C2-Kanäle über DNS aufzubauen |
| DGA | Domain Generation Algorithm – Malware-Technik zur Erzeugung pseudozufälliger Domainnamen für C2-Resilienz |
| Shannon-Entropie | Mathematisches Maß für die Zufälligkeit einer Zeichenkette – hohe Entropie (>3,5) bei Domainnamen deutet auf DGA oder Tunneling hin |
| TXT-Record-Missbrauch | Nutzung von DNS-TXT-Records (eigentlich für Textdaten vorgesehen) als hochbandbreitiger Kanal für Daten-Tunneling |
| DNS over HTTPS (DoH) | DNS-Abfragen, die über HTTPS (Port 443) verschlüsselt werden und damit die traditionelle DNS-Überwachung umgehen |
| Passives DNS | Historische Aufzeichnung von DNS-Auflösungen, die zeigt, zu welchen IPs eine Domain im Laufe der Zeit aufgelöst wurde |
Tools und Systeme
- Splunk Stream: Netzwerkverkehrs-Erfassungs-Add-on, das geparste DNS-Abfragedaten für die SIEM-Analyse bereitstellt
- Zeek (Bro): Netzwerksicherheitsmonitor, der detaillierte DNS-Transaktionsprotokolle zur Analyse erzeugt
- Cisco Umbrella (OpenDNS): Cloud-DNS-Sicherheitsplattform, die bösartige Domains blockiert und Abfragedaten protokolliert
- Infoblox DNS Firewall: DNS-Schicht-Sicherheit, die RPZ-basierte Blockierung und detaillierte Abfrageprotokollierung bietet
- Farsight DNSDB: Passive DNS-Datenbank für historische Domainauflösungs-Abfragen und Infrastruktur-Mapping
Häufige Szenarien
- Cobalt Strike DNS Beacon: Erkennung von periodischen TXT-Abfragen mit kodierten Payloads an C2-Domain
- Datenexfiltration: Große Mengen einzigartiger Subdomain-Abfragen, die gestohlene Daten in Base64/Hex kodieren
- DGA-Malware: Erkennung von DNS-Abfragen an algorithmisch generierte Domains (hohe Entropie, kein Webinhalt)
- DNS-over-HTTPS-Bypass: Mitarbeiter nutzt DoH, um unternehmenseigene DNS-Filterung und -Überwachung zu umgehen
- Slow-Drip-Exfiltration: DNS-Tunneling mit geringem Volumen, das unter den Schwellwert-Alarmen bleibt (erfordert Basislinienvergleich)
Ausgabeformat
DNS-EXFILTRATIONSANALYSE — WORKSTATION-042
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Zeitraum: 2024-03-14 bis 2024-03-15
Quelle: 192.168.1.105 (WORKSTATION-042, Abteilung Finanzen)
Ergebnisse:
[KRITISCH] DNS-Tunneling zu evil-tunnel[.]com erkannt
Abfragevolumen: 12.847 Abfragen in 18 Stunden
Durchschnittliche Subdomain-Länge: 63 Zeichen (normal: <20)
Durchschnittliche Entropie: 3,82 (Schwellwert: 3,5)
Abfragetypen: TXT (89%), A (11%)
Geschätzte Daten: ~4,7 MB über DNS exfiltriert
Rate: 0,58 kbps (Slow-Drip-Muster)
[HOCH] DGA-ähnliche Domains aufgelöst
Einzigartige DGA-Domains: 247 Domains aufgelöst
Muster: 15-stelliges zufälliges Alphanumerikum.xyz TLD
Entropiebereich: 3,6 - 4,1
Prozesszuordnung:
Prozess: svchost_update.exe (Tarnung – kein legitimer svchost)
PID: 4892
Eltern: explorer.exe
Hash: SHA256: a1b2c3d4... (VT: 34/72 bösartig – Cobalt Strike Beacon)
Eindämmung:
[ERLEDIGT] Host per EDR isoliert
[ERLEDIGT] Domain evil-tunnel[.]com zur DNS-Sinkhole hinzugefügt
[ERLEDIGT] Vorfall IR-2024-0448 erstellt