diff --git a/config/exclude-rules.txt b/config/exclude-rules.txt new file mode 100644 index 00000000..e69de29b diff --git a/config/noisy-rules.txt b/config/noisy-rules.txt new file mode 100644 index 00000000..e69de29b diff --git a/src/detections/configs.rs b/src/detections/configs.rs index 02499876..ce158ba2 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -58,6 +58,7 @@ fn build_app<'a>() -> ArgMatches<'a> { -u --utc 'Output time in UTC format (default: local time)' -d --directory=[DIRECTORY] 'Directory of multiple .evtx files' -s --statistics 'Prints statistics of event IDs' + -n --show-noisyalerts 'do not exclude noisy rules' -t --threadnum=[NUM] 'Thread number (default: optimal number for performance)' --contributors 'Prints the list of contributors'"; App::new(&program) diff --git a/src/detections/detection.rs b/src/detections/detection.rs index b6233769..7e610c42 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -11,6 +11,7 @@ use crate::detections::print::MESSAGES; use crate::detections::rule; use crate::detections::rule::RuleNode; use crate::detections::utils::get_serde_number_to_string; +use crate::fillter::RuleFill; use crate::yaml::ParseYaml; use std::sync::Arc; @@ -51,10 +52,15 @@ impl Detection { } // ルールファイルをパースします。 - pub fn parse_rule_files(level: String, rulespath: Option<&str>) -> Vec { + pub fn parse_rule_files( + level: String, + rulespath: Option<&str>, + fill_ids: RuleFill, + ) -> Vec { // ルールファイルのパースを実行 let mut rulefile_loader = ParseYaml::new(); - let result_readdir = rulefile_loader.read_dir(rulespath.unwrap_or(DIRPATH_RULES), &level); + let result_readdir = + rulefile_loader.read_dir(rulespath.unwrap_or(DIRPATH_RULES), &level, fill_ids); if result_readdir.is_err() { AlertMessage::alert( &mut std::io::stderr().lock(), diff --git a/src/fillter.rs b/src/fillter.rs new file mode 100644 index 00000000..0cd68b2a --- /dev/null +++ b/src/fillter.rs @@ -0,0 +1,6 @@ +use std::collections::HashMap; + +#[derive(Clone, Debug)] +pub struct RuleFill { + pub no_use_rule: HashMap, +} diff --git a/src/lib.rs b/src/lib.rs index 044dac24..9bf20692 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ pub mod afterfact; pub mod detections; +pub mod fillter; pub mod notify; pub mod omikuji; pub mod timeline; diff --git a/src/main.rs b/src/main.rs index 6b6b34c0..12d4c8a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,10 +6,12 @@ use evtx::{EvtxParser, ParserSettings}; use hayabusa::detections::detection; use hayabusa::detections::detection::EvtxRecordInfo; use hayabusa::detections::print::AlertMessage; +use hayabusa::fillter; use hayabusa::omikuji::Omikuji; use hayabusa::{afterfact::after_fact, detections::utils}; use hayabusa::{detections::configs, timeline::timeline::Timeline}; use hhmmss::Hhmmss; +use std::collections::HashMap; use std::{ fs::{self, File}, path::PathBuf, @@ -119,9 +121,29 @@ fn analysis_files(evtx_files: Vec) { .unwrap_or("INFO") .to_uppercase(); println!("Analyzing event files: {:?}", evtx_files.len()); + + //除外ルール前処理 + let mut ids = String::from_utf8(fs::read("config/exclude-rules.txt").unwrap()).unwrap(); + if !configs::CONFIG + .read() + .unwrap() + .args + .is_present("show-noisyalerts") + { + ids += &String::from_utf8(fs::read("config/noisy-rules.txt").unwrap()).unwrap(); + } + + let mut fill_ids = fillter::RuleFill { + no_use_rule: HashMap::new(), + }; + + for v in ids.split_whitespace().next() { + fill_ids.no_use_rule.insert(v.to_string(), true); + } let rule_files = detection::Detection::parse_rule_files( level, configs::CONFIG.read().unwrap().args.value_of("rules"), + fill_ids, ); let mut detection = detection::Detection::new(rule_files); for evtx_file in evtx_files { diff --git a/src/yaml.rs b/src/yaml.rs index b0ef5065..e67ac40b 100644 --- a/src/yaml.rs +++ b/src/yaml.rs @@ -3,6 +3,7 @@ extern crate yaml_rust; use crate::detections::configs; use crate::detections::print::AlertMessage; +use crate::fillter::RuleFill; use std::collections::HashMap; use std::ffi::OsStr; use std::fs; @@ -42,13 +43,18 @@ impl ParseYaml { Ok(file_content) } - pub fn read_dir>(&mut self, path: P, level: &str) -> io::Result { + pub fn read_dir>( + &mut self, + path: P, + level: &str, + fill_ids: RuleFill, + ) -> io::Result { let mut entries = fs::read_dir(path)?; let yaml_docs = entries.try_fold(vec![], |mut ret, entry| { let entry = entry?; // フォルダは再帰的に呼び出す。 if entry.file_type()?.is_dir() { - self.read_dir(entry.path(), level)?; + self.read_dir(entry.path(), level, fill_ids.clone())?; return io::Result::Ok(ret); } // ファイル以外は無視 @@ -115,6 +121,7 @@ impl ParseYaml { .unwrap_or(&0) + 1, ); + if configs::CONFIG.read().unwrap().args.is_present("verbose") { println!("Loaded yml file path: {}", filepath); } @@ -130,6 +137,18 @@ impl ParseYaml { return Option::None; } + //除外されたルールは無視する + match fill_ids + .no_use_rule + .get(&yaml_doc["id"].as_str().unwrap().to_string()) + { + Some(_) => (), + None => { + self.ignorerule_count += 1; + return Option::None; + } + } + return Option::Some((filepath, yaml_doc)); }) .collect(); @@ -148,7 +167,14 @@ mod tests { #[test] fn test_read_dir_yaml() { let mut yaml = yaml::ParseYaml::new(); - &yaml.read_dir("test_files/rules/yaml/".to_string(), &"".to_owned()); + let mut fill_ids = fillter::RuleFill { + no_use_rule: HashMap::new(), + }; + &yaml.read_dir( + "test_files/rules/yaml/".to_string(), + &"".to_owned(), + fill_ids, + ); assert_ne!(yaml.files.len(), 0); } @@ -183,7 +209,10 @@ mod tests { fn test_default_level_read_yaml() { let mut yaml = yaml::ParseYaml::new(); let path = Path::new("test_files/rules/level_yaml"); - yaml.read_dir(path.to_path_buf(), &"").unwrap(); + let mut fill_ids = RuleFill { + no_use_rule: HashMap::new(), + }; + yaml.read_dir(path.to_path_buf(), &"", fill_ids).unwrap(); assert_eq!(yaml.files.len(), 4); } @@ -191,36 +220,54 @@ mod tests { fn test_info_level_read_yaml() { let mut yaml = yaml::ParseYaml::new(); let path = Path::new("test_files/rules/level_yaml"); - yaml.read_dir(path.to_path_buf(), &"INFO").unwrap(); + let mut fill_ids = RuleFill { + no_use_rule: HashMap::new(), + }; + yaml.read_dir(path.to_path_buf(), &"INFO", fill_ids) + .unwrap(); assert_eq!(yaml.files.len(), 5); } #[test] fn test_low_level_read_yaml() { let mut yaml = yaml::ParseYaml::new(); let path = Path::new("test_files/rules/level_yaml"); - - yaml.read_dir(path.to_path_buf(), &"LOW").unwrap(); + let mut fill_ids = RuleFill { + no_use_rule: HashMap::new(), + }; + yaml.read_dir(path.to_path_buf(), &"LOW", fill_ids).unwrap(); assert_eq!(yaml.files.len(), 4); } #[test] fn test_medium_level_read_yaml() { let mut yaml = yaml::ParseYaml::new(); let path = Path::new("test_files/rules/level_yaml"); - yaml.read_dir(path.to_path_buf(), &"MEDIUM").unwrap(); + let mut fill_ids = RuleFill { + no_use_rule: HashMap::new(), + }; + yaml.read_dir(path.to_path_buf(), &"MEDIUM", fill_ids) + .unwrap(); assert_eq!(yaml.files.len(), 3); } #[test] fn test_high_level_read_yaml() { let mut yaml = yaml::ParseYaml::new(); let path = Path::new("test_files/rules/level_yaml"); - yaml.read_dir(path.to_path_buf(), &"HIGH").unwrap(); + let mut fill_ids = RuleFill { + no_use_rule: HashMap::new(), + }; + yaml.read_dir(path.to_path_buf(), &"HIGH", fill_ids) + .unwrap(); assert_eq!(yaml.files.len(), 2); } #[test] fn test_critical_level_read_yaml() { let mut yaml = yaml::ParseYaml::new(); let path = Path::new("test_files/rules/level_yaml"); - yaml.read_dir(path.to_path_buf(), &"CRITICAL").unwrap(); + let mut fill_ids = RuleFill { + no_use_rule: HashMap::new(), + }; + yaml.read_dir(path.to_path_buf(), &"CRITICAL", fill_ids) + .unwrap(); assert_eq!(yaml.files.len(), 1); } }