Problemi noti di YARA-L 2.0
Questo documento è rivolto ai detection engineer che vogliono eseguire il debug della logica delle regole e ottimizzare l'esecuzione di YARA-L 2.0. Spiega come gestire i comportamenti non standard del motore, come l'annullamento del nidificazione dei campi, l'espansione del prodotto cartesiano nelle aggregazioni e la coerenza finale dell'arricchimento. Seguendo questi metodi, puoi evitare errori logici che portano a valori dei risultati gonfiati o a mancate rilevazioni.
YARA-L 2.0 utilizza un modello di esecuzione che espande i campi ripetuti in singole righe di eventi durante la valutazione. Poiché questa trasformazione avviene a livello di motore, il riferimento a più campi ripetuti o l'esecuzione di operazioni aritmetiche su tipi di Unified Data Model (UDM) non firmati richiede soluzioni alternative di sintassi per evitare errori del compilatore, errori di runtime o set di risultati errati. Questo documento illustra questi vincoli tecnici e i pattern logici che li risolvono.
Prima di iniziare
Prima di testare o modificare le regole YARA-L 2.0, assicurati che il tuo account disponga dei seguenti diritti tecnici:
Ruoli IAM richiesti
roles/chronicle.viewer(Visualizzatore operazioni di sicurezza): per visualizzare le regole esistenti e i metadati di rilevamento.roles/chronicle.editor(Editor delle operazioni di sicurezza): per modificare la logica delle regole e salvare le modifiche.
Autorizzazioni obbligatorie
chronicle.rules.runTest: obbligatoria per eseguire la funzionalità Esegui test sui dati storici.chronicle.detections.get: per esaminare l'output degli eventi non nidificati nella dashboard di rilevamento.
Terminologia chiave
- UDM (Unified Data Model): lo schema normalizzato utilizzato per strutturare tutta la telemetria di sicurezza importata nella piattaforma.
- Unnesting: l'espansione a livello di motore di un singolo evento UDM contenente un campo ripetuto (array) in più righe. Ogni riga rappresenta un elemento univoco dell'array, il che può portare alla moltiplicazione delle righe durante la valutazione della regola.
- T₀ (esecuzione iniziale): la prima esecuzione di una regola sulla telemetria in entrata. Ciò si verifica durante la fase di "flusso di dati", spesso prima che vengano finalizzati i processi di arricchimento in background (come i true-up GeoIP o ASN).
Aggregazioni dei risultati con unnesting dei campi ripetuti
Quando una regola fa riferimento a un campo ripetuto in una variabile evento con più elementi, ogni elemento viene suddiviso in una riga evento separata.
Ad esempio, i due indirizzi IP nel campo ripetuto target.ip nell'evento $e vengono suddivisi in due istanze di $e, ciascuna con un valore target.ip diverso.
rule outbound_ip_per_app {
meta:
events:
$e.principal.application = $app
match:
$app over 10m
outcome:
$outbound_ip_count = count($e.target.ip) // yields 2.
condition:
$e
}
Record di eventi: prima e dopo l'annidamento
Le tabelle in questa sezione mostrano come un singolo evento contenente un array di indirizzi IP viene trasformato in due record distinti.
Prima dell'annidamento
La tabella seguente mostra il record dell'evento prima di unnesting del campo ripetuto:
| metadata.id | principal.application | target.ip |
|---|---|---|
aaaaaaaaa |
Google SecOps |
[192.0.2.20, 192.0.2.28] |
Dopo l'annidamento
La tabella seguente mostra il record dell'evento dopo l'annullamento della nidificazione del campo ripetuto:
| metadata.id | principal.application | target.ip |
|---|---|---|
aaaaaaaaa |
Google SecOps |
192.0.2.20 |
aaaaaaaaa |
Google SecOps |
192.0.2.28 |
Campi ripetuti nidificati (prodotto cartesiano)
Quando una regola fa riferimento a un campo ripetuto nidificato all'interno di un altro, ad esempio security_results.action, l'annullamento della nidificazione avviene contemporaneamente a entrambi i livelli (principale e secondario). In questo modo, viene generato un prodotto cartesiano di tutti gli elementi.
Nell'esempio seguente, un evento $e con due valori ripetuti su security_results e due valori ripetuti su security_results.actions viene nidificato in quattro istanze.
rule security_action_per_app {
meta:
events:
$e.principal.application = $app
match:
$app over 10m
outcome:
$security_action_count = count($e.security_results.actions) // yields 4.
condition:
$e
}
Record di eventi prima dell'annullamento della nidificazione
Il record originale memorizza le azioni all'interno di una struttura di array nidificata.
| metadata.id | principal.application | security_results |
|---|---|---|
aaaaaaaaa |
Google SecOps |
[ { actions: [ ALLOW, FAIL ] }, { actions: [ CHALLENGE, BLOCK ] } ] |
Record di eventi dopo l'annullamento della nidificazione
Dopo l'espansione, ogni azione univoca diventa una riga a sé stante, il che può portare a conteggi imprevisti nelle aggregazioni non distinte.
| metadata.id | principal.application | security_results.actions |
|---|---|---|
aaaaaaaaa |
Google SecOps |
CONSENTI |
aaaaaaaaa |
Google SecOps |
ESITO NEGATIVO |
aaaaaaaaa |
Google SecOps |
CHALLENGE |
aaaaaaaaa |
Google SecOps |
BLOCCA |
Impatto su campi non correlati
Questo comportamento di unnesting nella valutazione delle regole può produrre aggregazioni di risultati impreviste quando la regola fa riferimento a uno o più campi ripetuti con un campo principale che è anche un campo ripetuto. Le aggregazioni non distinte come sum(), array() e count() non possono tenere conto dei valori duplicati in altri campi dello stesso evento prodotti dal comportamento di unnesting.
Nell'esempio seguente, l'evento $e ha un singolo nome host (google.com), ma il risultato (hostnames)
aggrega quattro istanze non nidificate dello stesso evento $e, ciascuna con un valore
principal.hostname duplicato. Questo risultato produce quattro nomi host (anziché uno)
a causa dell'annullamento della nidificazione dei valori ripetuti su security_results.actions.
rule security_action_per_app {
meta:
events:
$e.principal.application = $app
match:
$app over 10m
outcome:
$hostnames = array($e.principal.hostname) // yields 4.
$security_action_count = count($e.security_results.action) // yields 4.
condition:
$e
}
Record di eventi prima dell'annullamento della nidificazione con campi non correlati
Il nome host è un valore singolo, ma si trova accanto ai risultati di sicurezza ripetuti.
| metadata.id | principal.application | principal.hostname | security_results |
|---|---|---|---|
aaaaaaaaa |
Google SecOps |
google.com |
[ { action: [ ALLOW, FAIL ] }, { action: [ CHALLENGE, BLOCK ] } ] |
Record di eventi dopo l'annidamento con campi non correlati
Il nome host è ora duplicato in quattro righe, il che fa sì che la funzione array() lo rilevi quattro volte.
| metadata.id | principal.application | principal.hostname | security_results.action |
|---|---|---|---|
aaaaaaaaa |
Google SecOps |
google.com |
CONSENTI |
aaaaaaaaa |
Google SecOps |
google.com |
ESITO NEGATIVO |
aaaaaaaaa |
Google SecOps |
google.com |
CHALLENGE |
aaaaaaaaa |
Google SecOps |
google.com |
BLOCCA |
Soluzione alternativa per il comportamento di unnesting
Per assicurarti che i valori dei risultati siano accurati quando si verifica l'un-nesting, utilizza la versione distinta dell'aggregazione selezionata. Le seguenti funzioni ignorano le righe duplicate create dall'annullamento del nidificazione:
max()min()array_distinct()count_distinct()
Aggregazioni dei risultati con più variabili di evento
Se una regola contiene più variabili di evento, nell'aggregazione è presente un elemento separato per ogni combinazione di eventi inclusa nel rilevamento. Ad esempio, se la seguente regola di esempio viene eseguita sugli eventi elencati:
events:
$e1.field = $e2.field
$e2.somefield = $ph
match:
$ph over 1h
outcome:
$some_outcome = sum(if($e1.otherfield = "value", 1, 0))
condition:
$e1 and $e2
event1:
// UDM event 1
field="a"
somefield="d"
event2:
// UDM event 2
field="b"
somefield="d"
event3:
// UDM event 3
field="c"
somefield="d"
La somma viene calcolata su ogni combinazione di eventi, consentendoti di utilizzare entrambe le variabili di evento nei calcoli del valore del risultato. Nel calcolo vengono utilizzati i seguenti elementi:
1: $e1 = event1, $e2 = event2
2: $e1 = event1, $e2 = event3
3: $e1 = event2, $e2 = event1
4: $e1 = event2, $e2 = event3
5: $e1 = event3, $e2 = event1
5: $e1 = event3, $e2 = event2
Ciò si traduce in una somma massima potenziale di 6, anche se $e2 può corrispondere solo a 3 eventi distinti.
Questo influisce su somma, conteggio e array. Per count e array, l'utilizzo di count_distinct o array_distinct può risolvere il problema, ma non esiste una soluzione alternativa per sum.
Parentesi all'inizio di un'espressione
L'inizio di un'espressione con parentesi non è supportato e attiva un errore di analisi nell'editor delle regole.
Sintassi non valida
parsing: error with token: ")"
invalid operator in events predicate
L'esempio seguente genera questo tipo di errore:
($event.metadata.ingested_timestamp.seconds -
$event.metadata.event_timestamp.seconds) / 3600 > 1
Varianti di sintassi valide
Le seguenti varianti di sintassi restituiscono lo stesso risultato, ma con una sintassi valida:
$event.metadata.ingested_timestamp.seconds / 3600 -
$event.metadata.event_timestamp.seconds / 3600 > 1
1 / 3600 * ($event.metadata.ingested_timestamp.seconds -
$event.metadata.event_timestamp.seconds) > 1
1 < ($event.metadata.ingested_timestamp.seconds -
$event.metadata.event_timestamp.seconds) / 3600
L'array di indici nel risultato richiede l'aggregazione
Non è consentito indicizzare direttamente un array all'interno della sezione outcome per i campi ripetuti. Richiede una variabile segnaposto temporanea.
outcome:
$principal_user_dept = $suspicious.principal.user.department[0]
Soluzione alternativa
Acquisisci l'indice dell'array specifico in una variabile segnaposto all'interno della sezione events, quindi fai riferimento a quel segnaposto nel risultato.
events:
$principal_user_dept = $suspicious.principal.user.department[0]
outcome:
$principal_user_department = $principal_user_dept
OR condizione con non esistenza
Se applichi una condizione OR tra due variabili di evento separate e se la regola corrisponde alla non esistenza, la regola viene compilata correttamente, ma può produrre rilevamenti di falsi positivi.
Ad esempio, la seguente sintassi della regola può trovare corrispondenze con eventi con $event_a.field = "something" anche se non dovrebbe:
events:
not ($event_a.field = "something" **or** $event_b.field = "something")
condition:
$event_a and #event_b >= 0
Soluzione alternativa
Separa i controlli di non esistenza in singoli blocchi per ogni variabile per mantenere l'integrità logica.
events:
not ($event_a.field = "something")
not ($event_b.field = "something")
condition:
$event_a and #event_b >= 0
Aritmetica con campi di eventi non firmati
Se utilizzi un valore letterale intero in un'operazione aritmetica (+, -, *) o modulo (%) con un campo UDM intero senza segno, ricevi un errore. I valori letterali interi standard vengono impostati per impostazione predefinita su numeri interi con segno a 64 bit (INT64), che non sono compatibili con i campi UDM definiti come numeri interi senza segno a 64 bit (UINT64), ad esempio network.received_bytes, network.sent_bytes e principal.file.size.
Questo vincolo di tipizzazione rigorosa si applica sia alle sezioni events che outcome, incluse le aggregazioni sum() o max() e le espressioni del divisore. Il controllo della sintassi YARA-L tratta sia i numeri interi con segno che quelli senza segno come un tipo int generico. Di conseguenza, una regola che combina campi UDM UINT64 e valori letterali interi INT64 potrebbe superare la convalida della sintassi al momento del salvataggio, ma non riuscire durante l'esecuzione della regola o quando fai clic su Esegui test. Quando la regola non va a buon fine, genera un "Errore regola" generico (cannot complete [arithmetic/mod] operation between unsigned and signed integer).
Ad esempio, il seguente confronto events supera la convalida della sintassi quando viene salvato, ma non riesce durante l'esecuzione perché $e.network.received_bytes (UINT64) viene moltiplicato per il valore letterale intero con segno 2 (INT64):
events:
$total_bytes = $e.network.total_bytes
$total_bytes = $e.network.received_bytes * 2
Soluzioni alternative
Puoi risolvere questa mancata corrispondenza del tipo utilizzando uno dei seguenti metodi:
- Cast esplicito di numeri interi (
cast.as_int()ocast.as_uint()): converti il campo UDM non firmato in un numero intero firmato utilizzandocast.as_int()o converti il valore letterale intero in un numero intero non firmato utilizzandocast.as_uint(). Utilizza questo metodo quando hai bisogno di un risultato intero o quando utilizzi l'operatore modulo (%), che accetta solo operandi interi. - Coercizione in rappresentazione in virgola mobile: forza la costante intera a comportarsi come un
floatutilizzando un valore letterale in rappresentazione in virgola mobile, ad esempio2.0o100.0. Nelle espressioni semplici senza altri operatori di divisione sulla stessa riga, puoi anche utilizzare un'operazione di divisione, ad esempio(2/1). Nelle espressioni che contengono già operatori di divisione (/), utilizza valori letterali in rappresentazione in virgola mobile ocast.as_int()anziché la coercizione della divisione(N/1). Più caratteri/sulla stessa riga possono essere interpretati come un valore letterale di espressione regolare, causando un errore di analisi durante la compilazione o interrompendo l'evidenziazione della sintassi nell'editor delle regole.
Soluzione alternativa nella sezione events
Utilizza cast.as_int(), una rappresentazione in virgola mobile o la coercizione della divisione per correggere l'errore nella sezione events:
Utilizza
cast.as_int():events: $total_bytes = $e.network.total_bytes $total_bytes = cast.as_int($e.network.received_bytes) * 2Utilizza un valore letterale in rappresentazione in virgola mobile:
events: $total_bytes = $e.network.total_bytes $total_bytes = $e.network.received_bytes * 2.0Utilizza la coercizione della divisione:
events: $total_bytes = $e.network.total_bytes $total_bytes = $e.network.received_bytes * (2/1)
Soluzione alternativa nella sezione outcome
La seguente espressione outcome viene salvata senza errori di sintassi, ma non riesce in fase di runtime perché sum($e.network.sent_bytes) e sum($e.network.received_bytes) restituiscono valori UINT64 che vengono combinati con i valori letterali INT64 con segno 100 e 1:
outcome:
// Saves, but fails at runtime:
$upload_ratio_pct = sum($e.network.sent_bytes) * 100 / (sum($e.network.received_bytes) + 1)
Riscrivi l'espressione utilizzando valori letterali in virgola mobile o cast.as_int() in modo che venga salvata ed eseguita senza errori:
Utilizza valori letterali in virgola mobile:
outcome: $upload_ratio_pct = sum($e.network.sent_bytes) * 100.0 / (sum($e.network.received_bytes) + 1.0)Utilizza
cast.as_int():outcome: $upload_ratio_pct = sum(cast.as_int($e.network.sent_bytes)) * 100 / (sum(cast.as_int($e.network.received_bytes)) + 1)
Arricchimento GeoIP e coerenza finale
Il sistema dà priorità alla velocità rispetto all'accuratezza immediata nelle fasi iniziali di arricchimento (Streaming e Sensibile alla latenza), il che può portare a dati mancanti e potenziali falsi positivi. Il sistema continua ad arricchire i dati in background, ma i dati potrebbero non essere disponibili quando viene eseguita la regola. Questo fa parte del normale processo di coerenza finale.
Per evitare falsi positivi causati dal ritardo di arricchimento, verifica esplicitamente che il campo non sia vuoto prima di valutarne il valore.
Ad esempio, considera questo evento della regola:
$e.principal.ip_geo_artifact.network.asn = "16509" AND
$e.principal.ip_geo_artifact.location.country_or_region = "United Kingdom"
La regola si basa sul fatto che l'evento deve avere $e.principal.ip_geo_artifact.network.asn = "16509" E $e.principal.ip_geo_artifact.location.country_or_region = "United Kingdom", che sono entrambi campi arricchiti. Se l'arricchimento non viene completato in tempo, la regola produrrà un falso positivo.
Per evitare questo problema, un controllo migliore per questa regola sarebbe:
$e.principal.ip_geo_artifact.network.asn != "" AND
$e.principal.ip_geo_artifact.network.asn = "16509" AND
$e.principal.ip_geo_artifact.location.country_or_region != "" AND
$e.principal.ip_geo_artifact.location.country_or_region = "United Kingdom"
Questa regola elimina la possibilità che l'evento venga attivato da IP con ASN 16509, ma situati al di fuori del Regno Unito. In questo modo, la precisione complessiva della regola viene migliorata.
Scopri come risolvere i problemi relativi al ritardo di arricchimento.
Risoluzione dei problemi
Questa sezione descrive le aspettative sul rendimento e fornisce soluzioni self-service per i problemi comuni in cui il comportamento di rilevamento in tempo reale differisce dai risultati dei test.
Eventi con data futura
Le regole multi-evento sono progettate per elaborare gli eventi in ordine cronologico rispetto all'importazione. Se specifichi e attivi una regola per più eventi, non vengono create rilevazioni per gli eventi con timestamp futuri, ad esempio quando event.timestamp ha una data e un'ora impostate dopo ingest.timestamp.
Ritardo di arricchimento
Google SecOps dà la priorità alla velocità di importazione per mostrare gli avvisi iniziali il più rapidamente possibile. Tuttavia, i processi di arricchimento in background, come la risoluzione dei metadati GeoIP, ASN o UDM, seguono un modello di coerenza finale.
Esecuzione iniziale (T₀)
Il motore live potrebbe valutare una regola prima del completamento dell'arricchimento in background. A seconda che la logica si basi su campi arricchiti per i rilevamenti o le esclusioni, ciò può portare alle seguenti discrepanze temporanee:
Falsi negativi (ritardo nel rilevamento): si tratta di un risultato comune. Se una regola dipende da un campo arricchito per l'attivazione (ad esempio,
target.user.department == "Finance") e questo campo ènull, la regola non corrisponde durante l'esecuzione iniziale.Falsi positivi (mancata esclusione): se la regola utilizza campi arricchiti per filtrare l'attività nota come valida (ad esempio
NOT target.ip_geo_country == "US"), la regola potrebbe attivare un falso positivo perché i dati di"esclusione" non sono ancora stati applicati.
Esecuzioni di allineamento
Queste esecuzioni in background rivalutano i dati dopo un ritardo (ad esempio, 4 ore o 30 ore). In questo modo, gli stati di rilevamento vengono "corretti" come segue:
Rilevamenti tardivi: gli eventi che erano "falsi negativi" al momento T₀ ora producono un rilevamento una volta finalizzato l'arricchimento.
Correzione: eventuali falsi positivi T₀ rimangono nel sistema, ma i dati completamente arricchiti sono visibili nel visualizzatore UDM per il triage manuale.
Discrepanza del test di esecuzione
Lo strumento Esegui test opera su dati storici già riconciliati. Poiché i dati sono completamente arricchiti al momento dell'esecuzione di un test manuale, puoi visualizzare immediatamente i risultati di "conguaglio". Ciò significa che non vedrai i falsi negativi T₀ o i falsi positivi basati sull'esclusione che si sono verificati durante l'esecuzione iniziale in tempo reale.
Correzione degli errori
Utilizza la seguente tabella per risolvere le discrepanze tra gli avvisi in tempo reale e i risultati dei test.
| Problema | Descrizione | Correzione applicabile |
|---|---|---|
| Errore di esclusione | Una regola viene attivata nonostante un'esclusione (ad esempio != "ASN_123") perché il campo era nullo durante l'esecuzione iniziale. |
Aggiungi un controllo non nullo alla sezione degli eventi per assicurarti che i dati vengano arricchiti prima della valutazione, ad esempio:$e.principal.ip_geo_artifact.network.asn != ""
|
| Confronto tra corrispondenza in tempo reale e corrispondenza di test | Le regole attive attivano gli avvisi, ma l'esecuzione del test sugli stessi dati mostra "No Results". |
Aggiungi $e.field != "", che controlla tutti i campi con informazioni aggiuntive (GeoIP, ASN, File Path) per sincronizzare il comportamento in tempo reale e storico. |
| Metadati mancanti | I rilevamenti vengono visualizzati nella dashboard con campi GeoIP o File Path vuoti. |
Questo è il comportamento previsto per le esecuzioni T0. Per risolvere il problema, includi un controllo field != "" o configura un ritardo di liquidazione nella pianificazione delle regole per concedere più tempo per l'importazione.
|
Convalida e test
Per verificare che una regola gestisca correttamente l'arricchimento ritardato:
Identifica il ritardo: individua un rilevamento che ritieni sia un falso positivo. Nella colonna Tipo di rilevamento, cerca l'icona
<span class="material-icons">lightbulb</span>. Gli avvisi senza questa icona provengono dall'esecuzione principale, in cui il ritardo di arricchimento è più comune.Aggiorna la logica della regola: aggiungi un controllo
field != ""per tutti i punti dati arricchiti utilizzati nella logica.
Esempio (percorso file):
$e.target.process.parent_process.file.full_path != ""Testa e verifica:
- Utilizza la funzionalità Esegui test per assicurarti che la logica corrisponda ancora ai dati storici previsti.
- Verifica che la regola venga attivata (o esclusa correttamente) solo durante le esecuzioni di aggiustamento una volta compilati i campi di arricchimento.
Per saperne di più, consulta Informazioni sulla pianificazione dell'esecuzione delle regole e Configura pianificazioni personalizzate per le regole.
Hai bisogno di ulteriore assistenza? Ricevi risposte dai membri della community e dai professionisti di Google SecOps.