Creare query in più fasi in YARA-L

Supportato in:

Questo documento descrive come le query in più fasi in YARA-L consentono di inserire l'output di una fase della query direttamente nell'input di una fase successiva. Questo processo ti offre un maggiore controllo sulla trasformazione dei dati rispetto a una singola query monolitica.

Integrare le query in più fasi con le funzionalità esistenti

Le query in più fasi funzionano in combinazione con le seguenti funzionalità esistenti in Google Security Operations:

  • Regole di rilevamento composito: le query in più fasi integrano le regole di rilevamento composito. A differenza delle regole composite, le query in più fasi che utilizzano la ricerca possono restituire risultati in tempo reale.

  • Intervalli di tempo e regole multi-evento:puoi utilizzare query in più fasi per rilevare anomalie confrontando diverse finestre temporali all'interno dei tuoi dati. Ad esempio, puoi utilizzare le fasi iniziali della query per stabilire una base di riferimento in un periodo di tempo prolungato e poi utilizzare una fase successiva per valutare l'attività recente rispetto a questa base di riferimento. Puoi anche utilizzare le regole multi-evento per creare un tipo di confronto simile.

Le query in più fasi in YARA-L sono supportate sia in Dashboard sia in Ricerca.

Le unioni aiutano a correlare i dati provenienti da più fonti per fornire un contesto più ampio per un'indagine. Collegando eventi, entità e altri dati correlati, puoi analizzare scenari di attacco complessi. Per saperne di più, consulta Utilizzare i join nella ricerca.

Definisci la sintassi YARA-L in più fasi

Quando configuri una query in più fasi, tieni presente quanto segue:

  • Fase di limite: le query in più fasi devono contenere da 1 a 4 fasi denominate, oltre alla fase radice.
  • Sintassi dell'ordine: definisci sempre la sintassi della fase denominata prima di definire la sintassi della fase radice.

Creare una query YARA-L in più fasi

Per creare una query YARA-L in più fasi, completa i seguenti passaggi.

Struttura e sintassi delle fasi

Vai a Indagine > Ricerca. Segui questi requisiti strutturali quando definisci le fasi della query:

Sintassi: utilizza la seguente sintassi per denominare ogni fase e separarla dalle altre fasi:

stage <stage name> { }

  • Parentesi graffe: inserisci tutta la sintassi dello stage tra parentesi graffe {}.

  • Order: definisci la sintassi per tutte le fasi denominate prima di definire la fase radice.

  • Riferimento: ogni fase può fare riferimento alle fasi definite in precedenza nella query.

  • Fase principale: una query deve avere una fase principale, che viene elaborata dopo tutte le fasi denominate.

La seguente fase di esempio, daily_stats, raccoglie le statistiche di rete giornaliere:

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)
}

Output della fase di accesso

L'output di una fase denominata è accessibile alle fasi successive utilizzando i campi della fase. I campi della fase corrispondono alle variabili match e outcome della fase e possono essere utilizzati in modo simile ai campi Unified Data Model (UDM).

Utilizza la seguente sintassi per accedere a un campo di fase:

$<stage name>.<variable name>

Timestamp della finestra di accesso (facoltativo)

Se una fase denominata utilizza una finestra di salto, scorrevole o a cascata, accedi all'inizio e alla fine della finestra per ogni riga di output utilizzando questi campi riservati:

  • $<stage name>.window_start

  • $<stage name>.window_end

window_start e window_end sono campi interi espressi in secondi dall'epoca Unix. Le finestre nelle diverse fasi possono variare di dimensioni.

Limitazioni

Le query in più fasi presentano i seguenti vincoli funzionali e strutturali:

Limiti strutturali e di palcoscenico

  • Fase principale: è consentita una sola fase principale per query.

  • Fasi denominate: sono supportate al massimo quattro fasi denominate.

  • Riferimento alle fasi: una fase può fare riferimento solo alle fasi definite logicamente prima di essa nella stessa query.

  • Join: sono consentiti un massimo di quattro join non di tabelle di dati in tutte le fasi.

  • Requisito di risultato: ogni fase denominata (esclusa la fase principale) deve includere una sezione match o una sezione outcome. La sezione outcome non richiede l'aggregazione.

Limiti di finestre e compatibilità

  • Supporto delle funzionalità: le query in più fasi sono supportate in Ricerca e Dashboard, ma non in Regole.

  • Tipi di finestre: evita di combinare diversi tipi di finestre in una singola query.

  • Dipendenza dalla finestra: una fase che utilizza una finestra di salto o scorrevole non può dipendere da un'altra fase che utilizza anche una finestra di salto o scorrevole.

  • Dimensione della finestra temporale: mentre le finestre temporali in fasi diverse possono variare di dimensioni, la differenza di dimensioni deve essere inferiore a 720x.

Esempio: Differenza di aggregazione dello stato

La seguente configurazione della finestra di esempio non è consentita:

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

Se la fase monthly_stats aggrega i dati per mese e la fase principale aggrega l'output di monthly_stats per minuto, ogni riga di monthly_stats corrisponde a 43.200 righe nella fase principale (perché ci sono 43.200 minuti in un mese).

Limitazioni di fase e query

Ogni singola fase di una query in più fasi presenta i seguenti vincoli:

  • La maggior parte delle limitazioni che si applicano a una query a una sola fase si applicano anche a ogni singola fase:

    • Requisito di output: ogni fase deve restituire almeno una variabile di corrispondenza o risultato (campo della fase).

    • Finestra nel join: la dimensione massima della finestra (hop, tumbling o scorrevole) utilizzata in un join è di 2 giorni.

    • Numero massimo di variabili di risultato:

      • 20 per i clienti che non hanno attivato l'opzione per consentire un limite più elevato per la variabile di risultato

      • 50 per i clienti che hanno attivato l'opzione per consentire un limite più elevato per la variabile di risultato

    • Dimensione minima e massima di una finestra di salto.

    • Numero massimo di elementi in una variabile di risultato con valori di array.

  • Le query in più fasi sono soggette agli stessi limiti delle query statistiche:

    • Query statistiche: 120 QPH (API e UI)

    • Visualizzazioni di ricerca da Google SecOps: 100 visualizzazioni al minuto

    • I join in più fasi sono supportati nell'interfaccia utente e nell'API EventService.UDMSearch, ma non nell'API SearchService.UDMSearch. Anche le query in più fasi senza join sono supportate nell'interfaccia utente.

Limitazioni relative agli eventi e globali

Numero massimo di eventi:

Il numero di eventi che possono essere elaborati contemporaneamente dalle query in più fasi è strettamente limitato:

  • Eventi UDM: sono consentiti al massimo 2 eventi UDM.

  • Eventi Entity Context Graph (ECG): è consentito un massimo di un evento ECG.

Limitazioni delle query globali:

Questi limiti sono vincoli a livello di piattaforma che controllano quanto indietro nel tempo e quanti dati può restituire una query in più fasi.

  • Per un intervallo di tempo della query, l'intervallo di tempo massimo per una query standard è di 30 giorni.

  • La dimensione massima del set di risultati totale è di 10.000 risultati.

Esempi di query multistadio

Gli esempi in questa sezione illustrano come creare una query YARA-L multistadio completa.

Esempio: cerca connessioni di rete insolitamente attive (ore)

Questo esempio YARA-L in più fasi identifica coppie di indirizzi IP con un'attività di rete superiore al normale, prendendo di mira le coppie che mantengono un'attività elevata per più di tre ore. La query include due componenti obbligatori: lo stage denominato hourly_stats e lo stage root.

La fase hourly_stats cerca coppie principal.ip e target.ip con livelli elevati di attività di rete.

Questa fase restituisce un singolo valore orario per i seguenti campi:

  • Statistiche per l'IP di origine (stringa): $hourly_stats.src_ip

  • Statistiche per l'IP di destinazione (stringa): $hourly_stats.dst_ip

  • Statistiche per il conteggio degli eventi (numero intero): $hourly_stats.count

  • Deviazione standard dei byte ricevuti (float): $hourly_stats.std_recd_bytes

  • Byte medi ricevuti (float): $hourly_stats.avg_recd_bytes

  • Ora di inizio del bucket orario in secondi a partire dal tempo Unix (numero intero): $hourly_stats.window_start

  • Ora di fine del bucket in secondi a partire dall'epoca Unix (numero intero): $hourly_stats.window_end

La fase principale elabora l'output della fase hourly_stats. Calcola le statistiche per le coppie principal.ip e target.ip con attività che superano la soglia specificata da $hourly_stats. A questo punto, filtra le coppie con più di tre ore di attività elevata.


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

Se modifichi la condizione di corrispondenza nella fase principale come segue, puoi introdurre un'aggregazione in finestra per giorno per la query in più fasi.

match:
 $src_ip, $dst_ip by day

Esempio: cerca connessioni di rete insolitamente attive (utilizzando lo Z-score)

Questa query in più fasi confronta l'attività di rete media giornaliera con l'attività odierna utilizzando un calcolo del punteggio Z (che misura il numero di deviazioni standard dalla media). Questa query cerca in modo efficace attività di rete insolitamente elevata tra asset interni e sistemi esterni.

Prerequisito: la finestra temporale della query deve essere maggiore o uguale a 2 giorni e includere il giorno corrente affinché lo Z-score calcolato sia efficace.

Questa query in più fasi include la fase daily_stats e la fase root, che lavorano insieme per calcolare lo Z-score per l'attività di rete:

  • La fase daily_stats esegue l'aggregazione giornaliera iniziale. Calcola il totale dei byte scambiati ogni giorno per ogni coppia di IP (source e target) e restituisce i seguenti campi di fase (corrispondenti alle colonne nelle righe di output):

    • $daily_stats.source: singolare, stringa
    • $daily_stats.target: singolare, stringa
    • $daily_stats.exchanged_bytes: singolare, numero intero
    • $daily_stats.window_start: singolare, numero intero
    • $daily_stats.window_end: singolare, numero intero
  • La fase principale aggrega l'output della fase daily_stats per ogni coppia di IP. Calcola la media e la deviazione standard dei byte giornalieri scambiati nell'intero intervallo di ricerca, insieme ai byte scambiati oggi. Utilizza questi tre valori calcolati per determinare lo Z-score.

  • L'output elenca gli Z-score per tutte le coppie di IP di oggi, ordinati in ordine decrescente.

// 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

Esportare variabili non aggregate dalle fasi

Le fasi denominate possono includere una sezione outcome non aggregata. Ciò significa che le variabili definite all'interno della sezione outcome vengono generate direttamente dalla fase, consentendo alle fasi successive di accedervi come campi della fase senza richiedere un'aggregazione raggruppata.

Esempio: esportare una variabile non aggregata

Questo esempio mostra come esportare le variabili non aggregate. Tieni presente la seguente logica:

  • top_5_bytes_sent esegue la ricerca di staging per i cinque eventi con la maggiore attività di rete.

  • La fase top_5_bytes_sent restituisce i seguenti campi della fase corrispondenti alle colonne nelle righe di output:

    • $top_5_bytes_sent.bytes_sent: singolare, numero intero
    • $top_5_bytes_sent.timestamp_seconds: singolare, numero intero
  • La fase root calcola i timestamp più recenti e meno recenti per i cinque eventi con l'attività di rete più elevata.

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))

Implementa il windowing nelle query in più fasi

Le query in più fasi supportano tutti i tipi di finestre (hop, scorrevole e sequenziale) nelle fasi denominate. Se una fase denominata include una finestra, l'inizio e la fine della finestra per ogni riga di output sono accessibili utilizzando i seguenti campi riservati:

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

Esempio: finestra di hop

L'esempio seguente mostra come potresti utilizzare le finestre di hop in una query in più fasi:

  • hourly_stats esegue ricerche di coppie di IP con un'attività di rete elevata nella stessa ora.

  • hourly_stats restituisce i seguenti campi di fase corrispondenti alle colonne nelle righe di output:

    • $hourly_stats.src_ip: singolare, stringa
    • $hourly_stats.dst_ip: singolare, stringa
    • $hourly_stats.count: singolare, numero intero
    • $hourly_stats.std_recd_bytes: singolare, float
    • $hourly_stats.avg_recd_bytes: singolare, float
    • $hourly_stats.window_start: singolare, numero intero
    • $hourly_stats.window_end: singolare, numero intero
  • Il filtro della fase radice esclude le coppie di IP con più di 3 ore di attività elevata. Le ore potrebbero sovrapporsi a causa dell'utilizzo di una finestra di hop nella fase hourly_stats.

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

Problemi noti

Ti consigliamo di esaminare le seguenti limitazioni e soluzioni alternative consigliate quando implementi query in più fasi:

  • Tutte le query in più fasi si comportano come le query di ricerca delle statistiche (l'output è costituito da statistiche aggregate anziché da eventi o righe di tabelle di dati non aggregati).

  • Le prestazioni dei join con UDM e gli eventi entità su un lato possono registrare prestazioni basse a causa delle dimensioni del set di dati. Ti consigliamo vivamente di filtrare gli eventi UDM e delle entità del lato di unione il più possibile (ad esempio, filtrare in base al tipo di evento).

Per indicazioni generali sulle pratiche consigliate, consulta le best practice di Yara-L e per informazioni specifiche sui join, consulta le best practice.

Hai bisogno di ulteriore assistenza? Ricevi risposte dai membri della community e dai professionisti di Google SecOps.