Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion condition_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import (
var (
searchExprLexer = lexer.Must(lexer.Regexp(`(?P<Keyword>(?i)(1 of them)|(all of them)|(1 of)|(all of))` +
`|(?P<SearchIdentifierPattern>\*?[a-zA-Z_]+\*[a-zA-Z0-9_*]*)` +
`|(?P<SearchIdentifier>[a-zA-Z_][a-zA-Z0-9_]*)` +
// `|(?P<SearchIdentifier>[a-zA-Z_][a-zA-Z0-9_]*)` +
`|(?P<SearchIdentifier>[a-zA-Z_][a-zA-Z0-9_.]*)` +
`|(?P<Operator>(?i)and|or|not|[()])` + // TODO: this never actually matches anything because they get matched as a SearchIdentifier instead. However this isn't currently a problem because we don't parse anything in the Grammar as an Operator (we just use string constants which don't care about Operator vs SearchIdentifier)
`|(?P<ComparisonOperation>=|!=|<=|>=|<|>)` +
`|(?P<ComparisonValue>0|[1-9][0-9]*)` +
Expand Down
57 changes: 56 additions & 1 deletion evaluator/evaluate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"

"github.com/bradleyjkemp/sigma-go"
"github.com/bradleyjkemp/sigma-go/evaluator/modifiers"
)
Expand Down Expand Up @@ -85,12 +86,66 @@ type Result struct {
// Event should be some form a map[string]interface{} or map[string]string
type Event interface{}

func flattenJSON(data interface{}, prefix string, result map[string]interface{}) {
switch v := data.(type) {
case map[string]interface{}:
for k, val := range v {
newKey := k
if prefix != "" {
newKey = prefix + "." + k
}
flattenJSON(val, newKey, result)
}

case []interface{}:
if len(v) == 0 {
result[prefix] = v
return
}

if _, isMap := v[0].(map[string]interface{}); isMap {
fieldsMap := make(map[string][]interface{})
for _, item := range v {
if mapItem, ok := item.(map[string]interface{}); ok {
for k, val := range mapItem {
key := prefix + "." + k
fieldsMap[key] = append(fieldsMap[key], val)
}
}
}
for k, val := range fieldsMap {
result[k] = val
}
} else {
result[prefix] = v
}

case string:
switch v {
case "False", "false":
result[prefix] = false
case "True", "true":
result[prefix] = true
default:
result[prefix] = v
}
default:
result[prefix] = v
}
}

func getNestedValue(data map[string]interface{}, path string) interface{} {
result := make(map[string]interface{})
flattenJSON(data, "", result)
return result[path]
}

func eventValue(e Event, key string) interface{} {
switch evt := e.(type) {
case map[string]string:
return evt[key]
case map[string]interface{}:
return evt[key]
return getNestedValue(evt, key)
default:
return ""
}
Expand Down
10 changes: 6 additions & 4 deletions evaluator/evaluate_search.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/PaesslerAG/jsonpath"
"github.com/bradleyjkemp/sigma-go"
"github.com/bradleyjkemp/sigma-go/evaluator/modifiers"
"path"
"reflect"
"regexp"
"strings"

"github.com/PaesslerAG/jsonpath"
"github.com/bradleyjkemp/sigma-go"
"github.com/bradleyjkemp/sigma-go/evaluator/modifiers"
)

func (rule RuleEvaluator) evaluateSearchExpression(search sigma.SearchExpr, searchResults func(string) bool) bool {
Expand Down Expand Up @@ -174,7 +175,8 @@ func (rule *RuleEvaluator) GetFieldValuesFromEvent(field string, event Event) ([
var actualValues []interface{}
if len(rule.fieldmappings[field]) == 0 {
// No FieldMapping exists so use the name directly from the rule
actualValues = []interface{}{eventValue(event, field)}
// actualValues = []interface{}{eventValue(event, field)}
actualValues = toGenericSlice(eventValue(event, field))
} else {
// FieldMapping does exist so check each of the possible mapped names instead of the name from the rule
for _, mapping := range rule.fieldmappings[field] {
Expand Down
7 changes: 6 additions & 1 deletion rule_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,14 @@ func (d *Detection) UnmarshalYAML(node *yaml.Node) error {
return err
}
case "timeframe":
if err := node.Decode(&d.Timeframe); err != nil {
if t, err := time.ParseDuration(value.Value); err != nil {
return err
} else {
d.Timeframe = t
}
// if err := node.Decode(&d.Timeframe); err != nil {
// return err
// }
default:
search := Search{}
if err := search.UnmarshalYAML(value); err != nil {
Expand Down
29 changes: 28 additions & 1 deletion rule_parser_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package sigma

import (
"github.com/google/go-cmp/cmp/cmpopts"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"

"github.com/google/go-cmp/cmp/cmpopts"

"github.com/bradleyjkemp/cupaloy/v2"
"github.com/google/go-cmp/cmp"
"gopkg.in/yaml.v3"
Expand Down Expand Up @@ -39,6 +40,32 @@ func TestParseRule(t *testing.T) {
}
}

func TestParseRule2(t *testing.T) {
err := filepath.Walk("./testdata/", func(path string, info os.FileInfo, err error) error {
if !strings.HasSuffix(path, "aws_enum_backup.rule.yml") {
return nil
}

t.Run(strings.TrimSuffix(filepath.Base(path), "aws_enum_backup.rule.yml"), func(t *testing.T) {
contents, err := ioutil.ReadFile(path)
if err != nil {
t.Fatalf("failed reading test input: %v", err)
}

rule, err := ParseRule(contents)
if err != nil {
t.Fatalf("error parsing rule: %v", err)
}

cupaloy.New(cupaloy.SnapshotSubdirectory("testdata")).SnapshotT(t, rule)
})
return nil
})
if err != nil {
t.Fatal(err)
}
}

func TestMarshalRule(t *testing.T) {
err := filepath.Walk("./testdata/", func(path string, info os.FileInfo, err error) error {
if !strings.HasSuffix(path, ".rule.yml") {
Expand Down
35 changes: 35 additions & 0 deletions testdata/aws_enum_backup.rule.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copied from https://github.com/SigmaHQ/sigma/blob/b062d8ad650054cd20836d5ba38031090b8d8c33/unsupported/cloud/aws_enum_backup.yml#L29
# under license https://github.com/Neo23x0/sigma/blob/master/LICENSE.Detection.Rules.md
title: Potential Backup Enumeration on AWS
id: 76255e09-755e-4675-8b6b-dbce9842cd2a
status: unsupported
description: Detects potential enumeration activity targeting an AWS instance backups
references:
- https://unit42.paloaltonetworks.com/compromised-cloud-compute-credentials/
author: Janantha Marasinghe
date: 2022/12/13
modified: 2023/03/24
tags:
- attack.discovery
- attack.t1580
logsource:
product: aws
service: cloudtrail
detection:
selection:
eventSource: 'ec2.amazonaws.com'
eventName:
- 'GetPasswordData'
- 'GetEbsEncryptionByDefault'
- 'GetEbsDefaultKmsKeyId'
- 'GetBucketReplication'
- 'DescribeVolumes'
- 'DescribeVolumesModifications'
- 'DescribeSnapshotAttribute'
- 'DescribeSnapshotTierStatus'
- 'DescribeImages'
timeframe: 10m
condition: selection | count() > 5
falsepositives:
- Unknown
level: medium