sigma

package module
v0.0.0-...-4e5df12 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 15, 2026 License: MIT Imports: 8 Imported by: 0

README

sigma-go Build Status GitHub release

Mascot

A Go implementation and parser of Sigma rules. Useful for building your own detection pipelines.

Who's using sigma-go in production?

Usage

This library is designed for you to build your own alert systems. It exposes the ability to check whether a rule matches a given event but not much else. It's up to you to use this building block in your own detection pipeline.

A basic usage of this library might look like this:

import (
    sigma "github.com/doracpphp/sigma-go"
    "github.com/doracpphp/sigma-go/evaluator"
)

// You can load/create rules dynamically or use sigmac to load Sigma rule files
rule, _ := sigma.ParseRule(contents)

// Rules need to be wrapped in an evaluator.
// This is also where (if needed) you provide functions implementing the count, max, etc. aggregation functions
e := evaluator.ForRule(rule, options...)

// Get a stream of events from somewhere e.g. audit logs
for event := range events {
    result, err := e.Matches(ctx, event)
    if err != nil {
        // Handle the error (e.g. a malformed rule or unparseable field)
        continue
    }
    if result.Match {
        // Raise your alert here
        newAlert(rule.ID, rule.Description, ...)
    }
}

To evaluate many rules against each event efficiently, wrap them in a bundle with evaluator.ForRules(rules, options...); its Matches returns one result per matching rule.

Aggregation functions

If your Sigma rules make use of the count, max, min, or any other aggregation function in your conditions then you'll need some extra setup.

When creating an evaluator, you can pass in implementations of each of the aggregation functions:

e := evaluator.ForRule(rule,
    evaluator.CountImplementation(countImplementation),
    evaluator.MaxImplementation(maxImplementation),
)

The available option constructors are CountImplementation, CountDistinctImplementation, SumImplementation, AverageImplementation, MinImplementation, and MaxImplementation.

This repo includes some toy implementations in the evaluator/aggregators package (aggregators.InMemory(timeframe) returns a ready-made set of these options) but for production use cases you'll need to supply your own.

sigmac CLI: scan Windows .evtx logs

This repo ships a small command, sigmac, that scans Windows Event Log (.evtx) files against a set of Sigma rules and writes the matching events to CSV. It uses Velocidex/evtx to parse the logs.

go build -o sigmac ./sigmac

# Scan one or more evtx files against a rule directory, writing alerts to CSV
sigmac -rules ./rules -out alerts.csv Security.evtx System.evtx

Flags:

Flag Description
-rules Sigma rule file or directory (scanned recursively for .yml/.yaml). Required.
-config Optional Sigma config file (field mappings).
-out Output CSV path (default: stdout).
-timeframe Default sliding window for aggregation rules that don't specify their own (default 1h).
-channel-filter Only evaluate a rule against events from the channel its logsource targets (default true; faster and avoids cross-channel matches).
-exclude Comma-separated files of rule IDs to skip, in <uuid> # comment format. Accepts Hayabusa's exclude_rules.txt/noisy_rules.txt verbatim.

Event field values are normalised on ingest the way Event Viewer/Hayabusa present them — leading/trailing whitespace is trimmed (Windows pads fields like LogonProcessName) — and aggregation/correlation windows are keyed off each event's own timestamp, so count()/correlation results are correct when replaying historical logs.

Each output row is one (event, matching rule) pair with columns: timestamp, source_file, record_id, computer, channel, event_id, rule_id, rule_title, rule_level, rule_tags, event_json.

The evtx event is flattened into the field names Sigma Windows rules expect (EventID, Provider_Name, Channel, Computer, plus the EventData/UserData fields), and the full flattened event is preserved as JSON in the last column.

Detection rules (including count()/aggregation rules) are evaluated together as a single bundle; Sigma correlation rules are evaluated separately. Note that aggregation and correlation windows use wall-clock arrival time, so on historical replay (processing an old evtx all at once) those windowed results are approximate — single-event detection rules are unaffected.

Documentation

Index

Constants

View Source
const (
	CorrelationEventCount      = "event_count"
	CorrelationValueCount      = "value_count"
	CorrelationTemporal        = "temporal"
	CorrelationTemporalOrdered = "temporal_ordered"
)

Correlation rule types.

Variables

This section is empty.

Functions

func FuzzConditionParser

func FuzzConditionParser(data []byte) int

func FuzzConfigParser

func FuzzConfigParser(data []byte) int

func FuzzRuleParser

func FuzzRuleParser(data []byte) int

func ParseTimespan

func ParseTimespan(s string) (time.Duration, error)

ParseTimespan parses a Sigma timespan (a number followed by a single unit of s, m, h or d) into a time.Duration.

Types

type AggregationExpr

type AggregationExpr interface {
	// contains filtered or unexported methods
}

type AggregationFunc

type AggregationFunc interface {
	// contains filtered or unexported methods
}

type AllOfIdentifier

type AllOfIdentifier struct {
	Ident SearchIdentifier
}

type AllOfPattern

type AllOfPattern struct {
	Pattern string
}

type AllOfThem

type AllOfThem struct{}

type And

type And []SearchExpr

type Average

type Average struct {
	Field     string
	GroupedBy []string
}

type Comparison

type Comparison struct {
	Func      AggregationFunc
	Op        ComparisonOp
	Threshold float64
}

type ComparisonOp

type ComparisonOp string
var (
	Equal            ComparisonOp = "="
	NotEqual         ComparisonOp = "!="
	LessThan         ComparisonOp = "<"
	LessThanEqual    ComparisonOp = "<="
	GreaterThan      ComparisonOp = ">"
	GreaterThanEqual ComparisonOp = ">="
)

type Condition

type Condition struct {
	Search      SearchExpr      `yaml:",omitempty" json:",omitempty"`
	Aggregation AggregationExpr `yaml:",omitempty" json:",omitempty"`
	// contains filtered or unexported fields
}

func ParseCondition

func ParseCondition(input string) (Condition, error)

Parses the Sigma condition syntax

func (Condition) MarshalYAML

func (c Condition) MarshalYAML() (interface{}, error)

func (Condition) Position

func (c Condition) Position() (int, int)

Position returns the line and column of this Condition in the original input

type Conditions

type Conditions []Condition

func (Conditions) MarshalYAML

func (c Conditions) MarshalYAML() (interface{}, error)

Marshal the conditions back to grammar expressions :sob:

func (*Conditions) UnmarshalYAML

func (c *Conditions) UnmarshalYAML(node *yaml.Node) error

type Config

type Config struct {
	Title         string   // A short description of what this configuration does
	Order         int      // Defines the order of expansion when multiple config files are applicable
	Backends      []string // Lists the Sigma implementations that this config file is compatible with
	FieldMappings map[string]FieldMapping
	Logsources    map[string]LogsourceMapping
	// LogsourceMerging controls how the conditions contributed by matching logsource
	// mappings are combined: "and" (default) requires all to match, "or" requires any.
	LogsourceMerging LogsourceMerging         `yaml:"logsourcemerging,omitempty"`
	DefaultIndex     string                   // Defines a default index if no logsources match
	Placeholders     map[string][]interface{} // Defines values for placeholders that might appear in Sigma rules
}

func ParseConfig

func ParseConfig(contents []byte) (Config, error)

type Correlation

type Correlation struct {
	// Type is one of event_count, value_count, temporal, temporal_ordered.
	Type string `yaml:",omitempty" json:",omitempty"`

	// Rules references the correlated rules by their `name` or `id`.
	Rules []string `yaml:",omitempty" json:",omitempty"`

	// GroupBy lists the fields whose values partition events into independent
	// correlation buckets (parsed from the `group-by` key).
	GroupBy []string `yaml:"group-by,omitempty" json:"group-by,omitempty"`

	// Timespan is the sliding window over which the correlation is evaluated.
	Timespan Timespan `yaml:",omitempty" json:",omitempty"`

	// Aliases optionally maps a group-by alias to the concrete field name used by
	// each referenced rule: aliases[alias][ruleNameOrID] = fieldName.
	Aliases map[string]map[string]string `yaml:",omitempty" json:",omitempty"`

	// Condition holds the threshold for event_count / value_count correlations.
	Condition *CorrelationCondition `yaml:",omitempty" json:",omitempty"`
}

Correlation describes a Sigma correlation rule: a meta-rule that aggregates the matches of one or more referenced rules over a sliding time window.

See https://github.com/SigmaHQ/sigma-specification (Sigma Correlation Rules).

func (*Correlation) UnmarshalYAML

func (c *Correlation) UnmarshalYAML(node *yaml.Node) error

type CorrelationCondition

type CorrelationCondition struct {
	// Op is one of gt, gte, lt, lte, eq, neq.
	Op string
	// Count is the threshold the (event or value) count is compared against.
	Count int
	// Field is the field whose distinct values are counted (value_count only).
	Field string
}

CorrelationCondition is the threshold comparison for event_count / value_count correlation rules, e.g. `{gte: 10}` or, for value_count, `{gte: 100, field: User}`.

type Count

type Count struct {
	Field     string
	GroupedBy []string
}

type Detection

type Detection struct {
	Searches   map[string]Search `yaml:",inline" json:",inline"`
	Conditions Conditions        `yaml:"condition" json:"condition"`
	Timeframe  time.Duration     `yaml:",omitempty" json:",omitempty"`
}

func (*Detection) UnmarshalYAML

func (d *Detection) UnmarshalYAML(node *yaml.Node) error

type EventMatcher

type EventMatcher []FieldMatcher

func (EventMatcher) MarshalYAML

func (f EventMatcher) MarshalYAML() (interface{}, error)

func (*EventMatcher) UnmarshalYAML

func (f *EventMatcher) UnmarshalYAML(node *yaml.Node) error

type FieldMapping

type FieldMapping struct {
	TargetNames []string // The name(s) that appear in the events being matched

}

func (*FieldMapping) UnmarshalYAML

func (f *FieldMapping) UnmarshalYAML(value *yaml.Node) error

type FieldMatcher

type FieldMatcher struct {
	Field     string        `yaml:",omitempty" json:",omitempty"`
	Modifiers []string      `yaml:",omitempty" json:",omitempty"`
	Values    []interface{} `yaml:",omitempty" json:",omitempty"`
	// contains filtered or unexported fields
}

func (FieldMatcher) Position

func (f FieldMatcher) Position() (int, int)

Position returns the line and column of this FieldMatcher in the original input

type FileType

type FileType string
const (
	UnknownFile FileType = ""
	InvalidFile FileType = "invalid"
	RuleFile    FileType = "rule"
	ConfigFile  FileType = "config"
)

func InferFileType

func InferFileType(contents []byte) FileType

func (*FileType) UnmarshalYAML

func (f *FileType) UnmarshalYAML(node *yaml.Node) error

type Logsource

type Logsource struct {
	Category   string `yaml:",omitempty" json:",omitempty"`
	Product    string `yaml:",omitempty" json:",omitempty"`
	Service    string `yaml:",omitempty" json:",omitempty"`
	Definition string `yaml:",omitempty" json:",omitempty"`

	// Any non-standard fields will end up in here
	AdditionalFields map[string]interface{} `yaml:",inline,omitempty" json:",inline,omitempty"`
}

type LogsourceIndexes

type LogsourceIndexes []string

func (*LogsourceIndexes) UnmarshalYAML

func (i *LogsourceIndexes) UnmarshalYAML(value *yaml.Node) error

type LogsourceMapping

type LogsourceMapping struct {
	Logsource  `yaml:",inline"` // Matches the logsource field in Sigma rules
	Index      LogsourceIndexes // The index(es) that should be used
	Conditions Search           // Conditions that are added to all rules targeting this logsource
	Rewrite    Logsource        // Rewrites this logsource (i.e. so that it can be matched by another lower precedence config)
}

type LogsourceMerging

type LogsourceMerging string

LogsourceMerging selects how logsource-mapping conditions are combined.

const (
	LogsourceMergeAnd LogsourceMerging = "and"
	LogsourceMergeOr  LogsourceMerging = "or"
)

type Max

type Max struct {
	Field     string
	GroupedBy []string
}

type Min

type Min struct {
	Field     string
	GroupedBy []string
}

type Near

type Near struct {
	Condition SearchExpr
}

type Not

type Not struct {
	Expr SearchExpr
}

type OneOfIdentifier

type OneOfIdentifier struct {
	Ident SearchIdentifier
}

type OneOfPattern

type OneOfPattern struct {
	Pattern string
}

type OneOfThem

type OneOfThem struct{}

type Or

type Or []SearchExpr

type RelatedRule

type RelatedRule struct {
	ID   string
	Type string
}

type Rule

type Rule struct {
	// Required fields
	Title     string
	Logsource Logsource
	Detection Detection

	ID          string        `yaml:",omitempty" json:",omitempty"`
	Name        string        `yaml:",omitempty" json:",omitempty"`
	Related     []RelatedRule `yaml:",omitempty" json:",omitempty"`
	Status      string        `yaml:",omitempty" json:",omitempty"`
	Description string        `yaml:",omitempty" json:",omitempty"`
	Author      string        `yaml:",omitempty" json:",omitempty"`
	Level       string        `yaml:",omitempty" json:",omitempty"`
	References  []string      `yaml:",omitempty" json:",omitempty"`
	Tags        []string      `yaml:",omitempty" json:",omitempty"`

	// Correlation is set for Sigma correlation rules (documents with a top-level
	// `correlation:` key) and is nil for normal detection rules.
	Correlation *Correlation `yaml:",omitempty" json:",omitempty"`

	// Any non-standard fields will end up in here
	AdditionalFields map[string]interface{} `yaml:",inline,omitempty" json:",inline,omitempty"`
}

func ParseRule

func ParseRule(input []byte) (Rule, error)
type Search struct {
	Keywords      []string       `yaml:",omitempty" json:",omitempty"`
	EventMatchers []EventMatcher `yaml:",omitempty" json:",omitempty"`
	// contains filtered or unexported fields
}

func (Search) MarshalYAML

func (s Search) MarshalYAML() (interface{}, error)

func (Search) Position

func (s Search) Position() (int, int)

Position returns the line and column of this Search in the original input

func (*Search) UnmarshalYAML

func (s *Search) UnmarshalYAML(node *yaml.Node) error

type SearchExpr

type SearchExpr interface {
	// contains filtered or unexported methods
}

type SearchIdentifier

type SearchIdentifier struct {
	Name string
}

type Sum

type Sum struct {
	Field     string
	GroupedBy []string
}

type Timespan

type Timespan time.Duration

Timespan is a Sigma correlation timespan such as `30s`, `5m`, `2h`, `7d`. Unlike Go durations it supports a `d` (day) unit and a single unit suffix.

func (Timespan) Duration

func (t Timespan) Duration() time.Duration

func (*Timespan) UnmarshalYAML

func (t *Timespan) UnmarshalYAML(node *yaml.Node) error

Directories

Path Synopsis
internal
Command sigmac scans Windows .evtx event logs against a set of Sigma rules and writes the matching events to CSV.
Command sigmac scans Windows .evtx event logs against a set of Sigma rules and writes the matching events to CSV.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL