From 37c28f56a717cbd916adc4311692c4e5a4a98d20 Mon Sep 17 00:00:00 2001 From: DastInDark <2350416+hitenkoku@users.noreply.github.com> Date: Fri, 16 Aug 2024 12:56:55 +0900 Subject: [PATCH 1/5] feat(matchers): added exists modifier support #1397 --- src/detections/rule/matchers.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/detections/rule/matchers.rs b/src/detections/rule/matchers.rs index acf4643db..9295ae024 100644 --- a/src/detections/rule/matchers.rs +++ b/src/detections/rule/matchers.rs @@ -560,9 +560,9 @@ impl LeafMatcher for DefaultMatcher { fn is_match(&self, event_value: Option<&String>, recinfo: &EvtxRecordInfo) -> bool { let pipe: &PipeElement = self.pipes.first().unwrap_or(&PipeElement::Wildcard); let match_result = match pipe { - PipeElement::EqualsField(_) | PipeElement::Endswithfield(_) => { - Some(pipe.is_eqfield_match(event_value, recinfo)) - } + PipeElement::Exists(..) + | PipeElement::EqualsField(_) + | PipeElement::Endswithfield(_) => Some(pipe.is_eqfield_match(event_value, recinfo)), PipeElement::Cidr(ip_result) => match ip_result { Ok(matcher_ip) => { let val = String::default(); @@ -665,6 +665,7 @@ enum PipeElement { Contains, Re, Wildcard, + Exists(String, String), EqualsField(String), Endswithfield(String), Base64offset, @@ -681,6 +682,7 @@ impl PipeElement { "endswith" => Option::Some(PipeElement::Endswith), "contains" => Option::Some(PipeElement::Contains), "re" => Option::Some(PipeElement::Re), + "exists" => Option::Some(PipeElement::Exists(key_list[0].split('|').collect::>()[0].to_string(), pattern.to_string())), "equalsfield" => Option::Some(PipeElement::EqualsField(pattern.to_string())), "endswithfield" => Option::Some(PipeElement::Endswithfield(pattern.to_string())), "base64offset" => Option::Some(PipeElement::Base64offset), @@ -711,6 +713,9 @@ impl PipeElement { fn is_eqfield_match(&self, event_value: Option<&String>, recinfo: &EvtxRecordInfo) -> bool { match self { + PipeElement::Exists(eq_key, val) => { + val.to_lowercase() == recinfo.get_value(eq_key).is_some().to_string() + }, PipeElement::EqualsField(eq_key) => { let eq_value = recinfo.get_value(eq_key); // Evtxのレコードに存在しないeventkeyを指定された場合はfalseにする From 79492b583cd44f743ee9244ee069456dd8ce22ca Mon Sep 17 00:00:00 2001 From: DastInDark <2350416+hitenkoku@users.noreply.github.com> Date: Fri, 16 Aug 2024 12:57:36 +0900 Subject: [PATCH 2/5] test(matchers): added exists modifier test #1397 --- src/detections/rule/matchers.rs | 79 +++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/detections/rule/matchers.rs b/src/detections/rule/matchers.rs index 9295ae024..e67f1d6e8 100644 --- a/src/detections/rule/matchers.rs +++ b/src/detections/rule/matchers.rs @@ -3252,4 +3252,83 @@ mod tests { false, ); } + + #[test] + fn test_exists_true() { + let rule_str = r#" + enabled: true + detection: + selection1: + Channel|exists: true + condition: selection1 + "#; + + let record_json_str = r#" + { + "Event": { + "System": { + "EventID": 1, + "Channel": "Microsoft-Windows-Sysmon/Operational" + }, + "EventData": { + "CurrentDirectory": "C:\\Windows\\system32\\" + } + } + }"#; + + check_select(rule_str, record_json_str, true); + } + + #[test] + fn test_exists_null_true() { + let rule_str = r#" + enabled: true + detection: + selection1: + Channel|exists: true + condition: selection1 + "#; + + let record_json_str = r#" + { + "Event": { + "System": { + "EventID": 1, + "Channel": "" + }, + "EventData": { + "CurrentDirectory": "C:\\Windows\\system32\\" + } + } + }"#; + + check_select(rule_str, record_json_str, true); + } + + #[test] + fn test_exists_false() { + let rule_str = r#" + enabled: true + detection: + selection1: + Dummy|exists: false + condition: selection1 + "#; + + let record_json_str = r#" + { + "Event": { + "System": { + "EventID": 1, + "Channel": "" + }, + "EventData": { + "CurrentDirectory": "C:\\Windows\\system32\\" + } + } + }"#; + + check_select(rule_str, record_json_str, true); + } + } From 9cd267465724b20b9b5715cdecd627ec3d2cbc90 Mon Sep 17 00:00:00 2001 From: DastInDark <2350416+hitenkoku@users.noreply.github.com> Date: Fri, 16 Aug 2024 13:00:37 +0900 Subject: [PATCH 3/5] style: cargo fmt --- src/detections/rule/matchers.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/detections/rule/matchers.rs b/src/detections/rule/matchers.rs index e67f1d6e8..9679013fe 100644 --- a/src/detections/rule/matchers.rs +++ b/src/detections/rule/matchers.rs @@ -682,7 +682,10 @@ impl PipeElement { "endswith" => Option::Some(PipeElement::Endswith), "contains" => Option::Some(PipeElement::Contains), "re" => Option::Some(PipeElement::Re), - "exists" => Option::Some(PipeElement::Exists(key_list[0].split('|').collect::>()[0].to_string(), pattern.to_string())), + "exists" => Option::Some(PipeElement::Exists( + key_list[0].split('|').collect::>()[0].to_string(), + pattern.to_string(), + )), "equalsfield" => Option::Some(PipeElement::EqualsField(pattern.to_string())), "endswithfield" => Option::Some(PipeElement::Endswithfield(pattern.to_string())), "base64offset" => Option::Some(PipeElement::Base64offset), @@ -715,7 +718,7 @@ impl PipeElement { match self { PipeElement::Exists(eq_key, val) => { val.to_lowercase() == recinfo.get_value(eq_key).is_some().to_string() - }, + } PipeElement::EqualsField(eq_key) => { let eq_value = recinfo.get_value(eq_key); // Evtxのレコードに存在しないeventkeyを指定された場合はfalseにする @@ -3330,5 +3333,4 @@ mod tests { check_select(rule_str, record_json_str, true); } - } From adec0640f94412a879467db372939fb454fe7b0a Mon Sep 17 00:00:00 2001 From: DastInDark <2350416+hitenkoku@users.noreply.github.com> Date: Fri, 16 Aug 2024 15:49:54 +0900 Subject: [PATCH 4/5] test: fixed test #1397 --- src/detections/rule/matchers.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/detections/rule/matchers.rs b/src/detections/rule/matchers.rs index 9d79b5b29..5cb500902 100644 --- a/src/detections/rule/matchers.rs +++ b/src/detections/rule/matchers.rs @@ -3304,6 +3304,11 @@ mod tests { "CurrentDirectory": "C:\\Windows\\system32\\" } } + }"#; + check_select(rule_str, record_json_str, true); + } + + #[test] fn test_re_caseinsensitive_detect() { let rule_str = r#" enabled: true @@ -3342,6 +3347,11 @@ mod tests { "CurrentDirectory": "C:\\Windows\\system32\\" } } + }"#; + check_select(rule_str, record_json_str, true); + } + + #[test] fn test_re_multiline_detect() { let rule_str = r#" enabled: true @@ -3380,6 +3390,11 @@ mod tests { "CurrentDirectory": "C:\\Windows\\system32\\" } } + }"#; + check_select(rule_str, record_json_str, true); + } + + #[test] fn test_re_singleline_detect() { let rule_str = r#" enabled: true From 8a73c5719d7363ffe9c901f8c4455b2ee31daf31 Mon Sep 17 00:00:00 2001 From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> Date: Fri, 16 Aug 2024 18:33:52 +0900 Subject: [PATCH 5/5] update changelog --- CHANGELOG-Japanese.md | 1 + CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG-Japanese.md b/CHANGELOG-Japanese.md index e5c9f191d..6451c695d 100644 --- a/CHANGELOG-Japanese.md +++ b/CHANGELOG-Japanese.md @@ -9,6 +9,7 @@ * `i`: (insensitive) 大文字小文字を区別しないマッチングを無効にする。 * `m`: (multi-line) 複数行にまたがってマッチする。`^` /`$` は行頭/行末にマッチする。 * `s`: (single-line) ドット文字 (`.`) は改行文字を含むすべての文字にマッチする。 +- Sigma V2の`|exists`モディファイアに対応した。 (#1400) (@hitenkoku) **改善:** diff --git a/CHANGELOG.md b/CHANGELOG.md index d8b21535f..5f3429f45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * `i`: (insensitive) disable case-sensitive matching. * `m`: (multi-line) match across multiple lines. `^` /`$` match the start/end of line. * `s`: (single-line) the dot character (`.`) matches all characters, including the newline character. +- Support for the Sigma V2 `|exists` modifier. (#1400) (@hitenkoku) **Enhancements:**