Créer des requêtes en plusieurs étapes dans YARA-L
Ce document explique comment les requêtes à plusieurs étapes dans YARA-L vous permettent d'intégrer la sortie d'une étape de requête directement dans l'entrée d'une étape ultérieure. Ce processus vous permet de mieux contrôler la transformation des données qu'une requête monolithique unique.
Intégrer les requêtes en plusieurs étapes aux fonctionnalités existantes
Les requêtes multi-étapes fonctionnent en parallèle avec les fonctionnalités existantes suivantes de Google Security Operations :
Règles de détection composite : les requêtes en plusieurs étapes complètent les règles de détection composite. Contrairement aux règles composites, les requêtes multi-étapes qui utilisent la recherche peuvent renvoyer des résultats en temps réel.
Plages de dates et règles multi-événements : vous pouvez utiliser des requêtes à plusieurs étapes pour détecter des anomalies en comparant différentes périodes dans vos données. Par exemple, vous pouvez utiliser vos étapes de requête initiales pour établir une référence sur une période prolongée, puis utiliser une étape ultérieure pour évaluer l'activité récente par rapport à cette référence. Vous pouvez également utiliser des règles multi-événements pour créer un type de comparaison similaire.
Les requêtes à plusieurs étapes en YARA-L sont compatibles avec les tableaux de bord et la recherche.
Les jointures permettent de corréler des données provenant de plusieurs sources afin de fournir plus de contexte pour une enquête. En associant des événements, des entités et d'autres données connexes, vous pouvez examiner des scénarios d'attaque complexes. Pour en savoir plus, consultez Utiliser des jointures dans la recherche.
Définir la syntaxe YARA-L à plusieurs étapes
Lorsque vous configurez une requête à plusieurs étapes, tenez compte des points suivants :
- Étape de limite : les requêtes à plusieurs étapes doivent contenir entre une et quatre étapes nommées, en plus de l'étape racine.
- Syntaxe de l'ordre : définissez toujours la syntaxe de l'étape nommée avant de définir la syntaxe de l'étape racine.
Créer une requête YARA-L en plusieurs étapes
Pour créer une requête YARA-L à plusieurs étapes, procédez comme suit.
Structure et syntaxe des étapes
Accédez à Investigation > Recherche. Suivez ces exigences structurelles lorsque vous définissez les étapes de votre requête :
Syntaxe : utilisez la syntaxe suivante pour nommer chaque étape et la séparer des autres :
stage <stage name> { }
Accolades : placez toute la syntaxe de l'étape entre accolades {}.
Order (Ordre) : définissez la syntaxe de toutes les étapes nommées avant de définir l'étape racine.
Références : chaque étape peut faire référence à des étapes définies plus tôt dans la requête.
Étape racine : une requête doit comporter une étape racine, qui est traitée après toutes les étapes nommées.
L'exemple de phase suivant, daily_stats, collecte les statistiques réseau quotidiennes :
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)
}
Sortie de la phase d'accès
La sortie d'une étape nommée est accessible aux étapes suivantes à l'aide des champs d'étape. Les champs d'étape correspondent aux variables match et outcome de l'étape. Ils peuvent être utilisés de la même manière que les champs du modèle de données unifié (UDM).
Utilisez la syntaxe suivante pour accéder à un champ d'étape :
$<stage name>.<variable name>
Codes temporels de la période d'accès (facultatif)
Si une étape nommée utilise une fenêtre glissante, par saut ou cumulée, accédez au début et à la fin de la fenêtre pour chaque ligne de sortie à l'aide des champs réservés suivants :
$<stage name>.window_start$<stage name>.window_end
window_start et window_end sont des champs entiers exprimés en secondes depuis l'epoch Unix. La taille des fenêtres peut varier selon l'étape.
Limites
Les requêtes à plusieurs étapes présentent les contraintes fonctionnelles et structurelles suivantes :
Limites structurelles et de phase
Étape racine : une seule étape racine est autorisée par requête.
Étapes nommées : vous pouvez créer jusqu'à quatre étapes nommées.
Référencement des étapes : une étape ne peut faire référence qu'à des étapes définies logiquement avant elle dans la même requête.
Jointures : un maximum de quatre jointures de tables non liées aux données est autorisé pour toutes les étapes.
Exigence de résultat : chaque étape nommée (à l'exception de l'étape racine) doit inclure une section
matchou une sectionoutcome. La sectionoutcomene nécessite pas d'agrégation.
Limites de fenêtre et de compatibilité
Compatibilité des fonctionnalités : les requêtes à plusieurs étapes sont compatibles avec Recherche et Tableaux de bord, mais pas avec Règles.
Types de fenêtres : évitez de mélanger différents types de fenêtres dans une même requête.
Dépendance de fenêtre : une étape utilisant une fenêtre glissante ou un saut ne peut pas dépendre d'une autre étape qui utilise également une fenêtre glissante ou un saut.
Taille de la fenêtre glissante : bien que la taille des fenêtres glissantes puisse varier d'une étape à l'autre, la différence de taille doit être inférieure à 720x.
Exemple : Différence d'agrégation des étapes
La configuration de fenêtre suivante n'est pas autorisée :
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
Si l'étape monthly_stats agrège les données par mois et que l'étape racine agrège la sortie de monthly_stats par minute, chaque ligne de monthly_stats correspond à 43 200 lignes dans l'étape racine (car il y a 43 200 minutes dans un mois).
Limites concernant les étapes et les requêtes
Chaque étape individuelle d'une requête à plusieurs étapes est soumise aux contraintes suivantes :
La plupart des limites qui s'appliquent à une requête à une seule étape s'appliquent également à chaque étape individuelle :
Exigence de sortie : chaque étape doit générer au moins une variable de correspondance ou de résultat (champ d'étape).
Fenêtre dans la jointure : la taille maximale de la fenêtre (par saut, cumulée ou glissante) utilisée dans une jointure est de deux jours.
Nombre maximal de variables de résultat :
20 pour les clients qui n'ont pas activé la limite de variables de résultat plus élevée
50 pour les clients qui ont choisi d'autoriser une limite plus élevée pour la variable de résultat
Nombre maximal d'éléments dans une variable de résultat à valeur de tableau.
Les requêtes à plusieurs étapes sont soumises aux mêmes limites que les requêtes statistiques :
Requêtes de statistiques : 120 QPH (API et UI)
Vues de recherche depuis Google SecOps : 100 vues par minute
Les jointures à plusieurs étapes sont compatibles avec l'interface utilisateur et l'API
EventService.UDMSearch, mais pas avec l'APISearchService.UDMSearch. Les requêtes à plusieurs étapes sans jointures sont également acceptées dans l'interface utilisateur.
Limites d'événement et globales
Nombre maximal d'événements :
Le nombre d'événements que les requêtes à plusieurs étapes peuvent traiter simultanément est strictement limité :
Événements UDM : deux événements UDM maximum sont autorisés.
Événements du graphique de contexte d'entité (ECG) : un événement ECG maximum est autorisé.
Limites globales des requêtes :
Ces limites sont des contraintes à l'échelle de la plate-forme qui contrôlent la période et la quantité de données qu'une requête à plusieurs étapes peut renvoyer.
Pour une période de requête, la période maximale pour une requête standard est de 30 jours.
La taille maximale de l'ensemble de résultats est de 10 000.
Exemples de requêtes en plusieurs étapes
Les exemples de cette section vous aideront à illustrer comment créer une requête YARA-L complète en plusieurs étapes.
Exemple : Rechercher les connexions réseau inhabituellement actives (heures)
Cet exemple YARA-L en plusieurs étapes identifie les paires d'adresses IP présentant une activité réseau supérieure à la normale, en ciblant les paires qui maintiennent une activité élevée pendant plus de trois heures. La requête inclut deux composants obligatoires : l'étape nommée hourly_stats et l'étape root.
L'étape hourly_stats recherche les paires principal.ip et target.ip avec des niveaux d'activité réseau élevés.
Cette étape renvoie une seule valeur horaire pour les champs suivants :
Statistiques pour l'adresse IP source (chaîne) :
$hourly_stats.src_ipStatistiques pour l'adresse IP de destination (chaîne) :
$hourly_stats.dst_ipStatistiques sur le nombre d'événements (entier) :
$hourly_stats.countÉcart-type des octets reçus (float) :
$hourly_stats.std_recd_bytesNombre moyen d'octets reçus (float) :
$hourly_stats.avg_recd_bytesHeure de début du bucket horaire en secondes depuis l'epoch Unix (entier) :
$hourly_stats.window_startHeure de fin du bucket horaire en secondes depuis l'époque Unix (entier) :
$hourly_stats.window_end
L'étape racine traite la sortie de l'étape hourly_stats. Il calcule les statistiques pour les paires principal.ip et target.ip dont l'activité dépasse le seuil spécifié par $hourly_stats. Il filtre ensuite les paires présentant plus de trois heures d'activité élevée.
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
Si vous modifiez la condition de correspondance dans l'étape racine comme suit, vous pouvez introduire une agrégation par jour pour la requête à plusieurs étapes.
match:
$src_ip, $dst_ip by day
Exemple : Rechercher les connexions réseau inhabituellement actives (à l'aide du score Z)
Cette requête en plusieurs étapes compare l'activité réseau moyenne quotidienne à l'activité du jour à l'aide d'un calcul de score Z (qui mesure le nombre d'écarts-types par rapport à la moyenne). Cette requête recherche efficacement une activité réseau inhabituellement élevée entre les ressources internes et les systèmes externes.
Prérequis : La période de la requête doit être supérieure ou égale à deux jours et inclure le jour actuel pour que le score Z calculé soit efficace.
Cette requête à plusieurs étapes inclut les étapes daily_stats et root, qui fonctionnent ensemble pour calculer le score Z de l'activité réseau :
L'étape
daily_statseffectue l'agrégation quotidienne initiale. Elle calcule le nombre total d'octets échangés chaque jour pour chaque paire d'adresses IP (sourceettarget), puis renvoie les champs d'étape suivants (correspondant aux colonnes des lignes de sortie) :$daily_stats.source: singulier, chaîne$daily_stats.target: singulier, chaîne$daily_stats.exchanged_bytes: singulier, entier$daily_stats.window_start: singulier, entier$daily_stats.window_end: singulier, entier
L'étape racine agrège la sortie de l'étape
daily_statspour chaque paire d'adresses IP. Il calcule la moyenne et l'écart-type des octets échangés quotidiennement sur l'ensemble de la plage de recherche, ainsi que les octets échangés aujourd'hui. Il utilise ces trois valeurs calculées pour déterminer le score Z.La sortie liste les scores Z pour toutes les paires d'adresses IP du jour, triées par ordre décroissant.
// 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
Exporter des variables non agrégées à partir d'étapes
Les étapes nommées peuvent inclure une section outcome non agrégée. Cela signifie que les variables définies dans cette section outcome sont générées directement à partir de l'étape, ce qui permet aux étapes suivantes d'y accéder en tant que champs d'étape sans nécessiter d'agrégation groupée.
Exemple : Exporter une variable non agrégée
Cet exemple montre comment exporter des variables non agrégées. Notez la logique suivante :
top_5_bytes_sentrecherche les cinq événements ayant la plus forte activité réseau.L'étape
top_5_bytes_sentgénère les champs d'étape suivants correspondant aux colonnes des lignes de sortie :$top_5_bytes_sent.bytes_sent: singulier, entier$top_5_bytes_sent.timestamp_seconds: singulier, entier
L'étape
rootcalcule les codes temporels les plus récents et les plus anciens pour les cinq événements ayant la plus forte activité réseau.
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))
Implémenter le fenêtrage dans les requêtes à plusieurs étapes
Les requêtes à plusieurs étapes sont compatibles avec tous les types de fenêtrage (par saut, glissant et cumulatif) dans les étapes nommées. Si une étape nommée inclut une fenêtre, le début et la fin de la fenêtre pour chaque ligne de sortie sont accessibles à l'aide des champs réservés suivants :
$<stage name>.window_start$<stage name>.window_end
Exemple : fenêtre de saut
L'exemple suivant montre comment utiliser les fenêtres de saut dans une requête à plusieurs étapes :
L'étape
hourly_statsrecherche les paires d'adresses IP qui présentent une activité réseau élevée au cours de la même heure.hourly_statsgénère les champs d'étape suivants correspondant aux colonnes des lignes de sortie :$hourly_stats.src_ip: singulier, chaîne$hourly_stats.dst_ip: singulier, chaîne$hourly_stats.count: singulier, entier$hourly_stats.std_recd_bytes: singulier, float$hourly_stats.avg_recd_bytes: singulier, float$hourly_stats.window_start: singulier, entier$hourly_stats.window_end: singulier, entier
L'étape racine filtre les paires d'adresses IP présentant plus de trois heures d'activité élevée. Les heures peuvent se chevaucher en raison de l'utilisation d'une fenêtre récurrente dans l'étape
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
Problèmes connus
Nous vous recommandons de consulter les limites et les solutions de contournement recommandées suivantes lorsque vous implémentez des requêtes à plusieurs étapes :
Toutes les requêtes à plusieurs étapes se comportent comme des requêtes de recherche de statistiques (la sortie se compose de statistiques agrégées plutôt que de lignes d'événements ou de tableaux de données non agrégées).
Les performances des jointures avec UDM et les événements d'entité d'un côté peuvent être faibles en raison de la taille de cet ensemble de données. Nous vous recommandons vivement de filtrer autant que possible les événements UDM et d'entité du côté de la jointure (par exemple, en filtrant sur le type d'événement).
Pour obtenir des conseils généraux sur les pratiques recommandées, consultez Bonnes pratiques concernant Yara-L. Pour obtenir des informations spécifiques aux jointures, consultez Bonnes pratiques.
Vous avez encore besoin d'aide ? Obtenez des réponses de membres de la communauté et de professionnels Google SecOps.