Datadog SECL est un langage spécifique à un domaine personnalisé utilisé pour créer des expressions d’Agent et des politiques au sein de Datadog Workload Protection. SECL permet aux équipes de sécurité de définir des règles de détection des menaces en temps réel en spécifiant des conditions, des opérateurs et des modèles que les Agents de sécurité peuvent surveiller sur les hôtes, les conteneurs, les applications et l’infrastructure cloud.

Comment les règles SECL s’articulent

Considérez SECL comme un filtre local : il s’exécute à l’intérieur de l’Agent sur chaque host, surveillant les événements du noyau et du système d’exploitation. Lorsqu’un événement correspond à votre expression SECL, l’Agent déclenche une détection.

Les règles de détection des menaces de Datadog agissent comme une logique backend : elles combinent une ou plusieurs règles d’Agent (en utilisant @agent.rule_id), ajoutent des seuils, suppriment le bruit et décident de la manière dont les alertes sont acheminées.

En résumé, la règle d’Agent détecte un comportement brut et la règle de détection le transforme en scénarios d’attaque de bout en bout.

Ce guide décrit comment créer manuellement des expressions de règle, mais Workload Protection fournit également l'assistant Assisted rule creator pour vous guider dans la création conjointe des règles d'Agent et de détection. Consultez Créer ensemble les règles personnalisées d'Agent et de détection.

Syntaxe des expressions SECL

Le format standard d’une expression SECL est le suivant :

<event-type>.<event-attribute> <operator> <value> [<operator> <event-type>.<event-attribute>] ...

En utilisant ce format, un exemple de règle pour un système Linux ressemble à ceci :

open.file.path == "/etc/shadow" && process.file.path not in ["/usr/sbin/vipw"]

Opérateurs

Les opérateurs SECL sont utilisés pour combiner des attributs d’événement en une expression complète. Les opérateurs suivants sont disponibles :

Opérateur SECLDéfinitionVersion de l’Agent
==Égal7.27
!=Différent de7.27
>Supérieur à7.27
>=Supérieur ou égal à7.27
<Inférieur7.27
<=Inférieur ou égal7.27
! ou notNon7.27
^Non binaire7.27
in [elem1, ...]L’élément est contenu dans la liste7.27
not in [elem1, ...]L’élément n’est pas contenu dans la liste7.27
=~Correspondance de chaîne7.27
!~Non correspondance de chaîne7.27
&Et binaire7.27
|Ou binaire7.27
&& ou andEt logique7.27
|| ou orOu logique7.27
in CIDRL’élément est dans la plage IP7.37
not in CIDRL’élément n’est pas dans la plage IP7.37
allin CIDRTous les éléments sont dans la plage IP7.37
in [CIDR1, ...]L’élément est dans les plages IP7.37
not in [CIDR1, ...]L’élément n’est pas dans les plages IP7.37
allin [CIDR1, ...]Tous les éléments sont dans les plages IP7.37

Modèles et expressions régulières

Des modèles ou des expressions régulières peuvent être utilisés dans les expressions SECL. Ils peuvent être utilisés avec les opérateurs in, not in, =~ et !~.

FormatExempleChamps pris en chargeVersion de l’agent
~"pattern"~"httpd.*"Tous7.27
r"regexp"r"rc[0-9]+"Tous sauf .path7.27

Les modèles sur les champs .path sont utilisés comme Glob. * correspond aux fichiers et dossiers au même niveau. **, introduit dans la version 7.34, peut être utilisé à la fin d’un chemin pour correspondre à tous les fichiers et sous-dossiers.

Durées

Vous pouvez utiliser SECL pour écrire des règles basées sur des durées, qui se déclenchent sur des événements survenant pendant une période spécifique. Par exemple, déclenchez sur un événement où un fichier secret est accédé plus d’une certaine durée après la création d’un processus. Une telle règle pourrait être rédigée comme suit :

open.file.path == "/etc/secret" && process.file.name == "java" && process.created_at > 5s

Les durées sont des nombres avec un suffixe d’unité. Les suffixes pris en charge sont « s », « m », « h ».

Syntaxe spécifique à la plateforme

Les expressions SECL prennent en charge plusieurs plateformes. Vous pouvez utiliser la documentation ci-dessous pour voir quels attributs et assistants sont disponibles pour chacun.

Conseils pour la création de règles

  • Définissez toujours le système d’exploitation (OS).
  • Ancrez sur l’ascendance pour réduire le bruit. Utilisez process.ancestors.file.name.
  • Utilisez des durées (par exemple, > 5s, 10m, 2h) pour cibler des fenêtres d’exécution étroites.
  • Utilisez la correspondance exacte (==) dans la mesure du possible, car elle génère le moins de bruit.
  • L’appartenance à une liste (in [...]) est idéale pour les listes d’autorisation ou les ensembles de valeurs contrôlés.
  • Utilisez une correspondance glob (~"/path/*") pour les familles de chemins, car elle est plus sûre et plus rapide que les regex.
  • Utilisez les regex (=~) uniquement lorsque les globs/listes ne peuvent pas être utilisés. Gardez l’expression regex aussi étroite que possible. En règle générale, commencez par == ou in [...]. N’utilisez les regex qu’en dernier recours.
  • Utilisez la négation (not in [...], !~) pour définir explicitement des exceptions (par exemple, des outils approuvés).
  • Utilisez les opérateurs CIDR (in CIDR, not in CIDR) pour les limites réseau.
  • Nommez les règles selon leur comportement, avec un format qui suit Quoi + Qui + Contexte.
  • Taggez généreusement : team, app, env, MITRE, severity.

Évitez les erreurs courantes

ModèleExplication
open.file.path == "/etc/passwd", exec.comm != ""Trop large. Correspond à de nombreux cas d’utilisation valides.
container.id != ""Utile uniquement s’il est limité par un champ plus spécifique.

Bibliothèque d’exemples

Vous trouverez des exemples plus détaillés dans la politique par défaut fournie prête à l'emploi avec l'Agent. Voir la politique par défaut de Workload Protection.

Dans les fichiers de politique de l’Agent, chaque règle inclut une id et une expression. Vous pouvez également ajouter des actions facultatives. Voir Variables et actions pour en savoir plus.

Linux

Accès aux fichiers sensibles (outils sûrs de la liste d’autorisation)

rules:
  - id: access_sensitive_files
    expression: >-
      open.file.path in ["/etc/shadow", "/etc/sudoers"] &&
      process.file.path not in ["/usr/sbin/vipw", "/usr/sbin/visudo"]
    filters:
      - os == "linux"

NGINX ou PHP lançant bash

rules:
  - id: nginx_php_spawn_bash
    expression: >-
      exec.file.path == "/usr/bin/bash" &&
      (
        process.ancestors.file.name == "nginx" ||
        process.ancestors.file.name =~ "php*"
      )
    filters:
      - os == "linux"

Accès suspect à l’IMDS depuis un conteneur

rules:
  - id: suspicious_imds_access
    expression: >-
      connect &&
      network.destination.ip in ["169.254.169.254"] &&
      container.id != ""
    filters:
      - os == "linux"

Chargements de modules noyau en dehors de la fenêtre de maintenance

rules:
  - id: kernel_module_load
    expression: >-
      load_module &&
      process.user != "root" &&
      process.ancestors.file.name not in ["modprobe", "insmod"]
    filters:
      - os == "linux"

Lecture de fichier sensible peu après le démarrage

rules:
  - id: sensitive_file_read_after_start
    expression: >-
      open.file.path == "/etc/secret" &&
      process.file.name == "java" &&
      process.created_at > 5s
    filters:
      - os == "linux"

Sortant vers des adresses IP n’appartenant pas à l’entreprise (CIDR allowlist)

rules:
  - id: outbound_non_corporate_ips
    expression: >-
      connect &&
      network.destination.ip not in [10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12]
    filters:
      - os == "linux"

Windows

Persistance du registre via une clé d’exécution

rules:
  - id: registry_run_key_persistence
    expression: >-
      set_key_value &&
      open_key.registry.key_path =~ "*\\Software\\Microsoft\\Windows\\CurrentVersion\\Run*"
    filters:
      - os == "windows"

Binaire non signé lançant PowerShell

rules:
  - id: unsigned_binary_powershell
    expression: >-
      exec.file.path =~ "*\\WindowsPowerShell\\v1.0\\powershell.exe" &&
      process.parent.file.path !~ "*\\Program Files*" &&
      process.user_sid != "S-1-5-18"
    filters:
      - os == "windows"

Multiplateforme

Indicateurs de crypto-mineurs

rules:
  - id: crypto_miner_indicators
    expression: >-
      exec.args_flags in ["cpu-priority", "donate-level", ~"randomx-1gb-pages"] ||
      exec.args in [~"*stratum+tcp*", ~"*nicehash*", ~"*yespower*"]