From b2692ef9837f317931821f7e43242c0974d983fe Mon Sep 17 00:00:00 2001 From: itiB Date: Wed, 24 Nov 2021 00:09:41 +0900 Subject: [PATCH 01/19] Add: input function for start/end option --- src/detections/configs.rs | 2 ++ src/main.rs | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/detections/configs.rs b/src/detections/configs.rs index d3a8db51..e90ad72a 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -52,6 +52,8 @@ fn build_app<'a>() -> ArgMatches<'a> { --rfc-2822 'Output date and time in RFC 2822 format. Example: Mon, 07 Aug 2006 12:34:56 -0600' --rfc-3339 'Output date and time in RFC 3339 format. Example: 2006-08-07T12:34:56.485214 -06:00' --verbose 'Output check information to target event file path and rule file.' + --start-time=[STARTTIME] + --end-time=[ENDTIME] -q 'Quiet Output Logo' -r --rules=[RULEDIRECTORY] 'using target of rule file directory' -L --level=[LEVEL] 'Specified execute rule level(default: LOW)' diff --git a/src/main.rs b/src/main.rs index 9db674d1..4e1a1beb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -118,6 +118,44 @@ fn analysis_files(evtx_files: Vec) { .value_of("level") .unwrap_or("INFO") .to_uppercase(); + + // TODO: config.rs に移す + // ./target/debug/hayabusa -f ./test_files/evtx/test1.evtx --start-time 2014-11-28T12:00:09Z + let start_time= if let Some(s_time) = configs::CONFIG + .read() + .unwrap() + .args + .value_of("start-time") + { + match s_time.parse::>() { + Ok(dt)=> Some(dt), + Err(err) => { + AlertMessage::alert(&mut std::io::stderr().lock(), format!("start-time field: {}", err)).ok(); + None + } + } + } else { + None + }; + + let end_time= if let Some(e_time) = configs::CONFIG + .read() + .unwrap() + .args + .value_of("end-time") + { + match s_time.parse::>() { + Ok(dt)=> Some(dt), + Err(err) => { + AlertMessage::alert(&mut std::io::stderr().lock(), format!("start-time field: {}", err)).ok(); + None + } + } + } else { + None + }; + + println!("TIME: {:?}", start_time); println!("Analyzing Event Files: {:?}", evtx_files.len()); let rule_files = detection::Detection::parse_rule_files( level, From e09cfb7231f84e7131880efd51c424013b7f2b90 Mon Sep 17 00:00:00 2001 From: itiB Date: Tue, 7 Dec 2021 00:11:34 +0900 Subject: [PATCH 02/19] Add: datetime util --- src/detections/print.rs | 19 ++----------------- src/detections/utils.rs | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/detections/print.rs b/src/detections/print.rs index 51e65acb..22a15d8d 100644 --- a/src/detections/print.rs +++ b/src/detections/print.rs @@ -9,6 +9,7 @@ use std::collections::BTreeMap; use std::collections::HashMap; use std::io::{self, Write}; use std::sync::Mutex; +use crate::detections::utils; #[derive(Debug)] pub struct Message { @@ -174,23 +175,7 @@ impl Message { pub fn get_event_time(event_record: &Value) -> Option> { let system_time = &event_record["Event"]["System"]["TimeCreated_attributes"]["SystemTime"]; - let system_time_str = system_time.as_str().unwrap_or(""); - if system_time_str.is_empty() { - return Option::None; - } - - let rfc3339_time = DateTime::parse_from_rfc3339(system_time_str); - if rfc3339_time.is_err() { - return Option::None; - } - let datetime = Utc - .from_local_datetime(&rfc3339_time.unwrap().naive_utc()) - .single(); - if datetime.is_none() { - return Option::None; - } else { - return Option::Some(datetime.unwrap()); - } + return utils::str_time_to_datetime(system_time.as_str().unwrap_or("")); } /// message内のマップをクリアする。テストする際の冪等性の担保のため作成。 diff --git a/src/detections/utils.rs b/src/detections/utils.rs index 9df91ff7..bde46871 100644 --- a/src/detections/utils.rs +++ b/src/detections/utils.rs @@ -14,6 +14,7 @@ use std::io::prelude::*; use std::io::{BufRead, BufReader}; use std::str; use std::string::String; +use chrono::{DateTime, TimeZone, Utc}; pub fn concat_selection_key(key_list: &Vec) -> String { return key_list @@ -93,6 +94,29 @@ pub fn get_event_id_key() -> String { return "Event.System.EventID".to_string(); } +pub fn get_event_time() -> String { + return "Event.System.TimeCreated_attributes.SystemTime".to_string(); +} + +pub fn str_time_to_datetime(system_time_str: &str) -> Option> { + if system_time_str.is_empty() { + return Option::None; + } + + let rfc3339_time = DateTime::parse_from_rfc3339(system_time_str); + if rfc3339_time.is_err() { + return Option::None; + } + let datetime = Utc + .from_local_datetime(&rfc3339_time.unwrap().naive_utc()) + .single(); + if datetime.is_none() { + return Option::None; + } else { + return Option::Some(datetime.unwrap()); + } +} + /// serde:Valueの型を確認し、文字列を返します。 pub fn get_serde_number_to_string(value: &serde_json::Value) -> Option { if value.is_string() { From 4bb445d4f58515a5bf9e6cce991107c624c3992c Mon Sep 17 00:00:00 2001 From: itiB Date: Tue, 7 Dec 2021 00:50:00 +0900 Subject: [PATCH 03/19] Add: time filter --- src/detections/configs.rs | 64 +++++++++++++++++++++++++++++++ src/detections/print.rs | 2 +- src/detections/utils.rs | 2 +- src/main.rs | 79 ++++++++++++++++++++++----------------- 4 files changed, 110 insertions(+), 37 deletions(-) diff --git a/src/detections/configs.rs b/src/detections/configs.rs index ef64bd99..4e79eb35 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -1,4 +1,6 @@ +use crate::detections::print::AlertMessage; use crate::detections::utils; +use chrono::{DateTime, Utc}; use clap::{App, AppSettings, ArgMatches}; use lazy_static::lazy_static; use std::collections::{HashMap, HashSet}; @@ -118,6 +120,68 @@ fn load_target_ids(path: &str) -> TargetEventIds { return ret; } +#[derive(Debug, Clone)] +pub struct TargetEventTime { + start_time: Option>, + end_time: Option>, +} + +impl TargetEventTime { + pub fn new() -> TargetEventTime { + let start_time = if let Some(s_time) = CONFIG.read().unwrap().args.value_of("start-time") { + match s_time.parse::>() { + Ok(dt) => Some(dt), + Err(err) => { + AlertMessage::alert( + &mut std::io::stderr().lock(), + format!("start-time field: {}", err), + ) + .ok(); + None + } + } + } else { + None + }; + let end_time = if let Some(e_time) = CONFIG.read().unwrap().args.value_of("end-time") { + match e_time.parse::>() { + Ok(dt) => Some(dt), + Err(err) => { + AlertMessage::alert( + &mut std::io::stderr().lock(), + format!("start-time field: {}", err), + ) + .ok(); + None + } + } + } else { + None + }; + return TargetEventTime { + start_time: start_time, + end_time: end_time, + }; + } + + pub fn is_target(&self, eventtime: &Option>) -> bool { + if eventtime.is_none() { + return true; + } + if let Some(starttime) = self.start_time { + if eventtime.unwrap() < starttime { + return false; + } + } + if let Some(endtime) = self.end_time { + if eventtime.unwrap() > endtime { + return false; + } + } + return true; + } +} + #[derive(Debug, Clone)] pub struct EventKeyAliasConfig { key_to_eventkey: HashMap, diff --git a/src/detections/print.rs b/src/detections/print.rs index 22a15d8d..26e05046 100644 --- a/src/detections/print.rs +++ b/src/detections/print.rs @@ -1,5 +1,6 @@ extern crate lazy_static; use crate::detections::configs; +use crate::detections::utils; use crate::detections::utils::get_serde_number_to_string; use chrono::{DateTime, TimeZone, Utc}; use lazy_static::lazy_static; @@ -9,7 +10,6 @@ use std::collections::BTreeMap; use std::collections::HashMap; use std::io::{self, Write}; use std::sync::Mutex; -use crate::detections::utils; #[derive(Debug)] pub struct Message { diff --git a/src/detections/utils.rs b/src/detections/utils.rs index bde46871..5d0d52ff 100644 --- a/src/detections/utils.rs +++ b/src/detections/utils.rs @@ -7,6 +7,7 @@ use crate::detections::configs; use tokio::runtime::Builder; use tokio::runtime::Runtime; +use chrono::{DateTime, TimeZone, Utc}; use regex::Regex; use serde_json::Value; use std::fs::File; @@ -14,7 +15,6 @@ use std::io::prelude::*; use std::io::{BufRead, BufReader}; use std::str; use std::string::String; -use chrono::{DateTime, TimeZone, Utc}; pub fn concat_selection_key(key_list: &Vec) -> String { return key_list diff --git a/src/main.rs b/src/main.rs index f2cf0458..73efcd6b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -121,43 +121,42 @@ fn analysis_files(evtx_files: Vec) { .unwrap_or("informational") .to_uppercase(); - // TODO: config.rs に移す - // ./target/debug/hayabusa -f ./test_files/evtx/test1.evtx --start-time 2014-11-28T12:00:09Z - let start_time = if let Some(s_time) = configs::CONFIG - .read() - .unwrap() - .args - .value_of("start-time") - { - match s_time.parse::>() { - Ok(dt)=> Some(dt), - Err(err) => { - AlertMessage::alert(&mut std::io::stderr().lock(), format!("start-time field: {}", err)).ok(); - None - } - } - } else { - None - }; + // // TODO: config.rs に移す + // // ./target/debug/hayabusa -f ./test_files/evtx/test1.evtx --start-time 2014-11-28T12:00:09Z + // let start_time = + // if let Some(s_time) = configs::CONFIG.read().unwrap().args.value_of("start-time") { + // match s_time.parse::>() { + // Ok(dt) => Some(dt), + // Err(err) => { + // AlertMessage::alert( + // &mut std::io::stderr().lock(), + // format!("start-time field: {}", err), + // ) + // .ok(); + // None + // } + // } + // } else { + // None + // }; - let end_time= if let Some(e_time) = configs::CONFIG - .read() - .unwrap() - .args - .value_of("end-time") - { - match e_time.parse::>() { - Ok(dt)=> Some(dt), - Err(err) => { - AlertMessage::alert(&mut std::io::stderr().lock(), format!("start-time field: {}", err)).ok(); - None - } - } - } else { - None - }; + // let end_time = if let Some(e_time) = configs::CONFIG.read().unwrap().args.value_of("end-time") { + // match e_time.parse::>() { + // Ok(dt) => Some(dt), + // Err(err) => { + // AlertMessage::alert( + // &mut std::io::stderr().lock(), + // format!("start-time field: {}", err), + // ) + // .ok(); + // None + // } + // } + // } else { + // None + // }; - println!("TIME: {:?}", start_time); + // println!("TIME: {:?}", start_time); println!("Analyzing Event Files: {:?}", evtx_files.len()); let rule_files = detection::Detection::parse_rule_files( level, @@ -192,6 +191,8 @@ fn analysis_file( let mut records = parser.records_json_value(); let tokio_rt = utils::create_tokio_runtime(); + let target_event_time = configs::TargetEventTime::new(); + loop { let mut records_per_detect = vec![]; while records_per_detect.len() < MAX_DETECT_RECORDS { @@ -228,6 +229,14 @@ fn analysis_file( } } + let eventtime = utils::get_event_value(&utils::get_event_time(), &data); + if eventtime.is_some() { + let time = utils::str_time_to_datetime(eventtime.unwrap().as_str().unwrap_or("")); + if !target_event_time.is_target(&time) { + continue; + } + } + // EvtxRecordInfo構造体に変更 let data_string = data.to_string(); let record_info = EvtxRecordInfo::new((&filepath_disp).to_string(), data, data_string); From a1ec06cc6c511edf3ba065a00b670a07f0e6a091 Mon Sep 17 00:00:00 2001 From: itiB Date: Tue, 7 Dec 2021 00:52:57 +0900 Subject: [PATCH 04/19] rm: comments --- src/main.rs | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/src/main.rs b/src/main.rs index 73efcd6b..d9da8b1d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -121,42 +121,6 @@ fn analysis_files(evtx_files: Vec) { .unwrap_or("informational") .to_uppercase(); - // // TODO: config.rs に移す - // // ./target/debug/hayabusa -f ./test_files/evtx/test1.evtx --start-time 2014-11-28T12:00:09Z - // let start_time = - // if let Some(s_time) = configs::CONFIG.read().unwrap().args.value_of("start-time") { - // match s_time.parse::>() { - // Ok(dt) => Some(dt), - // Err(err) => { - // AlertMessage::alert( - // &mut std::io::stderr().lock(), - // format!("start-time field: {}", err), - // ) - // .ok(); - // None - // } - // } - // } else { - // None - // }; - - // let end_time = if let Some(e_time) = configs::CONFIG.read().unwrap().args.value_of("end-time") { - // match e_time.parse::>() { - // Ok(dt) => Some(dt), - // Err(err) => { - // AlertMessage::alert( - // &mut std::io::stderr().lock(), - // format!("start-time field: {}", err), - // ) - // .ok(); - // None - // } - // } - // } else { - // None - // }; - - // println!("TIME: {:?}", start_time); println!("Analyzing Event Files: {:?}", evtx_files.len()); let rule_files = detection::Detection::parse_rule_files( level, From 0e4136e9cf8be473ca9fd09704b07b2dab434e29 Mon Sep 17 00:00:00 2001 From: itiB Date: Tue, 7 Dec 2021 01:00:18 +0900 Subject: [PATCH 05/19] fix: option's documents --- src/detections/configs.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/detections/configs.rs b/src/detections/configs.rs index 4e79eb35..fd227bd5 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -56,8 +56,8 @@ fn build_app<'a>() -> ArgMatches<'a> { --rfc-2822 'Output date and time in RFC 2822 format. Example: Mon, 07 Aug 2006 12:34:56 -0600' --rfc-3339 'Output date and time in RFC 3339 format. Example: 2006-08-07T12:34:56.485214 -06:00' --verbose 'Output verbose information to target event file path and rule file' - --start-time=[STARTTIME] - --end-time=[ENDTIME] + --starttimeline=[STARTTIMELINE] 'Start time of the event to load from event file' + --endtimeline=[ENDTIMELINE]'End time of the event to load from event file' -q 'Quiet mode. Do not display the launch banner' -r --rules=[RULEDIRECTORY] 'Rule file directory (default: ./rules)' -L --level=[LEVEL] 'Minimum level for rules (default: INFORMATIONAL)' @@ -128,13 +128,14 @@ pub struct TargetEventTime { impl TargetEventTime { pub fn new() -> TargetEventTime { - let start_time = if let Some(s_time) = CONFIG.read().unwrap().args.value_of("start-time") { + let start_time = if let Some(s_time) = CONFIG.read().unwrap().args.value_of("starttimeline") + { match s_time.parse::>() { Ok(dt) => Some(dt), Err(err) => { AlertMessage::alert( &mut std::io::stderr().lock(), - format!("start-time field: {}", err), + format!("starttimeline field: {}", err), ) .ok(); None @@ -143,13 +144,13 @@ impl TargetEventTime { } else { None }; - let end_time = if let Some(e_time) = CONFIG.read().unwrap().args.value_of("end-time") { + let end_time = if let Some(e_time) = CONFIG.read().unwrap().args.value_of("endtimeline") { match e_time.parse::>() { Ok(dt) => Some(dt), Err(err) => { AlertMessage::alert( &mut std::io::stderr().lock(), - format!("start-time field: {}", err), + format!("endtimeline field: {}", err), ) .ok(); None From f8bd73898471640700f3b833f086ca37737e622c Mon Sep 17 00:00:00 2001 From: itiB Date: Tue, 7 Dec 2021 01:25:21 +0900 Subject: [PATCH 06/19] fix: input time format --- src/detections/configs.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/detections/configs.rs b/src/detections/configs.rs index fd227bd5..7dfc4b29 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -56,8 +56,8 @@ fn build_app<'a>() -> ArgMatches<'a> { --rfc-2822 'Output date and time in RFC 2822 format. Example: Mon, 07 Aug 2006 12:34:56 -0600' --rfc-3339 'Output date and time in RFC 3339 format. Example: 2006-08-07T12:34:56.485214 -06:00' --verbose 'Output verbose information to target event file path and rule file' - --starttimeline=[STARTTIMELINE] 'Start time of the event to load from event file' - --endtimeline=[ENDTIMELINE]'End time of the event to load from event file' + --starttimeline=[STARTTIMELINE] 'Start time of the event to load from event file. Example: '2018/11/28 12:00:00 +09:00'' + --endtimeline=[ENDTIMELINE]'End time of the event to load from event file. Example: '2018/11/28 12:00:00 +09:00'' -q 'Quiet mode. Do not display the launch banner' -r --rules=[RULEDIRECTORY] 'Rule file directory (default: ./rules)' -L --level=[LEVEL] 'Minimum level for rules (default: INFORMATIONAL)' @@ -130,8 +130,10 @@ impl TargetEventTime { pub fn new() -> TargetEventTime { let start_time = if let Some(s_time) = CONFIG.read().unwrap().args.value_of("starttimeline") { - match s_time.parse::>() { - Ok(dt) => Some(dt), + match DateTime::parse_from_str(s_time, "%Y-%m-%d %H:%M:%S %z") // 2014-11-28 21:00:09 +09:00 + .or_else(|_| DateTime::parse_from_str(s_time, "%Y/%m/%d %H:%M:%S %z")) // 2014/11/28 21:00:09 +09:00 + { + Ok(dt) => Some(dt.with_timezone(&Utc)), Err(err) => { AlertMessage::alert( &mut std::io::stderr().lock(), @@ -145,9 +147,11 @@ impl TargetEventTime { None }; let end_time = if let Some(e_time) = CONFIG.read().unwrap().args.value_of("endtimeline") { - match e_time.parse::>() { - Ok(dt) => Some(dt), - Err(err) => { + match DateTime::parse_from_str(e_time, "%Y-%m-%d %H:%M:%S %z") // 2014-11-28 21:00:09 +09:00 + .or_else(|_| DateTime::parse_from_str(e_time, "%Y/%m/%d %H:%M:%S %z")) // 2014/11/28 21:00:09 +09:00 + { + Ok(dt) => Some(dt.with_timezone(&Utc)), + Err(err) => { AlertMessage::alert( &mut std::io::stderr().lock(), format!("endtimeline field: {}", err), From 708305c95866d7eed68748cad18492b1fcd78e83 Mon Sep 17 00:00:00 2001 From: itiB Date: Sat, 11 Dec 2021 15:27:11 +0900 Subject: [PATCH 07/19] Add: TargetTimefilter testcase --- src/detections/configs.rs | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/detections/configs.rs b/src/detections/configs.rs index 7dfc4b29..aab7bd2d 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -127,7 +127,7 @@ pub struct TargetEventTime { } impl TargetEventTime { - pub fn new() -> TargetEventTime { + pub fn new() -> Self { let start_time = if let Some(s_time) = CONFIG.read().unwrap().args.value_of("starttimeline") { match DateTime::parse_from_str(s_time, "%Y-%m-%d %H:%M:%S %z") // 2014-11-28 21:00:09 +09:00 @@ -163,10 +163,14 @@ impl TargetEventTime { } else { None }; - return TargetEventTime { + return Self::set(start_time, end_time) + } + + pub fn set(start_time: Option>, end_time: Option>) -> Self { + return Self { start_time: start_time, - end_time: end_time, - }; + end_time: end_time + } } pub fn is_target(&self, eventtime: &Option>) -> bool { @@ -306,6 +310,7 @@ fn load_eventcode_info(path: &str) -> EventInfoConfig { mod tests { use crate::detections::configs; + use chrono::{DateTime, Utc}; #[test] #[ignore] @@ -324,4 +329,29 @@ mod tests { ); assert_eq!(message, display); } + + #[test] + fn target_event_time_filter() { + let start_time = Some("2018-02-20T12:00:09Z".parse::>().unwrap()); + let end_time = Some("2020-03-30T12:00:09Z".parse::>().unwrap()); + let time_filter = configs::TargetEventTime::set(start_time, end_time); + + let out_of_range1 = Some("1999-01-01T12:00:09Z".parse::>().unwrap()); + let within_range = Some("2019-02-27T01:05:01Z".parse::>().unwrap()); + let out_of_range2 = Some("2021-02-27T01:05:01Z".parse::>().unwrap()); + + assert_eq!(time_filter.is_target(&out_of_range1), false); + assert_eq!(time_filter.is_target(&within_range), true); + assert_eq!(time_filter.is_target(&out_of_range2), false); + } + + #[test] + fn target_event_time_filter_containes_on_time() { + let start_time = Some("2018-02-20T12:00:09Z".parse::>().unwrap()); + let end_time = Some("2020-03-30T12:00:09Z".parse::>().unwrap()); + let time_filter = configs::TargetEventTime::set(start_time, end_time); + + assert_eq!(time_filter.is_target(&start_time), true); + assert_eq!(time_filter.is_target(&end_time), true); + } } From 721bf993f7ecbe3d37adde6bf7687b23f003cca5 Mon Sep 17 00:00:00 2001 From: itiB Date: Sat, 11 Dec 2021 15:28:13 +0900 Subject: [PATCH 08/19] cargo fmt --all --- src/detections/configs.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/detections/configs.rs b/src/detections/configs.rs index aab7bd2d..da65ad8e 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -163,14 +163,17 @@ impl TargetEventTime { } else { None }; - return Self::set(start_time, end_time) + return Self::set(start_time, end_time); } - pub fn set(start_time: Option>, end_time: Option>) -> Self { + pub fn set( + start_time: Option>, + end_time: Option>, + ) -> Self { return Self { start_time: start_time, - end_time: end_time - } + end_time: end_time, + }; } pub fn is_target(&self, eventtime: &Option>) -> bool { @@ -332,12 +335,12 @@ mod tests { #[test] fn target_event_time_filter() { - let start_time = Some("2018-02-20T12:00:09Z".parse::>().unwrap()); - let end_time = Some("2020-03-30T12:00:09Z".parse::>().unwrap()); + let start_time = Some("2018-02-20T12:00:09Z".parse::>().unwrap()); + let end_time = Some("2020-03-30T12:00:09Z".parse::>().unwrap()); let time_filter = configs::TargetEventTime::set(start_time, end_time); let out_of_range1 = Some("1999-01-01T12:00:09Z".parse::>().unwrap()); - let within_range = Some("2019-02-27T01:05:01Z".parse::>().unwrap()); + let within_range = Some("2019-02-27T01:05:01Z".parse::>().unwrap()); let out_of_range2 = Some("2021-02-27T01:05:01Z".parse::>().unwrap()); assert_eq!(time_filter.is_target(&out_of_range1), false); @@ -347,8 +350,8 @@ mod tests { #[test] fn target_event_time_filter_containes_on_time() { - let start_time = Some("2018-02-20T12:00:09Z".parse::>().unwrap()); - let end_time = Some("2020-03-30T12:00:09Z".parse::>().unwrap()); + let start_time = Some("2018-02-20T12:00:09Z".parse::>().unwrap()); + let end_time = Some("2020-03-30T12:00:09Z".parse::>().unwrap()); let time_filter = configs::TargetEventTime::set(start_time, end_time); assert_eq!(time_filter.is_target(&start_time), true); From d1d77b4e9f52f3d2398997a493ab66c839d6dd17 Mon Sep 17 00:00:00 2001 From: itiB Date: Thu, 16 Dec 2021 20:14:31 +0900 Subject: [PATCH 09/19] cargo fmt --all --- src/detections/configs.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/detections/configs.rs b/src/detections/configs.rs index ceabc05a..c2fa589f 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -316,23 +316,23 @@ mod tests { use crate::detections::configs; use chrono::{DateTime, Utc}; -// #[test] -// #[ignore] -// fn singleton_read_and_write() { -// let message = -// "EventKeyAliasConfig { key_to_eventkey: {\"EventID\": \"Event.System.EventID\"} }"; -// configs::EVENT_KEY_ALIAS_CONFIG = -// configs::load_eventkey_alias("test_files/config/eventkey_alias.txt"); -// let display = format!( -// "{}", -// format_args!( -// "{:?}", -// configs::CONFIG.write().unwrap().event_key_alias_config -// ) -// ); -// assert_eq!(message, display); -// } -// } + // #[test] + // #[ignore] + // fn singleton_read_and_write() { + // let message = + // "EventKeyAliasConfig { key_to_eventkey: {\"EventID\": \"Event.System.EventID\"} }"; + // configs::EVENT_KEY_ALIAS_CONFIG = + // configs::load_eventkey_alias("test_files/config/eventkey_alias.txt"); + // let display = format!( + // "{}", + // format_args!( + // "{:?}", + // configs::CONFIG.write().unwrap().event_key_alias_config + // ) + // ); + // assert_eq!(message, display); + // } + // } #[test] fn target_event_time_filter() { @@ -358,4 +358,4 @@ mod tests { assert_eq!(time_filter.is_target(&start_time), true); assert_eq!(time_filter.is_target(&end_time), true); } -} \ No newline at end of file +} From 8b1e2894623c34d72e8454980f59d7a2be92b2c7 Mon Sep 17 00:00:00 2001 From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> Date: Thu, 16 Dec 2021 22:04:23 +0900 Subject: [PATCH 10/19] delete noisy-rules folder. not needed anymore (#287) --- ...T1059_PowershellExecutionRemoteCommand.yml | 20 ---- rules-noisy/Security/4688.yml | 14 --- .../win_hidden_user_creation.yml | 30 ----- ...win_user_added_to_local_administrators.yml | 32 ----- .../win_user_creation.yml | 33 ------ .../Sigma/sysmon_wmi_event_subscription.yml | 27 ----- .../Sigma/win_metasploit_authentication.yml | 40 ------- .../Sigma/win_multiple_suspicious_cli.yml | 109 ------------------ ...powershell_script_installed_as_service.yml | 29 ----- .../Sigma/win_rare_schtasks_creations.yml | 33 ------ .../Sigma/win_rare_service_installs.yml | 28 ----- .../win_susp_failed_logons_single_source.yml | 34 ------ .../win_susp_failed_logons_single_source2.yml | 35 ------ rules-noisy/System/7036.yml | 15 --- 14 files changed, 479 deletions(-) delete mode 100644 rules-noisy/PowershellOperational/4104_T1059_PowershellExecutionRemoteCommand.yml delete mode 100644 rules-noisy/Security/4688.yml delete mode 100644 rules-noisy/Sigma/already-have-hayabusa-rule/win_hidden_user_creation.yml delete mode 100644 rules-noisy/Sigma/already-have-hayabusa-rule/win_user_added_to_local_administrators.yml delete mode 100644 rules-noisy/Sigma/already-have-hayabusa-rule/win_user_creation.yml delete mode 100644 rules-noisy/Sigma/sysmon_wmi_event_subscription.yml delete mode 100644 rules-noisy/Sigma/win_metasploit_authentication.yml delete mode 100644 rules-noisy/Sigma/win_multiple_suspicious_cli.yml delete mode 100644 rules-noisy/Sigma/win_powershell_script_installed_as_service.yml delete mode 100644 rules-noisy/Sigma/win_rare_schtasks_creations.yml delete mode 100644 rules-noisy/Sigma/win_rare_service_installs.yml delete mode 100644 rules-noisy/Sigma/win_susp_failed_logons_single_source.yml delete mode 100644 rules-noisy/Sigma/win_susp_failed_logons_single_source2.yml delete mode 100644 rules-noisy/System/7036.yml diff --git a/rules-noisy/PowershellOperational/4104_T1059_PowershellExecutionRemoteCommand.yml b/rules-noisy/PowershellOperational/4104_T1059_PowershellExecutionRemoteCommand.yml deleted file mode 100644 index 7fae9b75..00000000 --- a/rules-noisy/PowershellOperational/4104_T1059_PowershellExecutionRemoteCommand.yml +++ /dev/null @@ -1,20 +0,0 @@ -title: PowerShell Execution Remote Command -title_jp: Powershellのリモートコマンドの実行 -description: Powershell command executed remotely. -description_jp: Powershell command executed remotely. -author: Eric Conrad, Zach Mathis -mitre_attack: T1059 -level: medium -detection: - selection: - Channel: Microsoft-Windows-PowerShell/Operational - EventID: 4104 - Path: null - ScriptBlockText|re: '.+' - # condition: selection -falsepositives: - - normal system usage -output: 'Command: %ScriptBlockText%' -output: 'コマンド: %ScriptBlockText%' -creation_date: 2020/11/08 -updated_date: 2021/11/06 diff --git a/rules-noisy/Security/4688.yml b/rules-noisy/Security/4688.yml deleted file mode 100644 index f22fd44e..00000000 --- a/rules-noisy/Security/4688.yml +++ /dev/null @@ -1,14 +0,0 @@ -title: Command Line Logging -description: Command line logging. -author: Eric Conrad, Zach Mathis -detection: - selection: - Channel: Security - EventID: 4688 - CommandLine|re: '.+' - # condition: selection -falsepositives: - - unknown -output: 'CommandLine:%CommandLine% : ParentProcessName:%ParentProcessName%' -creation_date: 2020/11/8 -updated_date: 2021/11/8 diff --git a/rules-noisy/Sigma/already-have-hayabusa-rule/win_hidden_user_creation.yml b/rules-noisy/Sigma/already-have-hayabusa-rule/win_hidden_user_creation.yml deleted file mode 100644 index ffba158f..00000000 --- a/rules-noisy/Sigma/already-have-hayabusa-rule/win_hidden_user_creation.yml +++ /dev/null @@ -1,30 +0,0 @@ -title: Hidden Local User Creation -author: Christian Burkard -date: 2021/05/03 -description: Detects the creation of a local hidden user account which should not - happen for event ID 4720. -detection: - SELECTION_1: - EventID: 4720 - SELECTION_2: - TargetUserName: '*$' - condition: (SELECTION_1 and SELECTION_2) -falsepositives: -- unknown -fields: -- EventCode -- AccountName -id: 7b449a5e-1db5-4dd0-a2dc-4e3a67282538 -level: high -logsource: - product: windows - service: security -references: -- https://twitter.com/SBousseaden/status/1387743867663958021 -status: experimental -tags: -- attack.persistence -- attack.t1136.001 -yml_filename: win_hidden_user_creation.yml -yml_path: /Users/user/Documents/YamatoSecurity/sigma/rules/windows/builtin - diff --git a/rules-noisy/Sigma/already-have-hayabusa-rule/win_user_added_to_local_administrators.yml b/rules-noisy/Sigma/already-have-hayabusa-rule/win_user_added_to_local_administrators.yml deleted file mode 100644 index d7bb1f24..00000000 --- a/rules-noisy/Sigma/already-have-hayabusa-rule/win_user_added_to_local_administrators.yml +++ /dev/null @@ -1,32 +0,0 @@ -title: User Added to Local Administrators -author: Florian Roth -date: 2017/03/14 -description: This rule triggers on user accounts that are added to the local Administrators - group, which could be legitimate activity or a sign of privilege escalation activity -detection: - SELECTION_1: - EventID: 4732 - SELECTION_2: - TargetUserName: Administr* - SELECTION_3: - TargetSid: S-1-5-32-544 - SELECTION_4: - SubjectUserName: '*$' - condition: ((SELECTION_1 and (SELECTION_2 or SELECTION_3)) and not (SELECTION_4)) -falsepositives: -- Legitimate administrative activity -id: c265cf08-3f99-46c1-8d59-328247057d57 -level: medium -logsource: - product: windows - service: security -modified: 2021/07/07 -status: stable -tags: -- attack.privilege_escalation -- attack.t1078 -- attack.persistence -- attack.t1098 -yml_filename: win_user_added_to_local_administrators.yml -yml_path: /Users/user/Documents/YamatoSecurity/sigma/rules/windows/builtin - diff --git a/rules-noisy/Sigma/already-have-hayabusa-rule/win_user_creation.yml b/rules-noisy/Sigma/already-have-hayabusa-rule/win_user_creation.yml deleted file mode 100644 index fb8f5d46..00000000 --- a/rules-noisy/Sigma/already-have-hayabusa-rule/win_user_creation.yml +++ /dev/null @@ -1,33 +0,0 @@ -title: Local User Creation -author: Patrick Bareiss -date: 2019/04/18 -description: Detects local user creation on windows servers, which shouldn't happen - in an Active Directory environment. Apply this Sigma Use Case on your windows - server logs and not on your DC logs. -detection: - SELECTION_1: - EventID: 4720 - condition: SELECTION_1 -falsepositives: -- Domain Controller Logs -- Local accounts managed by privileged account management tools -fields: -- EventCode -- AccountName -- AccountDomain -id: 66b6be3d-55d0-4f47-9855-d69df21740ea -level: low -logsource: - product: windows - service: security -modified: 2020/08/23 -references: -- https://patrick-bareiss.com/detecting-local-user-creation-in-ad-with-sigma/ -status: experimental -tags: -- attack.persistence -- attack.t1136 -- attack.t1136.001 -yml_filename: win_user_creation.yml -yml_path: /Users/user/Documents/YamatoSecurity/sigma/rules/windows/builtin - diff --git a/rules-noisy/Sigma/sysmon_wmi_event_subscription.yml b/rules-noisy/Sigma/sysmon_wmi_event_subscription.yml deleted file mode 100644 index 36744d10..00000000 --- a/rules-noisy/Sigma/sysmon_wmi_event_subscription.yml +++ /dev/null @@ -1,27 +0,0 @@ -title: WMI Event Subscription -author: Tom Ueltschi (@c_APT_ure) -date: 2019/01/12 -description: Detects creation of WMI event subscription persistence method -detection: - SELECTION_1: - EventID: 19 - SELECTION_2: - EventID: 20 - SELECTION_3: - EventID: 21 - condition: (SELECTION_1 or SELECTION_2 or SELECTION_3) -falsepositives: -- exclude legitimate (vetted) use of WMI event subscription in your network -id: 0f06a3a5-6a09-413f-8743-e6cf35561297 -level: high -logsource: - category: wmi_event - product: windows -status: experimental -tags: -- attack.t1084 -- attack.persistence -- attack.t1546.003 -yml_filename: sysmon_wmi_event_subscription.yml -yml_path: /Users/user/Documents/YamatoSecurity/sigma/rules/windows/wmi_event - diff --git a/rules-noisy/Sigma/win_metasploit_authentication.yml b/rules-noisy/Sigma/win_metasploit_authentication.yml deleted file mode 100644 index 01d79062..00000000 --- a/rules-noisy/Sigma/win_metasploit_authentication.yml +++ /dev/null @@ -1,40 +0,0 @@ -title: Metasploit SMB Authentication -author: Chakib Gzenayi (@Chak092), Hosni Mribah -date: 2020/05/06 -description: Alerts on Metasploit host's authentications on the domain. -detection: - SELECTION_1: - EventID: 4625 - SELECTION_2: - EventID: 4624 - SELECTION_3: - LogonType: 3 - SELECTION_4: - AuthenticationPackageName: NTLM - SELECTION_5: - WorkstationName|re: ^[A-Za-z0-9]{16}$ - SELECTION_6: - ProcessName|re: ^$ - SELECTION_7: - EventID: 4776 - SELECTION_8: - Workstation|re: ^[A-Za-z0-9]{16}$ - condition: (((SELECTION_1 or SELECTION_2) and SELECTION_3 and SELECTION_4 and - SELECTION_5) or (SELECTION_6 and SELECTION_7 and SELECTION_8)) -falsepositives: -- Linux hostnames composed of 16 characters. -id: 72124974-a68b-4366-b990-d30e0b2a190d -level: high -logsource: - product: windows - service: security -modified: 2021/07/07 -references: -- https://github.com/rapid7/metasploit-framework/blob/master/lib/rex/proto/smb/client.rb -tags: -- attack.lateral_movement -- attack.t1077 -- attack.t1021.002 -yml_filename: win_metasploit_authentication.yml -yml_path: /Users/user/Documents/YamatoSecurity/sigma/rules/windows/builtin - diff --git a/rules-noisy/Sigma/win_multiple_suspicious_cli.yml b/rules-noisy/Sigma/win_multiple_suspicious_cli.yml deleted file mode 100644 index 3f5ae391..00000000 --- a/rules-noisy/Sigma/win_multiple_suspicious_cli.yml +++ /dev/null @@ -1,109 +0,0 @@ -title: Quick Execution of a Series of Suspicious Commands -author: juju4 -date: 2019/01/16 -description: Detects multiple suspicious process in a limited timeframe -detection: - SELECTION_1: - EventID: 1 - SELECTION_10: - CommandLine: '*nbtstat.exe*' - SELECTION_11: - CommandLine: '*net.exe*' - SELECTION_12: - CommandLine: '*netsh.exe*' - SELECTION_13: - CommandLine: '*nslookup.exe*' - SELECTION_14: - CommandLine: '*ping.exe*' - SELECTION_15: - CommandLine: '*quser.exe*' - SELECTION_16: - CommandLine: '*qwinsta.exe*' - SELECTION_17: - CommandLine: '*reg.exe*' - SELECTION_18: - CommandLine: '*runas.exe*' - SELECTION_19: - CommandLine: '*sc.exe*' - SELECTION_2: - CommandLine: '*arp.exe*' - SELECTION_20: - CommandLine: '*schtasks.exe*' - SELECTION_21: - CommandLine: '*ssh.exe*' - SELECTION_22: - CommandLine: '*systeminfo.exe*' - SELECTION_23: - CommandLine: '*taskkill.exe*' - SELECTION_24: - CommandLine: '*telnet.exe*' - SELECTION_25: - CommandLine: '*tracert.exe*' - SELECTION_26: - CommandLine: '*wscript.exe*' - SELECTION_27: - CommandLine: '*xcopy.exe*' - SELECTION_28: - CommandLine: '*pscp.exe*' - SELECTION_29: - CommandLine: '*copy.exe*' - SELECTION_3: - CommandLine: '*at.exe*' - SELECTION_30: - CommandLine: '*robocopy.exe*' - SELECTION_31: - CommandLine: '*certutil.exe*' - SELECTION_32: - CommandLine: '*vssadmin.exe*' - SELECTION_33: - CommandLine: '*powershell.exe*' - SELECTION_34: - CommandLine: '*wevtutil.exe*' - SELECTION_35: - CommandLine: '*psexec.exe*' - SELECTION_36: - CommandLine: '*bcedit.exe*' - SELECTION_37: - CommandLine: '*wbadmin.exe*' - SELECTION_38: - CommandLine: '*icacls.exe*' - SELECTION_39: - CommandLine: '*diskpart.exe*' - SELECTION_4: - CommandLine: '*attrib.exe*' - SELECTION_5: - CommandLine: '*cscript.exe*' - SELECTION_6: - CommandLine: '*dsquery.exe*' - SELECTION_7: - CommandLine: '*hostname.exe*' - SELECTION_8: - CommandLine: '*ipconfig.exe*' - SELECTION_9: - CommandLine: '*mimikatz.exe*' - condition: (SELECTION_1 and (SELECTION_2 or SELECTION_3 or SELECTION_4 or SELECTION_5 - or SELECTION_6 or SELECTION_7 or SELECTION_8 or SELECTION_9 or SELECTION_10 - or SELECTION_11 or SELECTION_12 or SELECTION_13 or SELECTION_14 or SELECTION_15 - or SELECTION_16 or SELECTION_17 or SELECTION_18 or SELECTION_19 or SELECTION_20 - or SELECTION_21 or SELECTION_22 or SELECTION_23 or SELECTION_24 or SELECTION_25 - or SELECTION_26 or SELECTION_27 or SELECTION_28 or SELECTION_29 or SELECTION_30 - or SELECTION_31 or SELECTION_32 or SELECTION_33 or SELECTION_34 or SELECTION_35 - or SELECTION_36 or SELECTION_37 or SELECTION_38 or SELECTION_39))| count() - by MachineName > 5 -falsepositives: -- False positives depend on scripts and administrative tools used in the monitored - environment -id: 61ab5496-748e-4818-a92f-de78e20fe7f1 -level: low -logsource: - category: process_creation - product: windows -modified: 2021/06/13 -references: -- https://car.mitre.org/wiki/CAR-2013-04-002 -status: experimental -tags: -- car.2013-04-002 -yml_filename: win_multiple_suspicious_cli.yml -yml_path: /Users/user/Documents/YamatoSecurity/sigma/rules/windows/process_creation - diff --git a/rules-noisy/Sigma/win_powershell_script_installed_as_service.yml b/rules-noisy/Sigma/win_powershell_script_installed_as_service.yml deleted file mode 100644 index 65f22ff2..00000000 --- a/rules-noisy/Sigma/win_powershell_script_installed_as_service.yml +++ /dev/null @@ -1,29 +0,0 @@ -title: PowerShell Scripts Installed as Services -author: oscd.community, Natalia Shornikova -date: 2020/10/06 -description: Detects powershell script installed as a Service -detection: - SELECTION_1: - EventID: 7045 - SELECTION_2: - ImagePath: '*powershell*' - SELECTION_3: - ImagePath: '*pwsh*' - condition: (SELECTION_1 and (SELECTION_2 or SELECTION_3)) -falsepositives: -- Unknown -id: a2e5019d-a658-4c6a-92bf-7197b54e2cae -level: high -logsource: - product: windows - service: system -modified: 2021/09/21 -references: -- https://speakerdeck.com/heirhabarov/hunting-for-powershell-abuse -status: experimental -tags: -- attack.execution -- attack.t1569.002 -yml_filename: win_powershell_script_installed_as_service.yml -yml_path: /Users/user/Documents/YamatoSecurity/sigma/rules/windows/builtin - diff --git a/rules-noisy/Sigma/win_rare_schtasks_creations.yml b/rules-noisy/Sigma/win_rare_schtasks_creations.yml deleted file mode 100644 index 40c51982..00000000 --- a/rules-noisy/Sigma/win_rare_schtasks_creations.yml +++ /dev/null @@ -1,33 +0,0 @@ -title: Rare Schtasks Creations -author: Florian Roth -date: 2017/03/23 -description: Detects rare scheduled tasks creations that only appear a few times per - time frame and could reveal password dumpers, backdoor installs or other types - of malicious code -detection: - SELECTION_1: - EventID: 4698 - condition: SELECTION_1| count() by TaskName < 5 -falsepositives: -- Software installation -- Software updates -id: b0d77106-7bb0-41fe-bd94-d1752164d066 -level: low -logsource: - definition: The Advanced Audit Policy setting Object Access > Audit Other Object - Access Events has to be configured to allow this detection (not in the baseline - recommendations by Microsoft). We also recommend extracting the Command field - from the embedded XML in the event data. - product: windows - service: security -status: experimental -tags: -- attack.execution -- attack.privilege_escalation -- attack.persistence -- attack.t1053 -- car.2013-08-001 -- attack.t1053.005 -yml_filename: win_rare_schtasks_creations.yml -yml_path: /Users/user/Documents/YamatoSecurity/sigma/rules/windows/builtin - diff --git a/rules-noisy/Sigma/win_rare_service_installs.yml b/rules-noisy/Sigma/win_rare_service_installs.yml deleted file mode 100644 index d5a4df16..00000000 --- a/rules-noisy/Sigma/win_rare_service_installs.yml +++ /dev/null @@ -1,28 +0,0 @@ -title: Rare Service Installs -author: Florian Roth -date: 2017/03/08 -description: Detects rare service installs that only appear a few times per time frame - and could reveal password dumpers, backdoor installs or other types of malicious - services -detection: - SELECTION_1: - EventID: 7045 - condition: SELECTION_1| count() by ServiceFileName < 5 -falsepositives: -- Software installation -- Software updates -id: 66bfef30-22a5-4fcd-ad44-8d81e60922ae -level: low -logsource: - product: windows - service: system -status: experimental -tags: -- attack.persistence -- attack.privilege_escalation -- attack.t1050 -- car.2013-09-005 -- attack.t1543.003 -yml_filename: win_rare_service_installs.yml -yml_path: /Users/user/Documents/YamatoSecurity/sigma/rules/windows/builtin - diff --git a/rules-noisy/Sigma/win_susp_failed_logons_single_source.yml b/rules-noisy/Sigma/win_susp_failed_logons_single_source.yml deleted file mode 100644 index 85c0b923..00000000 --- a/rules-noisy/Sigma/win_susp_failed_logons_single_source.yml +++ /dev/null @@ -1,34 +0,0 @@ -title: Failed Logins with Different Accounts from Single Source System -author: Florian Roth -date: 2017/01/10 -description: Detects suspicious failed logins with different user accounts from a - single source system -detection: - SELECTION_1: - EventID: 529 - SELECTION_2: - EventID: 4625 - SELECTION_3: - TargetUserName: '*' - SELECTION_4: - WorkstationName: '*' - condition: ((SELECTION_1 or SELECTION_2) and SELECTION_3 and SELECTION_4)| count(TargetUserName) - by WorkstationName > 3 -falsepositives: -- Terminal servers -- Jump servers -- Other multiuser systems like Citrix server farms -- Workstations with frequently changing users -id: e98374a6-e2d9-4076-9b5c-11bdb2569995 -level: medium -logsource: - product: windows - service: security -modified: 2021/09/21 -tags: -- attack.persistence -- attack.privilege_escalation -- attack.t1078 -yml_filename: win_susp_failed_logons_single_source.yml -yml_path: /Users/user/Documents/YamatoSecurity/sigma/rules/windows/builtin - diff --git a/rules-noisy/Sigma/win_susp_failed_logons_single_source2.yml b/rules-noisy/Sigma/win_susp_failed_logons_single_source2.yml deleted file mode 100644 index 79daca36..00000000 --- a/rules-noisy/Sigma/win_susp_failed_logons_single_source2.yml +++ /dev/null @@ -1,35 +0,0 @@ -title: Failed Logins with Different Accounts from Single Source System -author: Florian Roth -date: 2017/01/10 -description: Detects suspicious failed logins with different user accounts from a - single source system -detection: - SELECTION_1: - EventID: 4776 - SELECTION_2: - TargetUserName: '*' - SELECTION_3: - Workstation: '*' - condition: (SELECTION_1 and SELECTION_2 and SELECTION_3)| count(TargetUserName) - by Workstation > 3 -falsepositives: -- Terminal servers -- Jump servers -- Other multiuser systems like Citrix server farms -- Workstations with frequently changing users -id: 6309ffc4-8fa2-47cf-96b8-a2f72e58e538 -level: medium -logsource: - product: windows - service: security -modified: 2021/09/21 -related: -- id: e98374a6-e2d9-4076-9b5c-11bdb2569995 - type: derived -tags: -- attack.persistence -- attack.privilege_escalation -- attack.t1078 -yml_filename: win_susp_failed_logons_single_source2.yml -yml_path: /Users/user/Documents/YamatoSecurity/sigma/rules/windows/builtin - diff --git a/rules-noisy/System/7036.yml b/rules-noisy/System/7036.yml deleted file mode 100644 index c4f8d6b4..00000000 --- a/rules-noisy/System/7036.yml +++ /dev/null @@ -1,15 +0,0 @@ -title: The ... service entered the stopped|running state -description: hogehoge -author: DeepblueCLI, Zach Mathis -detection: - selection: - Channel: System - EventID: 7036 - param1: - regexes: ./config/regex/regexes_suspicous_service.txt - condition: selection -falsepositives: - - unknown -output: 'Suspicious Service Name¥nService name: %ServiceName%' -creation_date: 2020/11/8 -uodated_date: 2020/11/8 From 9be8b3d33f114a6cc34c7e6471f486dea9c16554 Mon Sep 17 00:00:00 2001 From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> Date: Fri, 17 Dec 2021 11:07:27 +0000 Subject: [PATCH 11/19] art update (#294) --- art/christmas.txt | 70 +++++++++++++++++++++++++++++++------- art/happynewyear.txt | 36 ++++++++++++++------ art/ninja.txt | 6 ++-- art/takoyaki.txt | 81 +++++++++++++++++++++----------------------- 4 files changed, 125 insertions(+), 68 deletions(-) diff --git a/art/christmas.txt b/art/christmas.txt index 48a94f93..144c9b8c 100644 --- a/art/christmas.txt +++ b/art/christmas.txt @@ -1,13 +1,57 @@ - /⌒\、__/⌒ ̄} - \__(__)__/ - 〃〓/ ̄ > <  ̄\〓〃 - ミ☆/: (:: ::):: >☆彡 - ★≡〃\/〉:: ::〈\/ ≡〃★ - ●※○ ^^^^^^^^ ○※● -〃≡★ Merry Christmas ★≡〃 - ☆〓 〓☆ - 〃≡★ (;) ★≡〃 - ●※○- ,_】【_, ,-○※● - ★〃≡〓 ○ 〓≡〃★ - ミ☆-★※★-☆彡 - ● \ No newline at end of file + + ,, + ,,,, + .,,,,,,,,,,,.. + .,,,,,,,,,,,,,,,, + ,,,,,,,,,,, + ,,,,,,,,,,, + ,,,,(((,,,,. + . ,(((((( + ((((((((( + ((((((((((((( + (((((((,,,,((((* + ((((((((,,,,,,,(((( + ((((((((((,,,,,,((((((( + ((*,(((((((((((((((((((((/ + ((* ((. .(((((((((((((((( + ((((( Merry ((((((((( + (((((((, Christmas ((((((((((( + ((((((((/(((((( ((((((((((((((((((((( + (((((((*******((((((((((((((((((((((((((((((( + (((((((((*******((((((((((((((((((((((((((((((((( + .//////(((((((((((((((((((((((((((,,,,,(((((((((//////* + (((((((((((((((((((((((((((,,,,,,,((((((((( + (( ((((((((((((((((((((((,,,,,(((((((((((( + ,((. .(((((((((((((((((((((((((((((((((( + (((((((( from ((((((((((((((((((((((((((((((* + (((((((((((( , (((((((((((((((((( *.(* + .((((((((((((((((((((((((((((((((((((((((( ((( + (((((((((((((((((((((((((((((((((((((((/ Yamato ,((((((( + (((((((((((((((((((((((((((((((((((((((( (((((((((((( + ((((((((((((((((((((((((,,,((((( (, ,(((((((((( + ((((((((((((((((((((((((,,,,,,,(((((((((((((((((((((((((( + *(((( ((((((((((((((((/,,,,,,((((((((((((((((******(((((( + (((( ((((((((((((((((((((((((((((((((********((((((. + (((((( Security! ((((((((((((((((((((((((((((((****(((((((((( + ((((((((( ((((((((((((((((((((((((((((((((((((((((((( + ,(((((((((((((((( .(( ((((((((((((((((((( ( ((((((((((((( + ((((((((((((((((,,,,,((((((((( (((((((((((( Ho ho ho!!! ,(((((((((((((. + (((((((((((((((((,,,,,,,(((((((((((((((((((((( (((((((((((((((((((( + ((((((((((((((((((((,,,,,((((((((((((((((((((((((((( ,(((((((/,,,,,(((((((((((((((( + (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((,,,,,,,((((((((((((((((( + .(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((,,,,*(((((((((((((((((((/ + ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( + /////////// + /////////// + /////////// + /////////// + /////////// + /////////// + ***************************** + ***************************** + %%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%#%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%% + #%%%%%%%%%%%%%%%#%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%% + \ No newline at end of file diff --git a/art/happynewyear.txt b/art/happynewyear.txt index 14c2bc91..bda3f44d 100644 --- a/art/happynewyear.txt +++ b/art/happynewyear.txt @@ -1,10 +1,26 @@ - _〆 - (∴) - ( ̄ ̄ ̄) - <( ̄ ̄ ̄ ̄)> - [二◆二二◆二] - |◇ ● ◇| - |◆ ◆| - |____| - - A Happy New Year!! + + @@ + @@@@@ @@ @@@@, + @@& @@@@@@@@@@@@@@@@@. @@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@( @@@@@@ @@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@. + .@@@@@@@@@@@ @@@@@ #@@@ @@@@@@ @@@@@@@@@@@@@ + @@@ @@@@ @@@@@@@@@@@@@@( @@@@@@@ + ,@@@ (@@@ @@ .@@@@@ + @@@@ @@@@@@@ %% @@@@@ + @@@@ @@@@ @@@@@@@%*.,@@@@@@ @@@@, + @@@@ @@@@@@ @@@@@ @@@@. @@@@@@@@@ + @@@@ @@@ @@@@@@@@ @@@@@ @@@@@@@ @@@@@@@@@@@@@@@@@ + @@@, @@@@& @@@@ @@@@ + @@@ %@@@@@@ %@@@@, @@@@ @@@@ + @@@@@@@@# @@@@@ @@@@ @@@@ + @@@ (@@@@@@@@@@ @@@@ @@@@ + @@@@@@@@@@@@@@@@@@@@@ @@@@ @@@@ %@@@ + @@@@@@( @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@ @@@@@@@ ,@@@@@@@@@@@@@@@@@@@@@@/ &@@@@@@@@@@@@@@& + @@@@@ @@@@@@@ @@@@@@@@@@ @@@@# + @@@@@ @@@@@@@ *@ + @@@@@ #@@@@ + + Happy New Year from Yamato Security!!! + Akemashite Omedetou Gozaimasu! + Honnen mo yoroshiku onegai shimasu! diff --git a/art/ninja.txt b/art/ninja.txt index 505ddce6..ddfd6831 100644 --- a/art/ninja.txt +++ b/art/ninja.txt @@ -1,5 +1,3 @@ - - Today is Ninja Day (2/22)! .`,I>>+<;"' .,}u#zcccccz*#W&jI. @@ -35,3 +33,7 @@ [$#ccccccccccccB$%WMcnnnnnnnnz$$$B&cc#@8nnnnnnu#@$$&*cccccccMB*ccccc#$$$, @%ccccccccccccz$#cxcnnnnnnnnM$$$@zcccc*$8nnnnnnnnW8$$%MMMM*#&zccccccc@$$| "$*cccccccccccc#$cnx@WnnnnnnW$$$$Wccccc#@$8unnnn*@Wu&@$$$$$$@#cccccccc&$$W + + Happy Ninja Day! Nin Nin! (2/22)! + from Yamato Security + \ No newline at end of file diff --git a/art/takoyaki.txt b/art/takoyaki.txt index 29320d36..a01fa6b7 100644 --- a/art/takoyaki.txt +++ b/art/takoyaki.txt @@ -1,43 +1,38 @@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@@#------@@@@@@@@--------@@@@@@@@--------@@@@@@@@--------@@@@@@@@--------@@@@@@@@------#@@ -@@* @@@@@@@% @@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ *@@ -@@* @@@@@@@% @@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ *@@ -@@#------********--------********--------********--------********--------********=-----#@@ -@@@@@@@@@ @@@@@@@# @@@@@@@@ @@@@@@@@ @@@@@@@% @@@@@@@@@ -@@@@@@@@@ @@@@@@@# @@@@@@@@ @@@@@@@@ @@@@@@@% @@@@@@@@@ -@@@@@@@@@ @@@@@@@# @@@@@@@@ @@@@@@@@ @@@@@@@% @@@@@@@@@ -@@@@@@@@@-------=@@@@@@@%-------=@@@@@@@@-------=@@@@@@@@-------=@@@@@@@@-------=@@@@@@@@@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@=:@@@@@@@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*.+@@@@@@@@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@= *@@@@@@@@@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%:-@@@@@@@@@@@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*.*@@@@@@@@@@@@ -@@@@@@@@@@@%*=-:::-=*@@@@@@@@@%*=-:::-+#@@@@@@@@@#+=-::-=+#@@@@@@@@@#+--::: =%@@@@@@@@@@@@ -@@@@@@@@@*: = :#@@@@+. -%@@@@= =@@@@%- .+@@@@@@@@@@ -@@@@@@@@: :: #* -@%. - *= =@* -. =#. *@+ :: -#: .#@@@@@@@@ -@@@@@@@. : -. . : .= : : = . %@@@@@@@ -@@@@@@+ =*. -+=. .++ =+= :*= =+- -*- .++- . -@@@@@@@ -@@@@@@= *@@@@=-#@@@@+:+@% .*@@@%==#@@@@=:*@# :#@@@#==%@@@%--#@+ -%@@@*-=@@@@#--%@-:@@@@@@@ -@@@@-== #@@@@@@@@@@@@@@@+ %@@@@@@@@@@@@@@@- .@@@@@@@@@@@@@@@@. :@@@@@@@@@@@@@@@@ :=:@@@@@ -@@@@#. =*#@@@@@@@@@@@* *-.%@@@@@@@@@@@@@= #::@@@@@@@@@@@@@@:.# -@@@@@@@@@@@%*=. .*@@@@@ -@@@@@@#- .:-=+*#%*::%@@*.-#@@@@@@@@@+.-%@@=.=%@@@@@@@@%=.=@@@=.+%#*+=-:. :*@@@@@@@ -@@@@@@@@@*- ..::- :====-. .=++++++: .-====-. :::.. :*@@@@@@@@@@ -@@@@@@@@@@@@%+-. .-+#@@@@@@@@@@@@@ -@@@@@@@@@@@@@@@@@%#*+-::. ..:-=*#%@@@@@@@@@@@@@@@@@@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@%##***++++=============++++***##%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@@======@@#--%@@#=+@@+=#@#=--=#@#==@@==#@@+-=@@@+=*@@==%==#@@@*====+%@@@#--%@@*=*@%==%==@@ -@@*+ ##@@. .@@* .@= :@- .+*: -# %% +@# -@@- =@: =@ *@@@- -#+ #@@. .@@= .@* % %@ -@@@# @@@: =- -@* .@@ #@@% @# *@% *. *@- =@@ #@@@= =@@= -@- -= -@@+ .#@:.@@ -@@@# @@+ :: ** .@: :@+ -- +@@# #@@: .:: %- =%. =@ *@@@- :+- .%* :: +@@+ @@@=-@@ -@@@%++@@*+%@@#+*%+*@@*+#@@*++*@@@@@**@@@++@@@*+##+#@%++%++%@@@#+++*#@@*+%@@%+*@@%**@@@*+@@ -@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@@@@@@@@@:.......@@@@@@@@:.......@@@@@@@@:.......@@@@@@@@:.......%@@@@@@@:.......@@@@@@@@@ -@@@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ %@@@@@@@ @@@@@@@@@ -@@@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ %@@@@@@@ @@@@@@@@@ -@@@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ %@@@@@@@ @@@@@@@@@ -@@#:::::-########::::::::########::::::::########::::::::########::::::::########::::::#@@ -@@* .@@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ *@@ -@@* .@@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ *@@ -@@%+++++*@@@@@@@@++++++++@@@@@@@@++++++++@@@@@@@@++++++++@@@@@@@@++++++++@@@@@@@@++++++%@@ \ No newline at end of file + + #### + ##.## + ##.(# + .#.,# + #..# + ##.#* + ##.## + *#.## + #.## + #(/# + ##.# + (#.#, + #.#( + #*## + #### + .,####* ##(# + ##.##../##((####.((####,# + ####(.(....(((((##,.#(((###### + ###.(##(((#######..##......#((((##/ + ########((((####.(##########(*(((#,##..# + #################..####*..(((######*,##..# + /###############......*####((((###.*##..#### + #######(,#####################*#####.((((#### + #(###,,,,#############(/######...##..#(###### + #,,,,,,,,*#######,,,,,,,,######(.######/*/### + #,,,,,,,,,,,,,,,,,,,,,,,#######/############# + #,,,,,,,,,,,,,,,,,,,,,,##########,,,(####### + #,,,,,,,,,,,,,,,,,,,,,#######,,,,,,####### + #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,###### + ##,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*## + ##,,,,,,,,,,,,,,,,,,,,,,,,,,,,,## + ###,,,,,,,,,,,,,,,,,,,,,,##. + .################# + + HAPPY TAKOYAKI DAY!!! (8/8) + from Yamato Security + \ No newline at end of file From d668fc9241a5dd7c49a7368eb7e75550608a2036 Mon Sep 17 00:00:00 2001 From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> Date: Fri, 17 Dec 2021 12:25:55 +0000 Subject: [PATCH 12/19] Regex filename change (#291) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update rule config files and art * regexサンプルファイルの名前変更 * fixed test error due to filename change #291 Co-authored-by: DustInDark --- art/christmas.txt | 2 +- config/eventkey_alias.txt | 4 ++++ config/exclude-rules-full.txt | 8 ++++++++ config/exclude-rules.txt | 3 ++- config/noisy-rules-full.txt | 9 +++++++++ config/noisy-rules.txt | 4 ++++ ...viceimage.txt => allowlist_legitimate_services.txt} | 0 ...s_service.txt => detectlist_suspicous_services.txt} | 0 ...rocess-WindowsService_MaliciousServiceInstalled.yml | 8 ++++---- src/detections/rule/matchers.rs | 10 +++++----- src/detections/utils.rs | 4 ++-- 11 files changed, 39 insertions(+), 13 deletions(-) create mode 100644 config/exclude-rules-full.txt create mode 100644 config/noisy-rules-full.txt rename config/regex/{allowlist_legimate_serviceimage.txt => allowlist_legitimate_services.txt} (100%) rename config/regex/{regexes_suspicous_service.txt => detectlist_suspicous_services.txt} (100%) diff --git a/art/christmas.txt b/art/christmas.txt index 144c9b8c..72c9807a 100644 --- a/art/christmas.txt +++ b/art/christmas.txt @@ -54,4 +54,4 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%% #%%%%%%%%%%%%%%%#%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% - \ No newline at end of file + diff --git a/config/eventkey_alias.txt b/config/eventkey_alias.txt index 3188c4b9..7c4bbcc4 100644 --- a/config/eventkey_alias.txt +++ b/config/eventkey_alias.txt @@ -12,6 +12,7 @@ AuthenticationPackageName,Event.EventData.AuthenticationPackageName CallTrace,Event.EventData.CallTrace Caller_Process_Name,Event.EventData.Caller_Process_Name CallingProcessName,Event.EventData.CallingProcessName +CategoryName,Event.EventData.Category Name Channel,Event.System.Channel Client_Address,Event.EventData.Client_Address CommandLine,Event.EventData.CommandLine @@ -30,6 +31,7 @@ DestinationIsIpv6,Event.EventData.DestinationIsIpv6 DestinationPort,Event.EventData.DestinationPort Details,Event.EventData.Details DetectionSource,Event.EventData.DetectionSource +DetectionUser,Event.EventData.Detection User Device,Event.EventData.Device DeviceClassName,Event.EventData.DeviceClassName DeviceDescription,Event.EventData.DeviceDescription @@ -107,6 +109,7 @@ Service,Event.EventData.Service ServiceFileName,Event.EventData.ServiceFileName ServiceName,Event.EventData.ServiceName ServicePrincipalNames,Event.EventData.ServicePrincipalNames +SeverityName,Event.EventData.Severity Name ShareName,Event.EventData.ShareName SidHistory,Event.EventData.SidHistory Signature,Event.EventData.Signature @@ -136,6 +139,7 @@ TargetProcessAddress,Event.EventData.TargetProcessAddress TargetSid,Event.EventData.TargetSid TargetUserName,Event.EventData.TargetUserName TaskName,Event.EventData.TaskName +ThreatName,Event.EventData.Threat Name TicketEncryptionType,Event.EventData.TicketEncryptionType TicketOptions,Event.EventData.TicketOptions Url,Event.EventData.url diff --git a/config/exclude-rules-full.txt b/config/exclude-rules-full.txt new file mode 100644 index 00000000..f03fce3c --- /dev/null +++ b/config/exclude-rules-full.txt @@ -0,0 +1,8 @@ +4fe151c2-ecf9-4fae-95ae-b88ec9c2fca6 # ./rules/sigma/other/msexchange/win_exchange_transportagent.yml +c92f1896-d1d2-43c3-92d5-7a5b35c217bb # ./rules/sigma/other/msexchange/win_exchange_cve_2021_42321.yml +9f7aa113-9da6-4a8d-907c-5f1a4b908299 # ./rules/sigma/deprecated/powershell_syncappvpublishingserver_exe.yml + +# Replaced by hayabusa rules +c265cf08-3f99-46c1-8d59-328247057d57 # ./rules/sigma/builtin/security/win_user_added_to_local_administrators.yml +66b6be3d-55d0-4f47-9855-d69df21740ea # ./rules/sigma/builtin/security/win_user_creation.yml +7b449a5e-1db5-4dd0-a2dc-4e3a67282538 # ./rules/sigma/builtin/security/win_hidden_user_creation.yml \ No newline at end of file diff --git a/config/exclude-rules.txt b/config/exclude-rules.txt index 201932cc..22e7479f 100644 --- a/config/exclude-rules.txt +++ b/config/exclude-rules.txt @@ -2,4 +2,5 @@ c92f1896-d1d2-43c3-92d5-7a5b35c217bb 7b449a5e-1db5-4dd0-a2dc-4e3a67282538 c265cf08-3f99-46c1-8d59-328247057d57 -66b6be3d-55d0-4f47-9855-d69df21740ea \ No newline at end of file +66b6be3d-55d0-4f47-9855-d69df21740ea +9f7aa113-9da6-4a8d-907c-5f1a4b908299 \ No newline at end of file diff --git a/config/noisy-rules-full.txt b/config/noisy-rules-full.txt new file mode 100644 index 00000000..abadf989 --- /dev/null +++ b/config/noisy-rules-full.txt @@ -0,0 +1,9 @@ +0f06a3a5-6a09-413f-8743-e6cf35561297 # ./rules/sigma/wmi_event/sysmon_wmi_event_subscription.yml +b0d77106-7bb0-41fe-bd94-d1752164d066 # ./rules/sigma/builtin/security/win_rare_schtasks_creations.yml +66bfef30-22a5-4fcd-ad44-8d81e60922ae # ./rules/sigma/builtin/system/win_rare_service_installs.yml +e98374a6-e2d9-4076-9b5c-11bdb2569995 # ./rules/sigma/builtin/security/win_susp_failed_logons_single_source.yml +6309ffc4-8fa2-47cf-96b8-a2f72e58e538 # ./rules/sigma/builtin/security/win_susp_failed_logons_single_source2.yml +61ab5496-748e-4818-a92f-de78e20fe7f1 # ./rules/sigma/process_creation/win_multiple_suspicious_cli.yml +add2ef8d-dc91-4002-9e7e-f2702369f53a # ./rules/sigma/builtin/security/win_susp_failed_remote_logons_single_source.yml +196a29c2-e378-48d8-ba07-8a9e61f7fab9 # ./rules/sigma/builtin/security/win_susp_failed_logons_explicit_credentials.yml +72124974-a68b-4366-b990-d30e0b2a190d # ./rules/sigma/builtin/security/win_metasploit_authentication.yml \ No newline at end of file diff --git a/config/noisy-rules.txt b/config/noisy-rules.txt index 1fa83b45..fce2d332 100644 --- a/config/noisy-rules.txt +++ b/config/noisy-rules.txt @@ -3,4 +3,8 @@ b0d77106-7bb0-41fe-bd94-d1752164d066 66bfef30-22a5-4fcd-ad44-8d81e60922ae e98374a6-e2d9-4076-9b5c-11bdb2569995 6309ffc4-8fa2-47cf-96b8-a2f72e58e538 +61ab5496-748e-4818-a92f-de78e20fe7f1 +add2ef8d-dc91-4002-9e7e-f2702369f53a +196a29c2-e378-48d8-ba07-8a9e61f7fab9 +72124974-a68b-4366-b990-d30e0b2a190d b20f6158-9438-41be-83da-a5a16ac90c2b \ No newline at end of file diff --git a/config/regex/allowlist_legimate_serviceimage.txt b/config/regex/allowlist_legitimate_services.txt similarity index 100% rename from config/regex/allowlist_legimate_serviceimage.txt rename to config/regex/allowlist_legitimate_services.txt diff --git a/config/regex/regexes_suspicous_service.txt b/config/regex/detectlist_suspicous_services.txt similarity index 100% rename from config/regex/regexes_suspicous_service.txt rename to config/regex/detectlist_suspicous_services.txt diff --git a/rules/hayabusa/alerts/System/7045_CreateOrModiftySystemProcess-WindowsService_MaliciousServiceInstalled.yml b/rules/hayabusa/alerts/System/7045_CreateOrModiftySystemProcess-WindowsService_MaliciousServiceInstalled.yml index 78b13842..a249f246 100644 --- a/rules/hayabusa/alerts/System/7045_CreateOrModiftySystemProcess-WindowsService_MaliciousServiceInstalled.yml +++ b/rules/hayabusa/alerts/System/7045_CreateOrModiftySystemProcess-WindowsService_MaliciousServiceInstalled.yml @@ -6,8 +6,8 @@ title: Malicious service installed title_jp: 悪意のあるサービスがインストールされた output: 'Service: %ServiceName% : Image path: %ImagePath' output_jp: 'サービス名: %ServiceName% : Imageパス: %ImagePath' -description: Malicious service was installed based on suspicious entries in ./config/regex/regexes_suspicous_service.txt -description_jp: Malicious service was installed based on suspicious entries in ./config/regex/regexes_suspicous_service.txt +description: Malicious service was installed based on suspicious entries in ./config/regex/detectlist_suspicous_services.txt +description_jp: Malicious service was installed based on suspicious entries in ./config/regex/detectlist_suspicous_services.txt id: dbbfd9f3-9508-478b-887e-03ddb9236909 level: high @@ -17,10 +17,10 @@ detection: Channel: System EventID: 7045 ServiceName: - regexes: ./config/regex/regexes_suspicous_service.txt + regexes: ./config/regex/detectlist_suspicous_services.txt ImagePath: min_length: 1000 - allowlist: ./config/regex/allowlist_legimate_serviceimage.txt + allowlist: .allowlist_legitimate_services.txt condition: selection falsepositives: - normal system usage diff --git a/src/detections/rule/matchers.rs b/src/detections/rule/matchers.rs index 42c69614..7fc45fd7 100644 --- a/src/detections/rule/matchers.rs +++ b/src/detections/rule/matchers.rs @@ -538,8 +538,8 @@ mod tests { - ホスト アプリケーション ImagePath: min_length: 1234321 - regexes: ./config/regex/regexes_suspicous_service.txt - allowlist: ./config/regex/allowlist_legimate_serviceimage.txt + regexes: ./config/regex/detectlist_suspicous_services.txt + allowlist: ./config/regex/allowlist_legitimate_services.txt falsepositives: - unknown level: medium @@ -1165,7 +1165,7 @@ mod tests { selection: EventID: 4103 Channel: - - allowlist: ./config/regex/allowlist_legimate_serviceimage.txt + - allowlist: ./config/regex/allowlist_legitimate_services.txt output: 'command=%CommandLine%' "#; @@ -1202,7 +1202,7 @@ mod tests { selection: EventID: 4103 Channel: - - allowlist: ./config/regex/allowlist_legimate_serviceimage.txt + - allowlist: ./config/regex/allowlist_legitimate_services.txt output: 'command=%CommandLine%' "#; @@ -1239,7 +1239,7 @@ mod tests { selection: EventID: 4103 Channel: - - allowlist: ./config/regex/allowlist_legimate_serviceimage.txt + - allowlist: ./config/regex/allowlist_legitimate_services.txt output: 'command=%CommandLine%' "#; diff --git a/src/detections/utils.rs b/src/detections/utils.rs index 72d6448c..c7e4c57b 100644 --- a/src/detections/utils.rs +++ b/src/detections/utils.rs @@ -192,7 +192,7 @@ mod tests { #[test] fn test_check_regex() { - let regexes = utils::read_txt("./config/regex/regexes_suspicous_service.txt") + let regexes = utils::read_txt("./config/regex/detectlist_suspicous_services.txt") .unwrap() .into_iter() .map(|regex_str| Regex::new(®ex_str).unwrap()) @@ -207,7 +207,7 @@ mod tests { #[test] fn test_check_allowlist() { let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate.exe\""; - let allowlist = utils::read_txt("./config/regex/allowlist_legimate_serviceimage.txt") + let allowlist = utils::read_txt("./config/regex/allowlist_legitimate_services.txt") .unwrap() .into_iter() .map(|allow_str| Regex::new(&allow_str).unwrap()) From ee80e6bc1eb5d96af9768332229197e1cf76682c Mon Sep 17 00:00:00 2001 From: DustInDark Date: Sat, 18 Dec 2021 11:06:08 +0900 Subject: [PATCH 13/19] Hotfix/regex filename replace lack#296 (#297) * fixed lacked replacement #286 * fixed typo #296 --- ...tySystemProcess-WindowsService_MaliciousServiceInstalled.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/hayabusa/alerts/System/7045_CreateOrModiftySystemProcess-WindowsService_MaliciousServiceInstalled.yml b/rules/hayabusa/alerts/System/7045_CreateOrModiftySystemProcess-WindowsService_MaliciousServiceInstalled.yml index a249f246..4dbbdbd5 100644 --- a/rules/hayabusa/alerts/System/7045_CreateOrModiftySystemProcess-WindowsService_MaliciousServiceInstalled.yml +++ b/rules/hayabusa/alerts/System/7045_CreateOrModiftySystemProcess-WindowsService_MaliciousServiceInstalled.yml @@ -20,7 +20,7 @@ detection: regexes: ./config/regex/detectlist_suspicous_services.txt ImagePath: min_length: 1000 - allowlist: .allowlist_legitimate_services.txt + allowlist: ./config/regex/allowlist_legitimate_services.txt condition: selection falsepositives: - normal system usage From 2626ef8e49698e67f52dc3bc1dcd4c178089a830 Mon Sep 17 00:00:00 2001 From: DustInDark Date: Sat, 18 Dec 2021 11:06:45 +0900 Subject: [PATCH 14/19] removed process-speed view in progress bar #289 (#292) --- src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.rs b/src/main.rs index 7bce8cc9..1bdc0e82 100644 --- a/src/main.rs +++ b/src/main.rs @@ -137,6 +137,7 @@ fn analysis_files(evtx_files: Vec) { &filter::exclude_ids(), ); let mut pb = ProgressBar::new(evtx_files.len() as u64); + pb.show_speed = false; let mut detection = detection::Detection::new(rule_files); for evtx_file in evtx_files { if configs::CONFIG.read().unwrap().args.is_present("verbose") { From 17b6b97aa30a976b00d095773cb8b35979fd7776 Mon Sep 17 00:00:00 2001 From: DustInDark Date: Sat, 18 Dec 2021 11:12:28 +0900 Subject: [PATCH 15/19] Revert "removed process-speed view in progress bar #289 (#292)" (#298) This reverts commit 2626ef8e49698e67f52dc3bc1dcd4c178089a830. --- src/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 1bdc0e82..7bce8cc9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -137,7 +137,6 @@ fn analysis_files(evtx_files: Vec) { &filter::exclude_ids(), ); let mut pb = ProgressBar::new(evtx_files.len() as u64); - pb.show_speed = false; let mut detection = detection::Detection::new(rule_files); for evtx_file in evtx_files { if configs::CONFIG.read().unwrap().args.is_present("verbose") { From cbbcb4c0688d3b6dd79c43860431f114320657d2 Mon Sep 17 00:00:00 2001 From: James Takai / hach1yon <32596618+hach1yon@users.noreply.github.com> Date: Sat, 18 Dec 2021 11:13:51 +0900 Subject: [PATCH 16/19] Feature/re tuning and bugfix for regexes keyword (#293) * re-tuning * not effective * re-tuning * set key * fix bug and fix testcase. * fmt --- src/detections/detection.rs | 22 +- src/detections/mod.rs | 2 +- src/detections/rule/condition_parser.rs | 26 +- src/detections/rule/count.rs | 19 +- src/detections/rule/matchers.rs | 293 ++++--------- src/detections/rule/mod.rs | 116 +++-- src/detections/rule/selectionnodes.rs | 51 +-- src/detections/utils.rs | 48 ++ src/main.rs | 559 +++++++++++++----------- 9 files changed, 522 insertions(+), 614 deletions(-) diff --git a/src/detections/detection.rs b/src/detections/detection.rs index 7f16a3b9..103297d9 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -1,18 +1,18 @@ extern crate csv; -use crate::detections::rule::AggResult; -use serde_json::Value; -use std::collections::HashMap; -use tokio::{runtime::Runtime, spawn, task::JoinHandle}; - use crate::detections::configs; use crate::detections::print::AlertMessage; use crate::detections::print::MESSAGES; use crate::detections::rule; +use crate::detections::rule::AggResult; use crate::detections::rule::RuleNode; use crate::detections::utils::get_serde_number_to_string; use crate::filter; use crate::yaml::ParseYaml; +use hashbrown; +use serde_json::Value; +use std::collections::HashMap; +use tokio::{runtime::Runtime, spawn, task::JoinHandle}; use std::sync::Arc; @@ -24,15 +24,12 @@ pub struct EvtxRecordInfo { pub evtx_filepath: String, // イベントファイルのファイルパス ログで出力するときに使う pub record: Value, // 1レコード分のデータをJSON形式にシリアライズしたもの pub data_string: String, + pub key_2_value: hashbrown::HashMap, } impl EvtxRecordInfo { - pub fn new(evtx_filepath: String, record: Value, data_string: String) -> EvtxRecordInfo { - return EvtxRecordInfo { - evtx_filepath: evtx_filepath, - record: record, - data_string: data_string, - }; + pub fn get_value(&self, key: &String) -> Option<&String> { + return self.key_2_value.get(key); } } @@ -185,9 +182,8 @@ impl Detection { // 複数のイベントレコードに対して、ルールを1個実行します。 fn execute_rule(mut rule: RuleNode, records: Arc>) -> RuleNode { - let records = &*records; let agg_condition = rule.has_agg_condition(); - for record_info in records { + for record_info in records.as_ref() { let result = rule.select(&record_info.evtx_filepath, &record_info); if !result { continue; diff --git a/src/detections/mod.rs b/src/detections/mod.rs index 3bfab408..e4ee98be 100644 --- a/src/detections/mod.rs +++ b/src/detections/mod.rs @@ -1,5 +1,5 @@ pub mod configs; pub mod detection; pub mod print; -mod rule; +pub mod rule; pub mod utils; diff --git a/src/detections/rule/condition_parser.rs b/src/detections/rule/condition_parser.rs index 8bc90d71..3f37ed60 100644 --- a/src/detections/rule/condition_parser.rs +++ b/src/detections/rule/condition_parser.rs @@ -503,11 +503,10 @@ impl ConditionCompiler { #[cfg(test)] mod tests { - use yaml_rust::YamlLoader; - - use crate::detections::detection::EvtxRecordInfo; use crate::detections::rule::create_rule; use crate::detections::rule::tests::parse_rule_from_str; + use crate::detections::{self, utils}; + use yaml_rust::YamlLoader; const SIMPLE_RECORD_STR: &str = r#" { @@ -537,11 +536,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!( rule_node.select(&"testpath".to_owned(), &recinfo), expect_select @@ -586,11 +582,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_rec) => { @@ -633,11 +626,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_rec) => { diff --git a/src/detections/rule/count.rs b/src/detections/rule/count.rs index 9ad83e2b..d4152518 100644 --- a/src/detections/rule/count.rs +++ b/src/detections/rule/count.rs @@ -316,10 +316,11 @@ pub fn judge_timeframe( #[cfg(test)] mod tests { - use crate::detections::detection::EvtxRecordInfo; + use crate::detections; use crate::detections::rule::create_rule; use crate::detections::rule::AggResult; - use std::collections::HashMap; + use crate::detections::utils; + use hashbrown::HashMap; use chrono::{TimeZone, Utc}; use yaml_rust::YamlLoader; @@ -642,11 +643,8 @@ mod tests { for record in target { match serde_json::from_str(record) { Ok(rec) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: rec, - data_string: record.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(rec, "testpath".to_owned(), &keys); let _result = rule_node.select(&"testpath".to_string(), &recinfo); } Err(_rec) => { @@ -735,11 +733,8 @@ mod tests { for record_str in records_str { match serde_json::from_str(record_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let result = &rule_node.select(&"testpath".to_owned(), &recinfo); assert_eq!(result, &true); } diff --git a/src/detections/rule/matchers.rs b/src/detections/rule/matchers.rs index 7fc45fd7..f5800c14 100644 --- a/src/detections/rule/matchers.rs +++ b/src/detections/rule/matchers.rs @@ -1,11 +1,15 @@ use regex::Regex; -use serde_json::Value; use std::collections::VecDeque; use yaml_rust::Yaml; use crate::detections::{detection::EvtxRecordInfo, utils}; use mopa::mopafy; +use lazy_static::lazy_static; +lazy_static! { + pub static ref STR_DEFAULT: String = String::default(); +} + // 末端ノードがEventLogの値を比較するロジックを表す。 // 正規条件のマッチや文字数制限など、比較ロジック毎にこのtraitを実装したクラスが存在する。 // @@ -18,7 +22,7 @@ pub trait LeafMatcher: mopa::Any { /// 引数に指定されたJSON形式のデータがマッチするかどうか判定する。 /// main.rsでWindows Event LogをJSON形式に変換していて、そのJSON形式のWindowsのイベントログデータがここには来る /// 例えば正規表現でマッチするロジックなら、ここに正規表現でマッチさせる処理を書く。 - fn is_match(&self, event_value: Option<&Value>, recinfo: &EvtxRecordInfo) -> bool; + fn is_match(&self, event_value: Option<&String>, recinfo: &EvtxRecordInfo) -> bool; /// 初期化ロジックをここに記載します。 /// ルールファイルの書き方が間違っている等の原因により、正しくルールファイルからパースできない場合、戻り値のResult型でエラーを返してください。 @@ -60,11 +64,10 @@ impl LeafMatcher for MinlengthMatcher { return Result::Ok(()); } - fn is_match(&self, event_value: Option<&Value>, _recinfo: &EvtxRecordInfo) -> bool { - return match event_value.unwrap_or(&Value::Null) { - Value::String(s) => s.len() as i64 >= self.min_len, - Value::Number(n) => n.to_string().len() as i64 >= self.min_len, - _ => false, + fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool { + return match event_value { + Some(s) => s.len() as i64 >= self.min_len, + None => false, }; } } @@ -118,12 +121,10 @@ impl LeafMatcher for RegexesFileMatcher { return Result::Ok(()); } - fn is_match(&self, event_value: Option<&Value>, _recinfo: &EvtxRecordInfo) -> bool { - //TODO Wildcardの場合、CaseInsensitiveなので、ToLowerする。 - return match event_value.unwrap_or(&Value::Null) { - Value::String(s) => !utils::check_regex(s, &self.regexes), - Value::Number(n) => !utils::check_regex(&n.to_string(), &self.regexes), - _ => false, + fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool { + return match event_value { + Some(s) => utils::check_regex(s, &self.regexes), + None => false, }; } } @@ -177,12 +178,10 @@ impl LeafMatcher for AllowlistFileMatcher { return Result::Ok(()); } - fn is_match(&self, event_value: Option<&Value>, _recinfo: &EvtxRecordInfo) -> bool { - return match event_value.unwrap_or(&Value::Null) { - Value::String(s) => !utils::check_allowlist(s, &self.regexes), - Value::Number(n) => !utils::check_allowlist(&n.to_string(), &self.regexes), - Value::Bool(b) => !utils::check_allowlist(&b.to_string(), &self.regexes), - _ => true, + fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool { + return match event_value { + Some(s) => !utils::check_allowlist(s, &self.regexes), + None => true, }; } } @@ -320,50 +319,18 @@ impl LeafMatcher for DefaultMatcher { return Result::Ok(()); } - fn is_match(&self, event_value: Option<&Value>, recinfo: &EvtxRecordInfo) -> bool { - // unwrap_orの引数に""ではなく" "を指定しているのは、 - // event_valueが文字列じゃない場合にis_event_value_nullの値がfalseになるように、len() == 0とならない値を指定している。 - let is_event_value_null = event_value.is_none() - || event_value.unwrap().is_null() - || event_value.unwrap().as_str().unwrap_or(" ").len() == 0; - + fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool { // yamlにnullが設定されていた場合 // keylistが空(==JSONのgrep検索)の場合、無視する。 - if !self.key_list.is_empty() && self.re.is_none() { - return is_event_value_null; - } - - // JSON形式のEventLogデータをstringに変換するための前処理 - // 以前のコードはstringに変換に変換する必ずto_string()がするような処理になっていた。 - // そうすると、凄く遅くなるので、そうならないように回避 - let mut b_str = String::default(); - let mut n_str = String::default(); - match event_value.unwrap_or(&Value::Null) { - Value::Bool(b) => b_str = b.to_string(), - Value::Number(n) => { - n_str = n.to_string(); - } - _ => (), - }; - - // JSON形式のEventLogデータをstringに変換 - let event_value_str: Option<&String> = if self.key_list.is_empty() { - Option::Some(&recinfo.data_string) - } else { - let value = match event_value.unwrap_or(&Value::Null) { - Value::Bool(_) => Option::Some(&b_str), - Value::String(s) => Option::Some(s), - Value::Number(_) => Option::Some(&n_str), - _ => Option::None, - }; - value - }; - if event_value_str.is_none() { + if self.key_list.is_empty() && self.re.is_none() { return false; } - // 変換したデータに対してパイプ処理を実行する。 - let event_value_str = event_value_str.unwrap(); + if event_value.is_none() { + return false; + } + + let event_value_str = event_value.unwrap(); if self.key_list.is_empty() { // この場合ただのgrep検索なので、ただ正規表現に一致するかどうか調べればよいだけ return self.re.as_ref().unwrap().is_match(&event_value_str); @@ -515,9 +482,8 @@ mod tests { use super::super::selectionnodes::{ AndSelectionNode, LeafSelectionNode, OrSelectionNode, SelectionNode, }; - use crate::detections::detection::EvtxRecordInfo; use crate::detections::rule::tests::parse_rule_from_str; - use serde_json::Value; + use crate::detections::{self, utils}; #[test] fn test_rule_parse() { @@ -739,11 +705,8 @@ mod tests { match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -772,11 +735,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -805,11 +765,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -839,11 +796,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -873,11 +827,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -906,11 +857,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -939,11 +887,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -973,11 +918,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -1007,11 +949,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -1041,11 +980,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -1075,11 +1011,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -1109,11 +1042,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -1142,11 +1072,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -1179,11 +1106,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -1216,11 +1140,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -1252,11 +1173,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -1297,11 +1215,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_rec) => { @@ -1342,11 +1257,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_rec) => { @@ -1387,11 +1299,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_rec) => { @@ -1432,11 +1341,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_rec) => { @@ -1477,11 +1383,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_rec) => { @@ -1522,11 +1425,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_rec) => { @@ -1555,11 +1455,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -1588,11 +1485,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -1621,11 +1515,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -1711,13 +1602,9 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { - Ok(rec) => { - let rec: Value = rec; - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: rec, - data_string: record_json_str.to_string(), - }; + Ok(record) => { + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -1746,12 +1633,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let rec: Value = record; - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: rec, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -1782,11 +1665,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -1817,11 +1697,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { diff --git a/src/detections/rule/mod.rs b/src/detections/rule/mod.rs index c5a1f779..0b50e470 100644 --- a/src/detections/rule/mod.rs +++ b/src/detections/rule/mod.rs @@ -9,7 +9,7 @@ use yaml_rust::Yaml; mod matchers; mod selectionnodes; -use self::selectionnodes::SelectionNode; +use self::selectionnodes::{LeafSelectionNode, SelectionNode}; mod aggregation_parser; use self::aggregation_parser::AggregationParseInfo; @@ -93,6 +93,31 @@ impl RuleNode { } } +// RuleNodeのdetectionに定義されているキーの一覧を取得する。 +pub fn get_detection_keys(node: &RuleNode) -> Vec { + let mut ret = vec![]; + let detection = &node.detection; + for key in detection.name_to_selection.keys() { + let selection = &detection.name_to_selection[key]; + let desc = selection.get_descendants(); + let keys = desc.iter().filter_map(|node| { + if !node.is::() { + return Option::None; + } + + let node = node.downcast_ref::().unwrap(); + let key = node.get_key(); + if key.is_empty() { + return Option::None; + } + return Option::Some(key.to_string()); + }); + ret.extend(keys); + } + + return ret; +} + /// Ruleファイルのdetectionを表すノード struct DetectionNode { pub name_to_selection: HashMap>>, @@ -300,10 +325,9 @@ impl AggResult { #[cfg(test)] mod tests { - use crate::detections::{detection::EvtxRecordInfo, rule::create_rule}; - use yaml_rust::YamlLoader; - use super::RuleNode; + use crate::detections::{self, rule::create_rule, utils}; + use yaml_rust::YamlLoader; pub fn parse_rule_from_str(rule_str: &str) -> RuleNode { let rule_yaml = YamlLoader::load_from_str(rule_str); @@ -335,11 +359,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -368,11 +389,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -401,11 +419,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -487,11 +502,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -549,11 +561,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -618,11 +627,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -665,11 +671,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -713,11 +716,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -780,11 +780,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -847,11 +844,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -896,11 +890,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_rec) => { @@ -957,11 +948,8 @@ mod tests { let _init = rule_node.init(); match serde_json::from_str(record_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let result = rule_node.select(&"testpath".to_string(), &recinfo); assert_eq!(rule_node.detection.aggregation_condition.is_some(), true); assert_eq!(result, true); diff --git a/src/detections/rule/selectionnodes.rs b/src/detections/rule/selectionnodes.rs index f7446866..c33a4db0 100644 --- a/src/detections/rule/selectionnodes.rs +++ b/src/detections/rule/selectionnodes.rs @@ -1,6 +1,5 @@ use crate::detections::{detection::EvtxRecordInfo, utils}; use mopa::mopafy; -use serde_json::Value; use std::{sync::Arc, vec}; use yaml_rust::Yaml; @@ -268,13 +267,13 @@ impl LeafSelectionNode { } /// JSON形式のEventJSONから値を取得する関数 aliasも考慮されている。 - fn get_event_value<'a>(&self, event_value: &'a Value) -> Option<&'a Value> { + fn get_event_value<'a>(&self, record: &'a EvtxRecordInfo) -> Option<&'a String> { // keyが指定されたいない場合は if self.key_list.is_empty() { - return Option::Some(event_value); + return Option::Some(&record.data_string); } - return utils::get_event_value(&self.get_key(), event_value); + return record.get_value(self.get_key()); } /// matchers::LeafMatcherの一覧を取得する。 @@ -334,7 +333,7 @@ impl SelectionNode for LeafSelectionNode { .matcher .as_ref() .unwrap() - .is_match(Option::Some(eventdata_data), event_record); + .is_match(event_record.get_value(self.get_key()), event_record); } // 配列の場合は配列の要素のどれか一つでもルールに合致すれば条件に一致したことにする。 if eventdata_data.is_array() { @@ -343,11 +342,12 @@ impl SelectionNode for LeafSelectionNode { .unwrap() .iter() .any(|ary_element| { + let aryelement_val = utils::value_to_string(ary_element); return self .matcher .as_ref() .unwrap() - .is_match(Option::Some(ary_element), event_record); + .is_match(aryelement_val.as_ref(), event_record); }); } else { return self @@ -358,7 +358,7 @@ impl SelectionNode for LeafSelectionNode { } } - let event_value = self.get_event_value(&event_record.record); + let event_value = self.get_event_value(&event_record); return self .matcher .as_ref() @@ -407,7 +407,7 @@ impl SelectionNode for LeafSelectionNode { #[cfg(test)] mod tests { - use crate::detections::{detection::EvtxRecordInfo, rule::tests::parse_rule_from_str}; + use crate::detections::{self, rule::tests::parse_rule_from_str, utils}; #[test] fn test_detect_mutiple_regex_and() { @@ -430,11 +430,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -466,11 +463,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { @@ -501,11 +495,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -536,11 +527,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), true); } Err(_) => { @@ -571,11 +559,8 @@ mod tests { let mut rule_node = parse_rule_from_str(rule_str); match serde_json::from_str(record_json_str) { Ok(record) => { - let recinfo = EvtxRecordInfo { - evtx_filepath: "testpath".to_owned(), - record: record, - data_string: record_json_str.to_string(), - }; + let keys = detections::rule::get_detection_keys(&rule_node); + let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); assert_eq!(rule_node.select(&"testpath".to_owned(), &recinfo), false); } Err(_) => { diff --git a/src/detections/utils.rs b/src/detections/utils.rs index c7e4c57b..0ba39dd4 100644 --- a/src/detections/utils.rs +++ b/src/detections/utils.rs @@ -16,6 +16,8 @@ use std::io::{BufRead, BufReader}; use std::str; use std::string::String; +use super::detection::EvtxRecordInfo; + pub fn concat_selection_key(key_list: &Vec) -> String { return key_list .iter() @@ -47,6 +49,17 @@ pub fn check_allowlist(target: &str, regexes: &Vec) -> bool { return false; } +pub fn value_to_string(value: &Value) -> Option { + return match value { + Value::Null => Option::None, + Value::Bool(b) => Option::Some(b.to_string()), + Value::Number(n) => Option::Some(n.to_string()), + Value::String(s) => Option::Some(s.to_string()), + Value::Array(_) => Option::None, + Value::Object(_) => Option::None, + }; +} + pub fn read_txt(filename: &str) -> Result, String> { let f = File::open(filename); if f.is_err() { @@ -184,6 +197,41 @@ pub fn create_tokio_runtime() -> Runtime { .unwrap(); } +// EvtxRecordInfoを作成します。 +pub fn create_rec_info(data: Value, path: String, keys: &Vec) -> EvtxRecordInfo { + // EvtxRecordInfoを作る + let data_str = data.to_string(); + let mut rec = EvtxRecordInfo { + evtx_filepath: path, + record: data, + data_string: data_str, + key_2_value: hashbrown::HashMap::new(), + }; + + // 高速化のための処理 + + // 例えば、Value型から"Event.System.EventID"の値を取得しようとすると、value["Event"]["System"]["EventID"]のように3回アクセスする必要がある。 + // この処理を高速化するため、rec.key_2_valueというhashmapに"Event.System.EventID"というキーで値を設定しておく。 + // これなら、"Event.System.EventID"というキーを1回指定するだけで値を取得できるようになるので、高速化されるはず。 + // あと、serde_jsonのValueからvalue["Event"]みたいな感じで値を取得する処理がなんか遅いので、そういう意味でも早くなるかも + // それと、serde_jsonでは内部的に標準ライブラリのhashmapを使用しているが、hashbrownを使った方が早くなるらしい。 + for key in keys { + let val = get_event_value(key, &rec.record); + if val.is_none() { + continue; + } + + let val = value_to_string(val.unwrap()); + if val.is_none() { + continue; + } + + rec.key_2_value.insert(key.to_string(), val.unwrap()); + } + + return rec; +} + #[cfg(test)] mod tests { use crate::detections::utils; diff --git a/src/main.rs b/src/main.rs index 7bce8cc9..efe79b34 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,9 +4,9 @@ extern crate serde_derive; use chrono::Datelike; use chrono::{DateTime, Local}; use evtx::{EvtxParser, ParserSettings}; -use hayabusa::detections::detection; -use hayabusa::detections::detection::EvtxRecordInfo; +use hayabusa::detections::detection::{self, EvtxRecordInfo}; use hayabusa::detections::print::AlertMessage; +use hayabusa::detections::rule::{get_detection_keys, RuleNode}; use hayabusa::filter; use hayabusa::omikuji::Omikuji; use hayabusa::{afterfact::after_fact, detections::utils}; @@ -14,315 +14,344 @@ use hayabusa::{detections::configs, timeline::timeline::Timeline}; use hhmmss::Hhmmss; use pbr::ProgressBar; use serde_json::Value; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::fmt::Display; +use std::sync::Arc; use std::{ fs::{self, File}, path::PathBuf, vec, }; +use tokio::runtime::Runtime; +use tokio::spawn; +use tokio::task::JoinHandle; // 一度にtimelineやdetectionを実行する行数 const MAX_DETECT_RECORDS: usize = 5000; fn main() { - let analysis_start_time: DateTime = Local::now(); - if !configs::CONFIG.read().unwrap().args.is_present("q") { - output_logo(); - println!(""); - output_eggs(&format!( - "{:02}/{:02}", - &analysis_start_time.month().to_owned(), - &analysis_start_time.day().to_owned() - )); - } - if configs::CONFIG.read().unwrap().args.args.len() == 0 { - println!( - "{}", - configs::CONFIG.read().unwrap().args.usage().to_string() - ); - return; - } - if let Some(filepath) = configs::CONFIG.read().unwrap().args.value_of("filepath") { - if !filepath.ends_with(".evtx") { - AlertMessage::alert( - &mut std::io::stderr().lock(), - "--filepath only accepts .evtx files.".to_owned(), - ) - .ok(); - return; - } - analysis_files(vec![PathBuf::from(filepath)]); - } else if let Some(directory) = configs::CONFIG.read().unwrap().args.value_of("directory") { - let evtx_files = collect_evtxfiles(&directory); - if evtx_files.len() == 0 { - AlertMessage::alert( - &mut std::io::stderr().lock(), - "No .evtx files were found.".to_owned(), - ) - .ok(); - return; - } - analysis_files(evtx_files); - } else if configs::CONFIG - .read() - .unwrap() - .args - .is_present("contributors") - { - print_contributors(); - return; - } - let analysis_end_time: DateTime = Local::now(); - let analysis_duration = analysis_end_time.signed_duration_since(analysis_start_time); - println!("Elapsed Time: {}", &analysis_duration.hhmmssxxx()); - println!(""); + let mut app = App::new(); + app.exec(); + app.rt.shutdown_background(); } -fn collect_evtxfiles(dirpath: &str) -> Vec { - let entries = fs::read_dir(dirpath); - if entries.is_err() { - let stderr = std::io::stderr(); - let mut stderr = stderr.lock(); - AlertMessage::alert(&mut stderr, format!("{}", entries.unwrap_err())).ok(); - return vec![]; +pub struct App { + rt: Runtime, + rule_keys: Vec, +} + +impl App { + pub fn new() -> App { + return App { + rt: utils::create_tokio_runtime(), + rule_keys: Vec::new(), + }; } - let mut ret = vec![]; - for e in entries.unwrap() { - if e.is_err() { - continue; + fn exec(&mut self) { + let analysis_start_time: DateTime = Local::now(); + if !configs::CONFIG.read().unwrap().args.is_present("q") { + self.output_logo(); + println!(""); + self.output_eggs(&format!( + "{:02}/{:02}", + &analysis_start_time.month().to_owned(), + &analysis_start_time.day().to_owned() + )); + } + if configs::CONFIG.read().unwrap().args.args.len() == 0 { + println!( + "{}", + configs::CONFIG.read().unwrap().args.usage().to_string() + ); + return; + } + if let Some(filepath) = configs::CONFIG.read().unwrap().args.value_of("filepath") { + if !filepath.ends_with(".evtx") { + AlertMessage::alert( + &mut std::io::stderr().lock(), + "--filepath only accepts .evtx files.".to_owned(), + ) + .ok(); + return; + } + self.analysis_files(vec![PathBuf::from(filepath)]); + } else if let Some(directory) = configs::CONFIG.read().unwrap().args.value_of("directory") { + let evtx_files = self.collect_evtxfiles(&directory); + if evtx_files.len() == 0 { + AlertMessage::alert( + &mut std::io::stderr().lock(), + "No .evtx files were found.".to_owned(), + ) + .ok(); + return; + } + self.analysis_files(evtx_files); + } else if configs::CONFIG + .read() + .unwrap() + .args + .is_present("contributors") + { + self.print_contributors(); + return; + } + let analysis_end_time: DateTime = Local::now(); + let analysis_duration = analysis_end_time.signed_duration_since(analysis_start_time); + println!("Elapsed Time: {}", &analysis_duration.hhmmssxxx()); + println!(""); + } + + fn collect_evtxfiles(&self, dirpath: &str) -> Vec { + let entries = fs::read_dir(dirpath); + if entries.is_err() { + let stderr = std::io::stderr(); + let mut stderr = stderr.lock(); + AlertMessage::alert(&mut stderr, format!("{}", entries.unwrap_err())).ok(); + return vec![]; } - let path = e.unwrap().path(); - if path.is_dir() { - path.to_str().and_then(|path_str| { - let subdir_ret = collect_evtxfiles(path_str); - ret.extend(subdir_ret); - return Option::Some(()); - }); - } else { - let path_str = path.to_str().unwrap_or(""); - if path_str.ends_with(".evtx") { - ret.push(path); + let mut ret = vec![]; + for e in entries.unwrap() { + if e.is_err() { + continue; + } + + let path = e.unwrap().path(); + if path.is_dir() { + path.to_str().and_then(|path_str| { + let subdir_ret = self.collect_evtxfiles(path_str); + ret.extend(subdir_ret); + return Option::Some(()); + }); + } else { + let path_str = path.to_str().unwrap_or(""); + if path_str.ends_with(".evtx") { + ret.push(path); + } + } + } + + return ret; + } + + fn print_contributors(&self) { + match fs::read_to_string("./contributors.txt") { + Ok(contents) => println!("{}", contents), + Err(err) => { + AlertMessage::alert(&mut std::io::stderr().lock(), format!("{}", err)).ok(); } } } - return ret; -} + fn analysis_files(&mut self, evtx_files: Vec) { + let level = configs::CONFIG + .read() + .unwrap() + .args + .value_of("min-level") + .unwrap_or("informational") + .to_uppercase(); + println!("Analyzing event files: {:?}", evtx_files.len()); -fn print_contributors() { - match fs::read_to_string("./contributors.txt") { - Ok(contents) => println!("{}", contents), - Err(err) => { - AlertMessage::alert(&mut std::io::stderr().lock(), format!("{}", err)).ok(); + let rule_files = detection::Detection::parse_rule_files( + level, + configs::CONFIG.read().unwrap().args.value_of("rules"), + &filter::exclude_ids(), + ); + let mut pb = ProgressBar::new(evtx_files.len() as u64); + self.rule_keys = self.get_all_keys(&rule_files); + let mut detection = detection::Detection::new(rule_files); + for evtx_file in evtx_files { + if configs::CONFIG.read().unwrap().args.is_present("verbose") { + println!("Checking target evtx FilePath: {:?}", &evtx_file); + } + detection = self.analysis_file(evtx_file, detection); + pb.inc(); } + after_fact(); + detection.print_unique_results(); } -} -fn analysis_files(evtx_files: Vec) { - let level = configs::CONFIG - .read() - .unwrap() - .args - .value_of("min-level") - .unwrap_or("informational") - .to_uppercase(); - println!("Analyzing event files: {:?}", evtx_files.len()); - - let rule_files = detection::Detection::parse_rule_files( - level, - configs::CONFIG.read().unwrap().args.value_of("rules"), - &filter::exclude_ids(), - ); - let mut pb = ProgressBar::new(evtx_files.len() as u64); - let mut detection = detection::Detection::new(rule_files); - for evtx_file in evtx_files { - if configs::CONFIG.read().unwrap().args.is_present("verbose") { - println!("Checking target evtx FilePath: {:?}", &evtx_file); + // Windowsイベントログファイルを1ファイル分解析する。 + fn analysis_file( + &self, + evtx_filepath: PathBuf, + mut detection: detection::Detection, + ) -> detection::Detection { + let path = evtx_filepath.display(); + let parser = self.evtx_to_jsons(evtx_filepath.clone()); + if parser.is_none() { + return detection; } - detection = analysis_file(evtx_file, detection); - pb.inc(); - } - after_fact(); - detection.print_unique_results(); -} -// Windowsイベントログファイルを1ファイル分解析する。 -fn analysis_file( - evtx_filepath: PathBuf, - mut detection: detection::Detection, -) -> detection::Detection { - let filepath_disp = evtx_filepath.display(); - let parser = evtx_to_jsons(evtx_filepath.clone()); - if parser.is_none() { - return detection; - } + let mut tl = Timeline::new(); + let mut parser = parser.unwrap(); + let mut records = parser.records_json_value(); - let mut tl = Timeline::new(); - let mut parser = parser.unwrap(); - let mut records = parser.records_json_value(); - let tokio_rt = utils::create_tokio_runtime(); + loop { + let mut records_per_detect = vec![]; + while records_per_detect.len() < MAX_DETECT_RECORDS { + // パースに失敗している場合、エラーメッセージを出力 + let next_rec = records.next(); + if next_rec.is_none() { + break; + } - let target_event_time = configs::TargetEventTime::new(); + let record_result = next_rec.unwrap(); + if record_result.is_err() { + let evtx_filepath = &path; + let errmsg = format!( + "Failed to parse event file. EventFile:{} Error:{}", + evtx_filepath, + record_result.unwrap_err() + ); + AlertMessage::alert(&mut std::io::stderr().lock(), errmsg).ok(); + continue; + } - loop { - let mut records_per_detect = vec![]; - while records_per_detect.len() < MAX_DETECT_RECORDS { - // パースに失敗している場合、エラーメッセージを出力 - let next_rec = records.next(); - if next_rec.is_none() { + // target_eventids.txtでフィルタする。 + let data = record_result.unwrap().data; + if self._is_target_event_id(&data) == false { + continue; + } + + // EvtxRecordInfo構造体に変更 + records_per_detect.push(data); + } + if records_per_detect.len() == 0 { break; } - let record_result = next_rec.unwrap(); - if record_result.is_err() { - let evtx_filepath = &filepath_disp; - let errmsg = format!( - "Failed to parse event file. EventFile:{} Error:{}", - evtx_filepath, - record_result.unwrap_err() - ); - AlertMessage::alert(&mut std::io::stderr().lock(), errmsg).ok(); - continue; + let records_per_detect = self.rt.block_on(App::create_rec_infos( + records_per_detect, + &path, + self.rule_keys.clone(), + )); + + // // timeline機能の実行 + tl.start(&records_per_detect); + + // // ruleファイルの検知 + detection = detection.start(&self.rt, records_per_detect); + } + + detection.add_aggcondtion_msg(); + tl.tm_stats_dsp_msg(); + + return detection; + } + + async fn create_rec_infos( + records_per_detect: Vec, + path: &dyn Display, + rule_keys: Vec, + ) -> Vec { + let path = Arc::new(path.to_string()); + let rule_keys = Arc::new(rule_keys); + let threads: Vec> = records_per_detect + .into_iter() + .map(|rec| { + let arc_rule_keys = Arc::clone(&rule_keys); + let arc_path = Arc::clone(&path); + return spawn(async move { + let rec_info = + utils::create_rec_info(rec, arc_path.to_string(), &arc_rule_keys); + return rec_info; + }); + }) + .collect(); + + let mut ret = vec![]; + for thread in threads.into_iter() { + ret.push(thread.await.unwrap()); + } + + return ret; + } + + fn get_all_keys(&self, rules: &Vec) -> Vec { + let mut key_set = HashSet::new(); + for rule in rules { + let keys = get_detection_keys(rule); + key_set.extend(keys); + } + + let ret: Vec = key_set.into_iter().collect(); + return ret; + } + + // target_eventids.txtの設定を元にフィルタする。 + fn _is_target_event_id(&self, data: &Value) -> bool { + let eventid = utils::get_event_value(&utils::get_event_id_key(), data); + if eventid.is_none() { + return true; + } + + return match eventid.unwrap() { + Value::String(s) => utils::is_target_event_id(s), + Value::Number(n) => utils::is_target_event_id(&n.to_string()), + _ => true, // レコードからEventIdが取得できない場合は、特にフィルタしない + }; + } + + fn evtx_to_jsons(&self, evtx_filepath: PathBuf) -> Option> { + match EvtxParser::from_path(evtx_filepath) { + Ok(evtx_parser) => { + // parserのデフォルト設定を変更 + let mut parse_config = ParserSettings::default(); + parse_config = parse_config.separate_json_attributes(true); // XMLのattributeをJSONに変換する時のルールを設定 + parse_config = parse_config.num_threads(0); // 設定しないと遅かったので、設定しておく。 + + let evtx_parser = evtx_parser.with_configuration(parse_config); + return Option::Some(evtx_parser); } - - // target_eventids.txtでフィルタする。 - let data = record_result.unwrap().data; - if _is_target_event_id(&data) == false { - continue; + Err(e) => { + eprintln!("{}", e); + return Option::None; } + } + } - let eventtime = utils::get_event_value(&utils::get_event_time(), &data); - if eventtime.is_some() { - let time = utils::str_time_to_datetime(eventtime.unwrap().as_str().unwrap_or("")); - if !target_event_time.is_target(&time) { - continue; - } + fn _output_with_omikuji(&self, omikuji: Omikuji) { + let fp = &format!("art/omikuji/{}", omikuji); + let content = fs::read_to_string(fp).unwrap(); + println!("{}", content); + } + + /// output logo + fn output_logo(&self) { + let fp = &format!("art/logo.txt"); + let content = fs::read_to_string(fp).unwrap_or("".to_owned()); + println!("{}", content); + } + + /// output easter egg arts + fn output_eggs(&self, exec_datestr: &str) { + let mut eggs: HashMap<&str, &str> = HashMap::new(); + eggs.insert("01/01", "art/happynewyear.txt"); + eggs.insert("02/22", "art/ninja.txt"); + eggs.insert("08/08", "art/takoyaki.txt"); + eggs.insert("12/25", "art/christmas.txt"); + + match eggs.get(exec_datestr) { + None => {} + Some(path) => { + let content = fs::read_to_string(path).unwrap_or("".to_owned()); + println!("{}", content); } - - // EvtxRecordInfo構造体に変更 - records_per_detect.push(_create_rec_info(data, &filepath_disp)); - } - if records_per_detect.len() == 0 { - break; - } - - // timeline機能の実行 - tl.start(&records_per_detect); - - // ruleファイルの検知 - detection = detection.start(&tokio_rt, records_per_detect); - } - - tokio_rt.shutdown_background(); - detection.add_aggcondtion_msg(); - tl.tm_stats_dsp_msg(); - - return detection; -} - -// target_eventids.txtの設定を元にフィルタする。 -fn _is_target_event_id(data: &Value) -> bool { - let eventid = utils::get_event_value(&utils::get_event_id_key(), data); - if eventid.is_none() { - return true; - } - - return match eventid.unwrap() { - Value::String(s) => utils::is_target_event_id(s), - Value::Number(n) => utils::is_target_event_id(&n.to_string()), - _ => true, // レコードからEventIdが取得できない場合は、特にフィルタしない - }; -} - -// EvtxRecordInfoを作成します。 -fn _create_rec_info(mut data: Value, path: &dyn Display) -> EvtxRecordInfo { - // 高速化のための処理 - // RuleNodeでワイルドカードや正規表現のマッチング処理をする際には、 - // Value(JSON)がstring型以外の場合はstringに変換して比較している。 - // RuleNodeでマッチングする毎にstring変換していると、 - // 1回の処理はそこまででもないが相当回数呼び出されれるとボトルネックになりうる。 - - // なので、よく使われるstring型ではない値を事前に変換しておくことで、 - // string変換する回数を減らせる。 - // 本当はやりたくないが... - match &data["Event"]["System"]["EventID"] { - Value::Number(n) => data["Event"]["System"]["EventID"] = Value::String(n.to_string()), - _ => (), - }; - match &data["Event"]["EventData"]["LogonType"] { - Value::Number(n) => data["Event"]["EventData"]["LogonType"] = Value::String(n.to_string()), - _ => (), - } - match &data["Event"]["EventData"]["DestinationPort"] { - Value::Number(n) => { - data["Event"]["EventData"]["DestinationPort"] = Value::String(n.to_string()) - } - _ => (), - } - - // EvtxRecordInfoを作る - let data_str = data.to_string(); - return EvtxRecordInfo::new(path.to_string(), data, data_str); -} - -fn evtx_to_jsons(evtx_filepath: PathBuf) -> Option> { - match EvtxParser::from_path(evtx_filepath) { - Ok(evtx_parser) => { - // parserのデフォルト設定を変更 - let mut parse_config = ParserSettings::default(); - parse_config = parse_config.separate_json_attributes(true); // XMLのattributeをJSONに変換する時のルールを設定 - parse_config = parse_config.num_threads(utils::get_thread_num()); // 設定しないと遅かったので、設定しておく。 - - let evtx_parser = evtx_parser.with_configuration(parse_config); - return Option::Some(evtx_parser); - } - Err(e) => { - eprintln!("{}", e); - return Option::None; - } - } -} - -fn _output_with_omikuji(omikuji: Omikuji) { - let fp = &format!("art/omikuji/{}", omikuji); - let content = fs::read_to_string(fp).unwrap(); - println!("{}", content); -} - -/// output logo -fn output_logo() { - let fp = &format!("art/logo.txt"); - let content = fs::read_to_string(fp).unwrap_or("".to_owned()); - println!("{}", content); -} - -/// output easter egg arts -fn output_eggs(exec_datestr: &str) { - let mut eggs: HashMap<&str, &str> = HashMap::new(); - eggs.insert("01/01", "art/happynewyear.txt"); - eggs.insert("02/22", "art/ninja.txt"); - eggs.insert("08/08", "art/takoyaki.txt"); - eggs.insert("12/25", "art/christmas.txt"); - - match eggs.get(exec_datestr) { - None => {} - Some(path) => { - let content = fs::read_to_string(path).unwrap_or("".to_owned()); - println!("{}", content); } } } #[cfg(test)] mod tests { - use crate::collect_evtxfiles; + use crate::App; #[test] fn test_collect_evtxfiles() { - let files = collect_evtxfiles("test_files/evtx"); + let app = App::new(); + let files = app.collect_evtxfiles("test_files/evtx"); assert_eq!(3, files.len()); files.iter().for_each(|file| { From cc14b7e4acf7ae136f680d8b2d58349ba2c74f41 Mon Sep 17 00:00:00 2001 From: DustInDark Date: Sat, 18 Dec 2021 11:59:16 +0900 Subject: [PATCH 17/19] Feature/improve output#253 (#285) * changed processing time pre code #253 - changed csv file writer to BufWriter * changed processing time pre code in stdout #253 --- src/afterfact.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/afterfact.rs b/src/afterfact.rs index 72d2aabb..1d9217b4 100644 --- a/src/afterfact.rs +++ b/src/afterfact.rs @@ -6,6 +6,7 @@ use serde::Serialize; use std::error::Error; use std::fs::File; use std::io; +use std::io::BufWriter; use std::process; #[derive(Debug, Serialize)] @@ -50,7 +51,7 @@ pub fn after_fact() { { // ファイル出力する場合 match File::create(csv_path) { - Ok(file) => Box::new(file), + Ok(file) => Box::new(BufWriter::new(file)), Err(err) => { AlertMessage::alert( &mut std::io::stderr().lock(), @@ -63,7 +64,7 @@ pub fn after_fact() { } else { displayflag = true; // 標準出力に出力する場合 - Box::new(io::stdout()) + Box::new(BufWriter::new(io::stdout())) }; if let Err(err) = emit_csv(&mut target, displayflag) { From c01dcbfc94f582a1b97e5f2d669d6801a409f22d Mon Sep 17 00:00:00 2001 From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> Date: Sat, 18 Dec 2021 12:14:23 +0900 Subject: [PATCH 18/19] =?UTF-8?q?Logo=E3=81=AE=E5=BE=AE=E8=AA=BF=E6=95=B4?= =?UTF-8?q?=20(#300)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- art/logo.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/art/logo.txt b/art/logo.txt index 3e2f102e..bd972b3b 100644 --- a/art/logo.txt +++ b/art/logo.txt @@ -1,7 +1,8 @@ + ██╗ ██╗ █████╗ ██╗ ██╗ █████╗ ██████╗ ██╗ ██╗███████╗ █████╗ ██║ ██║██╔══██╗╚██╗ ██╔╝██╔══██╗██╔══██╗██║ ██║██╔════╝██╔══██╗ ███████║███████║ ╚████╔╝ ███████║██████╔╝██║ ██║███████╗███████║ ██╔══██║██╔══██║ ╚██╔╝ ██╔══██║██╔══██╗██║ ██║╚════██║██╔══██║ ██║ ██║██║ ██║ ██║ ██║ ██║██████╔╝╚██████╔╝███████║██║ ██║ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ -by Yamato Security \ No newline at end of file + by Yamato Security \ No newline at end of file From 692fdae9a06b89265620878fd1ca6e80d5eda7e9 Mon Sep 17 00:00:00 2001 From: DustInDark Date: Sun, 19 Dec 2021 15:36:24 +0900 Subject: [PATCH 19/19] RevertedMerge: Feature/remove process speed#289 (#299) * removed process-speed view in progress bar #289 * insert changed code after resolve conflict #289 --- src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.rs b/src/main.rs index efe79b34..a48d4028 100644 --- a/src/main.rs +++ b/src/main.rs @@ -160,6 +160,7 @@ impl App { &filter::exclude_ids(), ); let mut pb = ProgressBar::new(evtx_files.len() as u64); + pb.show_speed = false; self.rule_keys = self.get_all_keys(&rule_files); let mut detection = detection::Detection::new(rule_files); for evtx_file in evtx_files {