diff --git a/src/detections/configs.rs b/src/detections/configs.rs index 6f7fc38d..601210e0 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -73,24 +73,24 @@ impl EventKeyAliasConfig { fn load_eventkey_alias() -> EventKeyAliasConfig { let mut config = EventKeyAliasConfig::new(); - utils::read_csv("config/eventkey_alias.txt") - .into_iter() - .for_each(|line| { - if line.len() != 2 { - return; - } + let read_result = utils::read_csv("config/eventkey_alias.txt"); + // eventkey_alisasが読み込めなかったらエラーで終了とする。 + read_result.unwrap().into_iter().for_each(|line| { + if line.len() != 2 { + return; + } - let empty = &"".to_string(); - let alias = line.get(0).unwrap_or(empty); - let event_key = line.get(1).unwrap_or(empty); - if alias.len() == 0 || event_key.len() == 0 { - return; - } + let empty = &"".to_string(); + let alias = line.get(0).unwrap_or(empty); + let event_key = line.get(1).unwrap_or(empty); + if alias.len() == 0 || event_key.len() == 0 { + return; + } - config - .key_to_eventkey - .insert(alias.to_owned(), event_key.to_owned()); - }); + config + .key_to_eventkey + .insert(alias.to_owned(), event_key.to_owned()); + }); return config; } diff --git a/src/detections/rule.rs b/src/detections/rule.rs index ccdf89fa..f8ffbeef 100644 --- a/src/detections/rule.rs +++ b/src/detections/rule.rs @@ -246,7 +246,12 @@ impl LeafSelectionNode { // LeafMatcherの一覧を取得する。 fn get_matchers(&self) -> Vec> { - return vec![Box::new(RegexMatcher::new())]; + return vec![ + Box::new(RegexMatcher::new()), + Box::new(MinlengthMatcher::new()), + Box::new(RegexesFileMatcher::new()), + Box::new(WhitelistFileMatcher::new()), + ]; } } @@ -275,6 +280,13 @@ impl SelectionNode for LeafSelectionNode { )]); } + if self.select_value.is_badvalue() { + return Result::Err(vec![format!( + "Cannot parse yaml file. key:{}", + concat_selection_key(&match_key_list) + )]); + } + return self .matcher .as_mut() @@ -316,7 +328,15 @@ impl RegexMatcher { impl LeafMatcher for RegexMatcher { fn is_target_key(&self, key_list: &Vec) -> bool { - return key_list.is_empty(); + if key_list.is_empty() { + return true; + } + + if key_list.len() == 1 { + return key_list.get(0).unwrap_or(&"".to_string()) == &"regex".to_string(); + } else { + return false; + } } fn init(&mut self, key_list: &Vec, select_value: &Yaml) -> Result<(), Vec> { @@ -378,3 +398,170 @@ impl LeafMatcher for RegexMatcher { }; } } + +// 指定された文字数以上であることをチェックするクラス。 +struct MinlengthMatcher { + min_len: i64, +} + +impl MinlengthMatcher { + fn new() -> MinlengthMatcher { + return MinlengthMatcher { min_len: 0 }; + } +} + +impl LeafMatcher for MinlengthMatcher { + fn is_target_key(&self, key_list: &Vec) -> bool { + if key_list.len() != 1 { + return false; + } + + return key_list.get(0).unwrap() == "min_length"; + } + + fn init(&mut self, key_list: &Vec, select_value: &Yaml) -> Result<(), Vec> { + let min_length = select_value.as_i64(); + if min_length.is_none() { + let errmsg = format!( + "min_length value should be Integer. [key:{}]", + concat_selection_key(key_list) + ); + return Result::Err(vec![errmsg]); + } + + self.min_len = min_length.unwrap(); + return Result::Ok(()); + } + + fn is_match(&self, event_value: Option<&Value>) -> 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, + }; + } +} + +// 正規表現のリストが記載されたファイルを読み取って、比較するロジックを表すクラス +// DeepBlueCLIのcheck_cmdメソッドの一部に同様の処理が実装されていた。 +struct RegexesFileMatcher { + regexes_csv_content: Vec>, +} + +impl RegexesFileMatcher { + fn new() -> RegexesFileMatcher { + return RegexesFileMatcher { + regexes_csv_content: vec![], + }; + } +} + +impl LeafMatcher for RegexesFileMatcher { + fn is_target_key(&self, key_list: &Vec) -> bool { + if key_list.len() != 1 { + return false; + } + + return key_list.get(0).unwrap() == "regexes"; + } + + fn init(&mut self, key_list: &Vec, select_value: &Yaml) -> Result<(), Vec> { + let value = match select_value { + Yaml::String(s) => Option::Some(s.to_owned()), + Yaml::Integer(i) => Option::Some(i.to_string()), + Yaml::Real(r) => Option::Some(r.to_owned()), + _ => Option::None, + }; + if value.is_none() { + let errmsg = format!( + "regexes value should be String. [key:{}]", + concat_selection_key(key_list) + ); + return Result::Err(vec![errmsg]); + } + + let csv_content = utils::read_csv(&value.unwrap()); + if csv_content.is_err() { + let errmsg = format!( + "cannot read regexes file. [key:{}]", + concat_selection_key(key_list) + ); + return Result::Err(vec![errmsg]); + } + self.regexes_csv_content = csv_content.unwrap(); + + return Result::Ok(()); + } + + fn is_match(&self, event_value: Option<&Value>) -> bool { + return match event_value.unwrap_or(&Value::Null) { + Value::String(s) => !utils::check_regex(s, 0, &self.regexes_csv_content).is_empty(), + Value::Number(n) => { + !utils::check_regex(&n.to_string(), 0, &self.regexes_csv_content).is_empty() + } + _ => false, + }; + } +} + +// ファイルに列挙された文字列に一致しない場合に検知するロジックを表す +// DeepBlueCLIのcheck_cmdメソッドの一部に同様の処理が実装されていた。 +struct WhitelistFileMatcher { + whitelist_csv_content: Vec>, +} + +impl WhitelistFileMatcher { + fn new() -> WhitelistFileMatcher { + return WhitelistFileMatcher { + whitelist_csv_content: vec![], + }; + } +} + +impl LeafMatcher for WhitelistFileMatcher { + fn is_target_key(&self, key_list: &Vec) -> bool { + if key_list.len() != 1 { + return false; + } + + return key_list.get(0).unwrap() == "whitelist"; + } + + fn init(&mut self, key_list: &Vec, select_value: &Yaml) -> Result<(), Vec> { + let value = match select_value { + Yaml::String(s) => Option::Some(s.to_owned()), + Yaml::Integer(i) => Option::Some(i.to_string()), + Yaml::Real(r) => Option::Some(r.to_owned()), + _ => Option::None, + }; + if value.is_none() { + let errmsg = format!( + "whitelist value should be String. [key:{}]", + concat_selection_key(key_list) + ); + return Result::Err(vec![errmsg]); + } + + let csv_content = utils::read_csv(&value.unwrap()); + if csv_content.is_err() { + let errmsg = format!( + "cannot read whitelist file. [key:{}]", + concat_selection_key(key_list) + ); + return Result::Err(vec![errmsg]); + } + self.whitelist_csv_content = csv_content.unwrap(); + + return Result::Ok(()); + } + + fn is_match(&self, event_value: Option<&Value>) -> bool { + return match event_value.unwrap_or(&Value::Null) { + Value::String(s) => !utils::check_regex(s, 0, &self.whitelist_csv_content).is_empty(), + Value::Number(n) => { + !utils::check_regex(&n.to_string(), 0, &self.whitelist_csv_content).is_empty() + } + _ => false, + }; + } +} diff --git a/src/detections/utils.rs b/src/detections/utils.rs index db3ddc97..704716a6 100644 --- a/src/detections/utils.rs +++ b/src/detections/utils.rs @@ -63,12 +63,13 @@ pub fn check_whitelist(target: &str, whitelist: &Vec>) -> bool { return false; } -pub fn read_csv(filename: &str) -> Vec> { +pub fn read_csv(filename: &str) -> Result>, String> { let mut f = File::open(filename).expect("file not found!!!"); let mut contents: String = String::new(); let mut ret = vec![]; + let read_res = f.read_to_string(&mut contents); if f.read_to_string(&mut contents).is_err() { - return ret; + return Result::Err(read_res.unwrap_err().to_string()); } let mut rdr = csv::Reader::from_reader(contents.as_bytes()); @@ -83,7 +84,7 @@ pub fn read_csv(filename: &str) -> Vec> { ret.push(v); }); - return ret; + return Result::Ok(ret); } pub fn get_event_value<'a>(key: &String, event_value: &'a Value) -> Option<&'a Value> { @@ -113,7 +114,7 @@ mod tests { use crate::detections::utils; #[test] fn test_check_regex() { - let regexes = utils::read_csv("regexes.txt"); + let regexes = utils::read_csv("regexes.txt").unwrap(); let regextext = utils::check_regex("\\cvtres.exe", 0, ®exes); assert!(regextext == "Resource File To COFF Object Conversion Utility cvtres.exe\n"); @@ -124,7 +125,7 @@ mod tests { #[test] fn test_check_whitelist() { let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate.exe\""; - let whitelist = utils::read_csv("whitelist.txt"); + let whitelist = utils::read_csv("whitelist.txt").unwrap(); assert!(true == utils::check_whitelist(commandline, &whitelist)); let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate2.exe\"";