diff --git a/ast.go b/ast.go index 5509dea..1d728bb 100644 --- a/ast.go +++ b/ast.go @@ -304,6 +304,9 @@ func searchToAST(node interface{}) (SearchExpr, error) { case n.Identifer != nil: return SearchIdentifier{Name: *n.Identifer}, nil + case n.Pattern != nil: + return OneOfPattern{Pattern: *n.Pattern}, nil + case n.Subexpression != nil: return searchToAST(*n.Subexpression) diff --git a/condition_parser.go b/condition_parser.go index 6ac63a8..a5aad46 100644 --- a/condition_parser.go +++ b/condition_parser.go @@ -8,7 +8,7 @@ import ( var ( searchExprLexer = lexer.Must(lexer.Regexp(`(?P(?i)(1 of them)|(all of them)|(1 of)|(all of))` + - `|(?P\*?[a-zA-Z_]+\*[a-zA-Z0-9_*]*)` + + `|(?P\*?[a-zA-Z0-9_]+\*[a-zA-Z0-9_*]*)` + `|(?P[a-zA-Z_][a-zA-Z0-9_]*)` + `|(?P(?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=|!=|<=|>=|<|>)` + diff --git a/condition_parser_test.go b/condition_parser_test.go index f163c77..e17ec30 100644 --- a/condition_parser_test.go +++ b/condition_parser_test.go @@ -17,6 +17,7 @@ func TestParseCondition(t *testing.T) { {"a and b and c", Condition{Search: And{SearchIdentifier{"a"}, SearchIdentifier{"b"}, SearchIdentifier{"c"}}}}, {"a | count(b) > 0", Condition{Search: SearchIdentifier{"a"}, Aggregation: Comparison{Func: Count{Field: "b"}, Op: GreaterThan, Threshold: 0}}}, {"a | count(b) >= 0", Condition{Search: SearchIdentifier{"a"}, Aggregation: Comparison{Func: Count{Field: "b"}, Op: GreaterThanEqual, Threshold: 0}}}, + {"selection1*", Condition{Search: OneOfPattern{Pattern: "selection1*"}}}, {"note and pad", Condition{Search: And{SearchIdentifier{"note"}, SearchIdentifier{"pad"}}}}, } diff --git a/internal/grammar/search_expression.go b/internal/grammar/search_expression.go index a251d44..bd8ee7f 100644 --- a/internal/grammar/search_expression.go +++ b/internal/grammar/search_expression.go @@ -17,6 +17,7 @@ type Term struct { Negated *Term `"not" @@` OneAllOf *OneAllOf `| @@` Identifer *string `| @SearchIdentifier` + Pattern *string `| @SearchIdentifierPattern` Subexpression *Disjunction `| "(" @@ ")"` } diff --git a/testdata/proxy_apt40_with_regex_in_condition.rule.yml b/testdata/proxy_apt40_with_regex_in_condition.rule.yml new file mode 100644 index 0000000..35b9726 --- /dev/null +++ b/testdata/proxy_apt40_with_regex_in_condition.rule.yml @@ -0,0 +1,30 @@ +title: APT40 Dropbox Tool User Agent with digits in detection +id: 5ba715b6-71b7-44fd-8245-f66893e81b3d +status: experimental +description: Detects suspicious user agent string of APT40 Dropbox tool +references: + - Internal research from Florian Roth +author: Thomas Patzke +date: 2019/11/12 +modified: 2020/09/02 +tags: + - attack.command_and_control + - attack.t1071.001 + - attack.t1043 # an old one + - attack.exfiltration + - attack.t1567.002 + - attack.t1048 # an old one +logsource: + category: proxy +detection: + selection1_test: + c-useragent: 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36' + selection1test: + r-dns: 'api.dropbox.com' + condition: selection1* +fields: + - c-ip + - c-uri +falsepositives: + - Old browsers +level: high