Mehrstufige Abfragen in YARA-L erstellen

Unterstützt in:

In diesem Dokument wird beschrieben, wie Sie mit mehrstufigen Abfragen in YARA-L die Ausgabe einer Abfragephase direkt in die Eingabe einer nachfolgenden Phase einfügen können. Dieser Prozess bietet Ihnen mehr Kontrolle über die Datentransformation als eine einzelne, monolithische Abfrage.

Mehrstufige Anfragen in bestehende Funktionen einbinden

Mehrphasenabfragen funktionieren in Verbindung mit den folgenden vorhandenen Funktionen in Google Security Operations:

  • Regeln für zusammengesetzte Erkennung: Mehrstufige Abfragen ergänzen Regeln für zusammengesetzte Erkennung. Im Gegensatz zu zusammengesetzten Regeln können mehrstufige Abfragen, bei denen die Suche verwendet wird, Ergebnisse in Echtzeit zurückgeben.

  • Zeiträume und Regeln für mehrere Ereignisse:Mit mehrstufigen Abfragen können Sie Anomalien erkennen, indem Sie verschiedene Zeiträume in Ihren Daten vergleichen. Sie können beispielsweise mit den ersten Abfragephasen eine Baseline über einen längeren Zeitraum erstellen und dann mit einer späteren Phase die aktuelle Aktivität anhand dieser Baseline bewerten. Sie können auch Regeln mit mehreren Ereignissen verwenden, um einen ähnlichen Vergleich zu erstellen.

Mehrstufige Abfragen in YARA-L werden sowohl in Dashboards als auch in Search unterstützt.

Mit Joins lassen sich Daten aus mehreren Quellen in Beziehung setzen, um mehr Kontext für eine Untersuchung zu erhalten. Durch die Verknüpfung von zugehörigen Ereignissen, Entitäten und anderen Daten können Sie komplexe Angriffsszenarien untersuchen. Weitere Informationen finden Sie unter Joins in Search verwenden.

Mehrstufige YARA-L-Syntax definieren

Beachten Sie bei der Konfiguration einer mehrstufigen Abfrage Folgendes:

  • Phase mit Limit: Mehrphasenabfragen müssen zusätzlich zur Root-Phase zwischen 1 und 4 benannte Phasen enthalten.
  • Syntax für die Reihenfolge: Definieren Sie immer zuerst die Syntax für die benannte Phase und dann die Syntax für die Root-Phase.

Mehrstufige YARA-L-Abfrage erstellen

So erstellen Sie eine mehrstufige YARA-L-Abfrage:

Phasenstruktur und ‑syntax

Klicken Sie auf Prüfung > Suche. Beachten Sie beim Definieren Ihrer Abfragephasen die folgenden strukturellen Anforderungen:

Syntax: Verwenden Sie die folgende Syntax, um die einzelnen Phasen zu benennen und von anderen Phasen zu trennen:

stage <stage name> { }

  • Geschweifte Klammern: Setzen Sie die gesamte Phasen-Syntax in geschweifte Klammern {}.

  • Reihenfolge: Definieren Sie die Syntax für alle benannten Phasen, bevor Sie die Root-Phase definieren.

  • Verweise: In jeder Phase kann auf Phasen verwiesen werden, die zuvor in der Abfrage definiert wurden.

  • Stammphase: Eine Abfrage muss eine Stammphase haben, die nach allen benannten Phasen verarbeitet wird.

In der folgenden Beispielphase daily_stats werden tägliche Netzwerkstatistiken erfasst:

stage daily_stats {
  metadata.event_type = "NETWORK_CONNECTION"
  $source = principal.hostname
  $target = target.ip
  $source != ""
  $target != ""
  $total_bytes = cast.as_int(network.sent_bytes + network.received_bytes)
  match:
    $source, $target by day
  outcome:
    $exchanged_bytes = sum($total_bytes)
}

Ausgabe der Zugriffsphase

Die Ausgabe einer benannten Phase ist für nachfolgende Phasen über Phasenfelder zugänglich. Die Staging-Felder entsprechen den Variablen match und outcome der Phase und können ähnlich wie Felder des einheitlichen Datenmodells (Unified Data Model, UDM) verwendet werden.

Verwenden Sie die folgende Syntax, um auf ein Phasenfeld zuzugreifen:

$<stage name>.<variable name>

Zeitstempel für den Zugriff (optional)

Wenn in einer benannten Phase ein Hop-, gleitendes oder rollierendes Fenster verwendet wird, können Sie mit diesen reservierten Feldern auf den Fensteranfang und das Fensterende für jede Ausgaberow zugreifen:

  • $<stage name>.window_start

  • $<stage name>.window_end

window_start und window_end sind Ganzzahlfelder, die in Sekunden seit der Unix-Epoche angegeben werden. Fenster in verschiedenen Phasen können unterschiedlich groß sein.

Beschränkungen

Für mehrstufige Abfragen gelten die folgenden funktionalen und strukturellen Einschränkungen:

Strukturelle und phasenbezogene Limits

  • Root-Phase: Pro Abfrage ist nur eine Root-Phase zulässig.

  • Benannte Phasen: Es werden maximal vier benannte Phasen unterstützt.

  • Verweise auf Phasen: Eine Phase kann nur auf Phasen verweisen, die in derselben Abfrage logisch vor ihr definiert sind.

  • Joins: Über alle Phasen hinweg sind maximal vier Joins ohne Datentabelle zulässig.

  • Ergebnisvoraussetzung: Jede benannte Phase (mit Ausnahme der Stammphase) muss entweder einen match- oder einen outcome-Abschnitt enthalten. Für den Abschnitt outcome ist keine Aggregation erforderlich.

Zeitfenster- und Kompatibilitätslimits

  • Unterstützung von Funktionen: Mehrstufige Abfragen werden in Search und Dashboards unterstützt, nicht aber in Rules.

  • Fenstertypen: Mischen Sie nicht verschiedene Fenstertypen in einer einzelnen Abfrage.

  • Fensterabhängigkeit: Eine Phase mit einem Hop- oder gleitenden Fenster kann nicht von einer anderen Phase abhängen, die ebenfalls ein Hop- oder gleitendes Fenster verwendet.

  • Größe des gleitenden Fensters: Die Größe der gleitenden Fenster in den verschiedenen Phasen kann variieren, der Unterschied darf jedoch nicht mehr als 720-fach betragen.

Beispiel: Differenz bei der Aggregation von Phasen

Die folgende Beispielkonfiguration für das Fenster ist nicht zulässig:

stage monthly_stats {
  metadata.event_type = "NETWORK_CONNECTION"
    $source = principal.hostname
    $target = target.ip
    $source != ""
    $target != ""
    $total_bytes = cast.as_int(network.sent_bytes + network.received_bytes)

  match:
    $source, $target by month

  outcome:
    $exchanged_bytes = sum($total_bytes)
}

$source = $monthly_stats.source
$target = $monthly_stats.target

match:
    $source, $target by minute

Wenn in der Phase monthly_stats Daten nach Monat zusammengefasst werden und in der Root-Phase die Ausgabe von monthly_stats nach Minute zusammengefasst wird, entspricht jede Zeile aus monthly_stats 43.200 Zeilen in der Root-Phase (da ein Monat 43.200 Minuten hat).

Beschränkungen für Phasen und Abfragen

Für jede einzelne Phase einer mehrphasigen Abfrage gelten die folgenden Einschränkungen:

  • Die meisten Einschränkungen, die für eine einphasige Abfrage gelten, gelten auch für jede einzelne Phase:

  • Für mehrstufige Abfragen gelten dieselben Einschränkungen wie für Statistikabfragen:

    • Statistikabfragen: 120 Abfragen pro Stunde (API und Benutzeroberfläche)

    • Suchansichten aus Google SecOps: 100 Aufrufe pro Minute

    • Mehrstufige Joins werden in der Benutzeroberfläche und in der EventService.UDMSearch API unterstützt, nicht aber in der SearchService.UDMSearch API. Mehrstufige Abfragen ohne Joins werden auch in der Benutzeroberfläche unterstützt.

Einschränkungen für Ereignisse und global

Maximale Anzahl von Ereignissen:

Die Anzahl der Ereignisse, die gleichzeitig von mehrstufigen Abfragen verarbeitet werden können, ist streng begrenzt:

  • UDM-Ereignisse: Es sind maximal zwei UDM-Ereignisse zulässig.

  • ECG-Ereignisse (Entity Context Graph): Es ist maximal ein ECG-Ereignis zulässig.

Globale Abfragebeschränkungen:

Diese Grenzwerte sind plattformweite Einschränkungen, die festlegen, wie weit zurückliegende Daten und wie viele Daten eine mehrstufige Abfrage zurückgeben kann.

  • Der maximale Zeitraum für eine Standardabfrage beträgt 30 Tage.

  • Die maximale Gesamtgröße des Ergebnissatzes beträgt 10.000 Ergebnisse.

Beispiele für mehrstufige Abfragen

Die Beispiele in diesem Abschnitt veranschaulichen, wie Sie eine vollständige mehrstufige YARA-L-Abfrage erstellen können.

Beispiel: Suche nach ungewöhnlich aktiven Netzwerkverbindungen (Stunden)

In diesem mehrstufigen YARA-L-Beispiel werden IP-Adressenpaare mit überdurchschnittlicher Netzwerkaktivität identifiziert. Dabei werden Paare berücksichtigt, die über einen Zeitraum von mehr als drei Stunden eine hohe Aktivität aufweisen. Die Abfrage enthält zwei erforderliche Komponenten: die benannte Phase hourly_stats und die Phase root.

In der hourly_stats-Phase wird nach principal.ip- und target.ip-Paaren mit hoher Netzwerkaktivität gesucht.

In dieser Phase wird ein einzelner stündlicher Wert für die folgenden Felder zurückgegeben:

  • Statistiken für die Quell-IP-Adresse (String): $hourly_stats.src_ip

  • Statistiken für die Ziel-IP-Adresse (String): $hourly_stats.dst_ip

  • Statistiken für die Anzahl der Ereignisse (Ganzzahl): $hourly_stats.count

  • Standardabweichung der empfangenen Byte (Gleitkommazahl): $hourly_stats.std_recd_bytes

  • Durchschnittliche empfangene Byte (Gleitkommazahl): $hourly_stats.avg_recd_bytes

  • Startzeit des Stundenbereichs in Sekunden seit der Unix-Epoche (Ganzzahl): $hourly_stats.window_start

  • Endzeit des Stundenbereichs in Sekunden seit der Unix-Epoche (Ganzzahl): $hourly_stats.window_end

In der Root-Phase wird die Ausgabe der Phase hourly_stats verarbeitet. Es werden Statistiken für principal.ip- und target.ip-Paare mit Aktivitäten berechnet, die den von $hourly_stats angegebenen Grenzwert überschreiten. Anschließend wird nach Paaren mit mehr als drei Stunden hoher Aktivität gefiltert.


stage hourly_stats {
  metadata.event_type = "NETWORK_CONNECTION"
  $src_ip = principal.ip
  $dst_ip = target.ip
  $src_ip != ""
  $dst_ip != ""

  match:
    $src_ip, $dst_ip by hour

  outcome:
    $count = count(metadata.id)
    $avg_recd_bytes = avg(network.received_bytes)
    $std_recd_bytes = stddev(network.received_bytes)

  condition:
    $avg_recd_bytes > 100 and $std_recd_bytes > 50
}

$src_ip = $hourly_stats.src_ip
$dst_ip = $hourly_stats.dst_ip
$time_bucket_count = strings.concat(timestamp.get_timestamp($hourly_stats.window_start), "|", $hourly_stats.count)

match:
 $src_ip, $dst_ip

outcome:
 $list = array_distinct($time_bucket_count)
 $count = count_distinct($hourly_stats.window_start)

condition:
 $count > 3

Wenn Sie die Abgleichsbedingung in der Root-Phase wie folgt ändern, können Sie eine fensterbasierte Aggregation nach Tag für die mehrstufige Abfrage einführen.

match:
 $src_ip, $dst_ip by day

Beispiel: Suche nach ungewöhnlich aktiven Netzwerkverbindungen (mit Z-Score)

Bei dieser mehrstufigen Abfrage wird die durchschnittliche tägliche Netzwerkaktivität mit der heutigen Aktivität verglichen. Dazu wird ein Z-Score berechnet, der die Anzahl der Standardabweichungen vom Mittelwert angibt. Mit dieser Abfrage wird effektiv nach ungewöhnlich hoher Netzwerkaktivität zwischen internen Assets und externen Systemen gesucht.

Voraussetzung: Der Abfragezeitraum muss mindestens zwei Tage umfassen und den aktuellen Tag einschließen, damit der berechnete Z-Score wirksam ist.

Diese mehrstufige Abfrage umfasst die Phase daily_stats und die Phase root, die zusammen den Z-Score für die Netzwerkaktivität berechnen:

  • In der Phase daily_stats wird die erste tägliche Zusammenfassung durchgeführt. Dabei werden die insgesamt ausgetauschten Byte für jedes IP-Paar (source und target) berechnet und die folgenden Phasenfelder zurückgegeben (entsprechend den Spalten in den Ausgaberow):

    • $daily_stats.source: Singular, String
    • $daily_stats.target: Singular, String
    • $daily_stats.exchanged_bytes: Singular, Ganzzahl
    • $daily_stats.window_start: Singular, Ganzzahl
    • $daily_stats.window_end: Singular, Ganzzahl
  • In der Root-Phase wird die Ausgabe der daily_stats-Phase für jedes IP-Paar zusammengefasst. Es werden der Durchschnitt und die Standardabweichung der täglich ausgetauschten Bytes für den gesamten Suchbereich sowie die heute ausgetauschten Bytes berechnet. Anhand dieser drei berechneten Werte wird der Z-Score ermittelt.

  • In der Ausgabe werden die Z-Scores für alle IP-Paare des aktuellen Tages in absteigender Reihenfolge aufgeführt.

// Calculate the total bytes exchanged per day by source and target

stage daily_stats {
  metadata.event_type = "NETWORK_CONNECTION"
  $source = principal.hostname
  $target = target.ip
  $source != ""
  $target != ""
  $total_bytes = cast.as_int(network.sent_bytes + network.received_bytes)
  match:
    $source, $target by day
  outcome:
    $exchanged_bytes = sum($total_bytes)
}

// Calculate the average per day over the time window and compare with the bytes
   exchanged today

$source = $daily_stats.source
$target = $daily_stats.target
$date = timestamp.get_date($daily_stats.window_start)

match:
  $source, $target

outcome:
  $today_bytes = sum(if($date = timestamp.get_date(timestamp.current_seconds()), $daily_stats.exchanged_bytes, 0))
  $average_bytes = window.avg($daily_stats.exchanged_bytes)
  $stddev_bytes = window.stddev($daily_stats.exchanged_bytes)
  $zscore = ($today_bytes - $average_bytes) / $stddev_bytes

order:
  $zscore desc

Nicht aggregierte Variablen aus Phasen exportieren

Benannte Phasen können einen nicht aggregierten outcome-Abschnitt enthalten. Das bedeutet, dass Variablen, die in diesem outcome-Abschnitt definiert sind, direkt aus der Phase ausgegeben werden. Nachfolgende Phasen können ohne gruppierte Aggregation als Phasenfelder darauf zugreifen.

Beispiel: Nicht aggregierte Variable exportieren

In diesem Beispiel wird gezeigt, wie nicht aggregierte Variablen exportiert werden. Beachten Sie die folgende Logik:

  • In der top_5_bytes_sent-Phase wird nach den fünf Ereignissen mit der höchsten Netzwerkaktivität gesucht.

  • In der top_5_bytes_sent-Phase werden die folgenden Phasenfelder ausgegeben, die den Spalten in den Ausgaberow entsprechen:

    • $top_5_bytes_sent.bytes_sent: Singular, Ganzzahl
    • $top_5_bytes_sent.timestamp_seconds: Singular, Ganzzahl
  • In der Phase root werden die neuesten und frühesten Zeitstempel für die fünf Ereignisse mit der höchsten Netzwerkaktivität berechnet.

stage top_5_bytes_sent {
  metadata.event_type = "NETWORK_CONNECTION"
  network.sent_bytes > 0

  outcome:
    $bytes_sent = cast.as_int(network.sent_bytes)
    $timestamp_seconds = metadata.event_timestamp.seconds

  order:
    $bytes_sent desc 
  
  limit:
    5
}

outcome:
  $latest_timestamp = timestamp.get_timestamp(max($top_5_bytes_sent.timestamp_seconds))
  $earliest_timestamp = timestamp.get_timestamp(min($top_5_bytes_sent.timestamp_seconds))

Fensterfunktionen in mehrstufigen Abfragen implementieren

Mehrphasenabfragen unterstützen alle Arten von Zeitfenstern (Hop, Sliding und Tumbling) in benannten Phasen. Wenn eine benannte Phase ein Fenster enthält, sind der Fensterstart und das Fensterende für jede Ausgaberow über die folgenden reservierten Felder verfügbar:

  • $<stage name>.window_start
  • $<stage name>.window_end

Beispiel: Hop-Window

Das folgende Beispiel veranschaulicht, wie Sie Hop-Fenster in einer mehrstufigen Abfrage verwenden können:

  • In der hourly_stats-Phase wird nach IP-Paaren gesucht, die innerhalb derselben Stunde eine hohe Netzwerkaktivität aufweisen.

  • hourly_stats gibt die folgenden Phasenfelder aus, die den Spalten in den Ausgabezeilen entsprechen:

    • $hourly_stats.src_ip: Singular, String
    • $hourly_stats.dst_ip: Singular, String
    • $hourly_stats.count: Singular, Ganzzahl
    • $hourly_stats.std_recd_bytes: Singular, Gleitkommazahl
    • $hourly_stats.avg_recd_bytes: Singular, Gleitkommazahl
    • $hourly_stats.window_start: Singular, Ganzzahl
    • $hourly_stats.window_end: Singular, Ganzzahl
  • Im Root-Abschnitt werden IP-Paare mit mehr als 3 Stunden hoher Aktivität herausgefiltert. Die Stunden können sich überschneiden, wenn in der hourly_stats-Phase ein Hop-Fenster verwendet wird.

stage hourly_stats {
  metadata.event_type = "NETWORK_CONNECTION"
  $src_ip = principal.ip
  $dst_ip = target.ip
  $src_ip != ""
  $dst_ip != ""

  match:
    $src_ip, $dst_ip over 1h

  outcome:
    $count = count(metadata.id)
    $avg_recd_bytes = avg(network.received_bytes)
    $std_recd_bytes = stddev(network.received_bytes)

  condition:
    $avg_recd_bytes > 100 and $std_recd_bytes > 50
}

$src_ip = $hourly_stats.src_ip
$dst_ip = $hourly_stats.dst_ip
$time_bucket_count = strings.concat(timestamp.get_timestamp($hourly_stats.window_start), "|", $hourly_stats.count)

match:
 $src_ip, $dst_ip

outcome:
 $list = array_distinct($time_bucket_count)
 $count = count_distinct($hourly_stats.window_start)

condition:
 $count > 3

Bekannte Probleme

Wir empfehlen, die folgenden Einschränkungen und empfohlenen Problemumgehungen bei der Implementierung von mehrstufigen Abfragen zu berücksichtigen:

  • Alle Abfragen mit mehreren Phasen verhalten sich wie Statistik-Suchanfragen. Die Ausgabe besteht aus aggregierten Statistiken und nicht aus nicht aggregierten Ereignissen oder Datentabellenzeilen.

  • Die Leistung von Joins mit UDM- und Entitätsereignissen auf einer Seite kann aufgrund der Größe des Datensatzes gering sein. Wir empfehlen dringend, die UDM- und Entitätsereignisse auf der Join-Seite so weit wie möglich zu filtern (z. B. nach Ereignistyp).

Allgemeine Informationen zu empfohlenen Vorgehensweisen finden Sie unter Yara-L-Best Practices. Informationen speziell zu Joins finden Sie unter Best Practices.

Benötigen Sie weitere Hilfe? Antworten von Community-Mitgliedern und Google SecOps-Experten erhalten