Vai al contenuto
New

DeepSeek V4.1 Flash free for 7 days: 10M tokens a day after a top-up of $10 or more. Until Nov 30.

How it works

Scrivere una regola Sigma: il primo rilevamento, con i test

Scrivi una regola Sigma per i logon Windows falliti, validala, convertila per Splunk e provala su eventi known-good e known-bad.

Intermedio14 min di letturaDi NotHoly

Che cos'è una regola Sigma

Sigma è un formato aperto e indipendente dai vendor per descrivere un rilevamento. Scrivi la logica una volta sola, in un piccolo file YAML, e un convertitore la traduce nel linguaggio di query del SIEM o della piattaforma di log che usi davvero. Splunk, Elastic, Microsoft Sentinel e altri backend hanno tutti un convertitore, e la stessa regola può puntare a più di uno.

Il formato è mantenuto dal progetto SigmaHQ. La specifica sta nel repository sigma-specification, e la documentazione delle regole su sigmahq.io spiega i campi uno per uno. Se sai leggere un evento Windows, la parte difficile la conosci già. Una regola Sigma è solo un modo per dire "questo evento, con questi valori nei campi" in una forma che altre persone e altri tool possono leggere.

Perché conta

La maggior parte della logica di rilevamento si scrive direttamente nel linguaggio di query di un singolo prodotto. Una ricerca Splunk non gira in Sentinel e una query KQL non gira in Splunk. Quando un team cambia piattaforma, o quando una community condivide un rilevamento, la logica va riscritta da zero. È un lavoro lento, e la riscrittura introduce errori.

Sigma separa la logica di rilevamento dal linguaggio di query. La regola descrive la sorgente dei log e la condizione. Il backend la trasforma in una query. Ne derivano due conseguenze:

  • Una regola si può condividere, rivedere e versionare come testo, come fai con il codice.
  • La stessa regola si può provare su eventi di esempio prima che arrivi in un SIEM di produzione.

Questo articolo parla del secondo punto. Una regola Sigma che passa il parsing e si converte non è finita: è solo sintatticamente corretta. Diventa utile quando puoi dimostrare che scatta sugli eventi giusti e resta muta su quelli da ignorare.

Le parti di una regola

Questa è la regola che costruirai nel lab, con la spiegazione di ogni campo.

yaml
title: Failed Logon Event
id: bd37e49d-15ab-4e88-a3fb-53594e831b27
status: experimental
description: Detects Windows Security event 4625 (an account failed to log on).
logsource:
    product: windows
    service: security
detection:
    selection:
        EventID: 4625
    condition: selection
falsepositives:
    - Users mistyping passwords
level: low
tags:
    - attack.credential-access
    - attack.t1110

Il tuo UUID sarà diverso. Generane uno con una qualsiasi libreria UUID, per esempio python -c "import uuid; print(uuid.uuid4())", e conservalo quando modifichi la regola in seguito. L'id è il modo in cui gli altri tool seguono la stessa regola da una versione all'altra.

  • title è un nome breve, pensato per le persone. Compare negli alert e negli elenchi di regole.
  • id è un UUID che identifica la regola, non il contenuto. Non cambiarlo quando modifichi la logica.
  • status dice a chi legge quanta fiducia dare alla regola. I valori ammessi sono stable, test, experimental, deprecated e unsupported. Una regola nuova, mai provata in un ambiente reale, va sotto experimental.
  • description dice che cosa cerca la regola, in una o due frasi.
  • logsource dice da dove arrivano gli eventi. product: windows e service: security puntano al registro eventi Security di Windows. Altri campi, come category, ti permettono di descrivere una classe di log, per esempio la creazione di processi, senza nominare il canale esatto.
  • detection contiene la logica di corrispondenza. Ogni blocco con un nome, qui selection, è un insieme di condizioni sui campi. I nomi dei campi sono quelli della sorgente dei log, e i valori si confrontano con quei campi.
  • condition dice come si combinano i blocchi. selection da solo significa "gli eventi che corrispondono al blocco selection". Più avanti aggiungerai blocchi di filtro e li combinerai con and e not.
  • falsepositives elenca i casi innocui che corrisponderanno anche loro. Scriverli fa parte della regola, non è un'aggiunta da fare dopo.
  • level è la gravità attribuita alla regola quando scatta. I valori ammessi sono informational, low, medium, high e critical. Scegli il livello in base a che cosa significa un riscontro nel tuo ambiente, non a quanto sembra spettacolare la tecnica.
  • tags collega la regola ai framework. Il prefisso attack. punta a una tattica MITRE ATT&CK, e attack.t1110 punta alla tecnica T1110, Brute Force.

Due note su questa regola. Primo, EventID è il nome del campo nel mondo Sigma, e si riferisce all'Event ID che leggi in Event Viewer. Secondo, level: low è una scelta. Un singolo logon fallito è comune. Così com'è scritta, la regola scatta su ogni 4625, quindi un livello basso è il punto di partenza onesto finché non aggiungi una soglia.

Da Sigma al linguaggio di query di un SIEM

Sigma non conosce il tuo SIEM. Il backend sì. Il convertitore legge il rilevamento, applica una pipeline di elaborazione che rinomina i campi e aggiunge restrizioni di indice o di sorgente, e scrive una query nel linguaggio di destinazione.

Una pipeline di elaborazione è un insieme di regole di mappatura. Risponde a domande come: qual è il nome del campo Splunk per l'Event ID, quale indice contiene gli eventi Security di Windows, quale sourcetype deve cercare la query. Senza pipeline, un convertitore può restituire il nome del campo Sigma così com'è scritto. Spesso quell'output non è utilizzabile sulla piattaforma che usi.

Ogni backend ha le sue pipeline. Alcune sono obbligatorie, e il convertitore si rifiuta di continuare senza, a meno che tu non chieda esplicitamente di saltare la pipeline. Lo vedrai nel lab.

L'idea è semplice: la regola resta la stessa e la pipeline la adatta alla piattaforma. Quando passi da Splunk a un altro SIEM, cambi il backend e la pipeline, e il rilevamento resta com'è.

Lab pratico: scrivi, controlla e converti la regola

Il lab gira sulla tua macchina. Non ti servono un dominio, un domain controller né alcun sistema bersaglio. Stai scrivendo e convertendo una regola. Non stai generando logon falliti contro qualcosa, e non dovresti provarci contro un sistema che non è tuo o che non hai il permesso scritto di testare.

Questo lab è stato eseguito su Windows 11 con Git Bash, Python 3.12 e il registro Security predefinito di Windows. I comandi sono gli stessi su Linux e macOS, a parte il percorso dell'ambiente virtuale.

Passo 1: crea un ambiente virtuale e installa sigma-cli

Crea una cartella per il lab, poi esegui:

bash
python3 -m venv .venv
. .venv/bin/activate
pip install sigma-cli

Su Windows con Git Bash, python3 potrebbe non essere nel path. Usa il percorso completo della tua installazione di Python 3.12, oppure py -3.12. L'ambiente virtuale mette poi i suoi script in .venv/Scripts invece che in .venv/bin, quindi attivalo con:

bash
. .venv/Scripts/activate

Controlla l'installazione:

bash
sigma version

Sulla macchina usata per questo articolo, sigma-cli 4.0.0 si è installato con pysigma 2.0.2 e Python 3.12.10. Le tue versioni potrebbero essere più recenti. Nei tuoi appunti registra l'output della versione, non i numeri qui sopra.

Passo 2: scrivi la regola

Crea un file chiamato failed_logon.yml con il contenuto mostrato nella sezione precedente. Genera un UUID tuo per il campo id. Salva il file in UTF-8, senza tabulazioni nell'indentazione YAML.

Passo 3: valida la regola

bash
sigma check failed_logon.yml

sigma check analizza il file ed esegue i validatori. Trova un campo mancante, un tag malformato, uno status o un level sconosciuto e diversi problemi di stile.

Sulla macchina usata per questo articolo, il controllo non è arrivato in fondo. Il validatore dei tag carica i dati di MITRE ATT&CK dalla rete. La richiesta ha restituito un HTTP 504 gateway timeout, e l'errore si è ripetuto a ogni tentativo. L'output era un traceback che terminava con RuntimeError: Failed to load MITRE ATT&CK data, e il codice di uscita era 1.

Il problema è una dipendenza di rete, la regola non c'entra. Una volta caricati i dati dovresti vedere un esito positivo o un elenco di problemi dei validatori. Se il controllo fallisce con lo stesso messaggio su MITRE, rilancialo più tardi o da una rete che raggiunge i dati a monte. Non segnare la regola come validata finché il controllo non finisce.

Se il controllo segnala problemi, leggili uno per uno. Correggi la regola, non il validatore. Un problema su un tag di solito indica un refuso, come attack.t1100 al posto di attack.t1110.

Passo 4: installa il backend Splunk

bash
sigma plugin install splunk

Sulla macchina usata per questo articolo, il comando ha stampato Successfully installed plugin 'splunk' e pySigma version is compatible with sigma-cli.

Passo 5: converti senza pipeline

bash
sigma convert -t splunk --without-pipeline failed_logon.yml

L'output è stato:

text
Parsing Sigma rules
EventID=4625

Questa è la regola prima di qualsiasi mappatura per Splunk. Il nome del campo è ancora quello Sigma, EventID. Una ricerca Splunk sugli eventi Windows di solito usa EventCode e una source, quindi questa query non troverebbe gli eventi che vuoi su una configurazione standard con l'add-on per Windows.

Passo 6: converti con la pipeline Splunk

Prova la stessa conversione senza --without-pipeline:

bash
sigma convert -t splunk failed_logon.yml

Il convertitore si è fermato spiegando che il backend Splunk richiede una pipeline di elaborazione. Il messaggio diceva di aggiungere --without-pipeline solo se sai quello che stai facendo. Qui la pipeline è obbligatoria, quindi il convertitore si rifiuta di produrre una query senza. Leggi quel messaggio come una protezione.

Ora aggiungi la pipeline per Windows:

bash
sigma list pipelines
sigma convert -t splunk -p splunk_windows failed_logon.yml

L'elenco mostrava tre pipeline Splunk: splunk_windows, splunk_sysmon_acceleration e splunk_cim. La conversione con splunk_windows ha stampato:

text
Parsing Sigma rules
source="WinEventLog:Security" EventCode=4625

La pipeline ha cambiato due cose. Ha mappato EventID su EventCode e ha aggiunto una restrizione di source su WinEventLog:Security. Il risultato è una query che un'istanza Splunk con l'add-on per Windows può eseguire. Indice e sourcetype potrebbero essere diversi nel tuo ambiente, quindi prendila come punto di partenza e verificala sui tuoi dati.

Anche il tentativo con la pipeline CIM è fallito, con Rule type not yet supported by the Splunk data model CIM pipeline!. Non tutte le regole si convertono con ogni pipeline. Un fallimento del genere è un'informazione utile: la regola o il backend non si adatta alla mappatura che hai chiesto.

Passo 7: confronta con l'evento che hai generato

Nell'articolo sul registro eventi di Windows hai guardato un evento in Event Viewer. Riapri quell'evento e confrontalo con la query:

  • Il campo Event ID in Event Viewer dovrebbe essere 4625. È il valore che la selection di Sigma cerca.
  • In Event Viewer il nome del campo è Event ID. Nell'output di Splunk compare come EventCode, perché la pipeline lo mappa sul nome usato dall'add-on per Windows.
  • La source nella query, WinEventLog:Security, corrisponde al canale Security che hai letto in Event Viewer.

Se il tuo evento in Event Viewer ha un Event ID diverso da 4625, la regola non lo intercetta, ed è il comportamento corretto. La regola riguarda i logon falliti, non tutti gli eventi del registro.

Testa la regola

Una regola che si converte resta un'ipotesi finché non la provi. Il test minimo ha due metà:

  • Eventi known-bad. Eventi su cui la regola deve scattare. Per questa regola, gli eventi 4625.
  • Eventi known-good. Eventi su cui la regola non deve scattare. I logon riusciti (4624), i logoff (4634) e la creazione di processi (4688) sono buoni negativi.

Senza i negativi non hai prove che la regola sia specifica. Senza i positivi non hai prove che funzioni.

Per questo lab il test usa un piccolo file di fixture con eventi sintetici, events.json, con cinque voci: due eventi 4625 e tre eventi che non devono corrispondere. Lo script carica il blocco selection della regola e controlla ogni evento. È un test semplificato. Mostra che la logica della selection fa quello che hai scritto, e non sostituisce la prova della query convertita su dati reali.

python
import json, sys
import yaml

rule = yaml.safe_load(open("failed_logon.yml", encoding="utf-8"))
events = json.load(open("events.json", encoding="utf-8"))

selection = rule["detection"]["selection"]

def selection_matches(event):
    return all(str(event.get(k)) == str(v) for k, v in selection.items())

failures = 0
for ev in events:
    got = "match" if selection_matches(ev) else "no match"
    ok = got == ev["expect"]
    failures += 0 if ok else 1
    print(f"{'PASS' if ok else 'FAIL'}  {ev['name']:<40} expected={ev['expect']:<9} got={got}")

print(f"{len(events) - failures}/{len(events)} passed")
sys.exit(1 if failures else 0)

Il file di fixture ha questo aspetto:

json
[
  {"name": "known-bad: failed logon", "EventID": 4625, "expect": "match"},
  {"name": "known-bad: failed logon, second try", "EventID": 4625, "expect": "match"},
  {"name": "known-good: successful logon", "EventID": 4624, "expect": "no match"},
  {"name": "known-good: logoff", "EventID": 4634, "expect": "no match"},
  {"name": "known-good: process creation", "EventID": 4688, "expect": "no match"}
]

Eseguilo:

bash
python test_failed_logon.py

Sulla macchina usata per questo articolo, l'output è stato:

text
PASS  known-bad: failed logon                  expected=match     got=match
PASS  known-bad: failed logon, second try      expected=match     got=match
PASS  known-good: successful logon             expected=no match  got=no match
PASS  known-good: logoff                       expected=no match  got=no match
PASS  known-good: process creation             expected=no match  got=no match
5/5 passed

Nota che cosa mostra questo test e che cosa no. Mostra che la regola corrisponde agli eventi giusti e ignora quelli da ignorare. Non mostra che la regola sia buona. Una regola che intercettasse ogni 4625 passerebbe questo test, perché ogni 4625 nella fixture deve corrispondere. Il test è utile, ma solo come soglia minima.

Il passo successivo, in un ambiente reale, è eseguire la query Splunk convertita su un periodo di log Security di Windows che sai contenere logon falliti e attività ordinaria. Conta i risultati e aprine un campione. Ogni riscontro dovrebbe essere un evento 4625. Se la query restituisce riscontri per altri Event ID, la pipeline o la mappatura dei campi è sbagliata.

La regola non è finita

Questa regola scatta su ogni 4625. Una sola password digitata male produce un riscontro, e lo stesso fa un account di servizio con la password scaduta che riprova ogni minuto. Un tentativo di brute force è una serie di fallimenti sullo stesso account in poco tempo, e la prima versione non può esprimerlo.

Sigma gestisce questo caso con le regole di correlazione, definite in una specifica separata. Una regola di correlazione non intercetta eventi da sola. Punta a una o più regole di base tramite name, raggruppa le loro corrispondenze per un campo e applica un conteggio su una finestra temporale. Il tipo event_count conta le corrispondenze, e condition: gte: 5 significa cinque o più. Metti entrambe le regole in un solo file, separate da ---, e dai un name alla regola di base:

yaml
title: Failed Logon Event
name: failed_logon_event
id: bd37e49d-15ab-4e88-a3fb-53594e831b27
status: experimental
description: Detects Windows Security event 4625 (an account failed to log on).
logsource:
    product: windows
    service: security
detection:
    selection:
        EventID: 4625
    condition: selection
falsepositives:
    - Users mistyping passwords
level: low
tags:
    - attack.credential-access
    - attack.t1110
---
title: Many Failed Logons For One Account
id: 3f6c1d52-8a0e-4b7e-9d2a-6c41e0b7a915
status: experimental
description: Five or more failed logons for the same target account within ten minutes.
correlation:
    type: event_count
    rules:
        - failed_logon_event
    group-by:
        - TargetUserName
    timespan: 10m
    condition:
        gte: 5
falsepositives:
    - Service accounts with an expired password
level: medium

Salvalo come multi.yml e convertilo con la stessa pipeline:

bash
sigma convert -t splunk -p splunk_windows multi.yml

L'output è stato:

text
Parsing Sigma rules
source="WinEventLog:Security" EventCode=4625

| bin _time span=10m
| stats count as event_count by _time TargetUserName

| search event_count >= 5

La prima riga è la regola di base. Le tre righe successive sono la correlazione: raggruppa gli eventi in finestre di dieci minuti, li conta per TargetUserName, tiene i gruppi con cinque o più. I valori 5 e 10 minuti sono segnaposto. Sceglili in base ai tuoi dati e prova il risultato con gli stessi eventi known-good e known-bad. Non tutti i backend gestiscono tutti i tipi di correlazione, quindi controlla l'output del convertitore prima di farci affidamento.

Conserva la prima versione della regola. È la baseline, e il confronto con la versione correlata mostra se la soglia aiuta.

Errori comuni

  • Scrivere una regola e saltare i negativi. Una regola con soli test positivi può corrispondere a tutto e passare lo stesso. Aggiungi di proposito eventi known-good.
  • Chiamare il campo come l'etichetta di Event Viewer. Sigma usa EventID. Splunk usa EventCode dopo la pipeline. Mescolare i due nomi in una regola provoca mancate corrispondenze silenziose.
  • Convertire senza pipeline e fidarsi dell'output. --without-pipeline è un flag da usare di proposito, non una scorciatoia. L'output potrebbe non girare sulla tua piattaforma.
  • Usare level: high per una regola rumorosa. La gravità deve riflettere che cosa significa un riscontro. Un evento comune e senza contesto dovrebbe partire basso.
  • Dimenticare il campo falsepositives. Se non sai nominare un caso innocuo, probabilmente non hai ancora pensato abbastanza all'ambiente.
  • Cambiare l'id quando modifichi la logica. L'id deve identificare la regola nel tempo. Una regola nuova ha un id nuovo. Una regola modificata tiene il suo.
  • Prendere un controllo superato per una prova. sigma check verifica la struttura del file. Non verifica se il rilevamento trova gli eventi giusti.

Per andare oltre

Quando hai una regola che supera il controllo, si converte e passa i test, i passi successivi sono semplici:

  • Calibra la regola di correlazione su dati reali: cambia la soglia e la finestra temporale, poi confronta il numero di riscontri e i falsi positivi con la regola di base.
  • Leggi il resto della specifica Sigma per vedere i filtri, i modificatori come |contains e |endswith, e il campo category.
  • Converti la stessa regola per un secondo backend, come Elastic o Splunk SPL2, e confronta le query. Annota dove la pipeline cambia la logica e dove rinomina soltanto i campi.
  • Costruisci un set di fixture più grande, con eventi provenienti da un lab in cui controlli l'attività. Genera logon falliti solo su account e sistemi tuoi, e solo in un lab che hai il permesso di testare.

La versione breve: una regola è un'affermazione sugli eventi. Scrivi l'affermazione, convertila e poi dimostrala con eventi che devono corrispondere ed eventi che non devono.

Fonti

Fonti

  1. SigmaHQ, Sigma specification
  2. SigmaHQ, Sigma rules documentation
  3. SigmaHQ, sigma-cli
  4. MITRE ATT&CK, T1110 Brute Force
  5. SigmaHQ, Sigma correlation rules specification

Altro su Rilevamento e blue team

Rilevamento e blue team

Che cos'è un SIEM? Log, query e alert

Un SIEM raccoglie i log, li normalizza, applica regole di correlazione e genera alert per il triage. Segui la pipeline con un esempio che puoi eseguire.

Base14 min di lettura