diff --git a/src/detections/detection.rs b/src/detections/detection.rs index b8a76d41..2adf50d7 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -6,6 +6,7 @@ use crate::options::profile::{ LOAEDED_PROFILE_ALIAS, PRELOAD_PROFILE, PRELOAD_PROFILE_REGEX, PROFILES, }; use chrono::{TimeZone, Utc}; +use itertools::Itertools; use termcolor::{BufferWriter, Color, ColorChoice}; use crate::detections::message::AlertMessage; @@ -208,7 +209,7 @@ impl Detection { /// 条件に合致したレコードを格納するための関数 fn insert_message(rule: &RuleNode, record_info: &EvtxRecordInfo) { - let tag_info: Vec = Detection::get_tag_info(rule); + let tag_info: &Vec = &Detection::get_tag_info(rule); let recinfo = record_info .record_information .as_ref() @@ -316,6 +317,48 @@ impl Detection { .to_string(), ); } + "%MitreTactics%" => { + let tactics: &Vec = &tag_info.iter().filter(|x| TAGS_CONFIG.values().contains(x)).map(|y| { + y.to_owned() + }) + .collect(); + profile_converter.insert("%MitreTactics%".to_string(), tactics.join(" : ")); + }, + "%MitreTechniques%" => { + let techniques: &Vec = &tag_info. + iter() + .filter(|x| { + !TAGS_CONFIG.values().contains(x) + && (x.starts_with("attack.t") + || x.starts_with("attack.g") + || x.starts_with("attack.s")) + }) + .map(|y| { + let mut replaced_tag = y.replace("attack.", ""); + let (head, _) = replaced_tag.split_at_mut(1); + head.make_ascii_uppercase(); + replaced_tag.to_owned() + }) + .collect(); + profile_converter + .insert("%MitreTechniques%".to_string(), techniques.join(" : ")); + }, + "%OtherTags%" => { + let tags: &Vec = &tag_info.iter() + .filter(|x| { + !(TAGS_CONFIG.values().contains(x) + || x.starts_with("attack.t") + || x.starts_with("attack.g") + || x.starts_with("attack.s")) + }) + .map(|y| { + y.to_owned() + }) + .collect(); + profile_converter + .insert("%OtherTags%".to_string(), tags.join(" : ")); + }, + _ => {} } } @@ -347,7 +390,7 @@ impl Detection { /// insert aggregation condition detection message to output stack fn insert_agg_message(rule: &RuleNode, agg_result: AggResult) { - let tag_info: Vec = Detection::get_tag_info(rule); + let tag_info: &Vec = &Detection::get_tag_info(rule); let output = Detection::create_count_output(rule, &agg_result); let rec_info = if LOAEDED_PROFILE_ALIAS.contains("%RecordInformation%") { Option::Some(String::default()) @@ -408,7 +451,48 @@ impl Detection { } "%EvtxFile%" => { profile_converter.insert("%EvtxFile%".to_string(), "-".to_owned()); - } + }, + "%MitreTactics%" => { + let tactics: &Vec = &tag_info.iter().filter(|x| TAGS_CONFIG.values().contains(x)).map(|y| { + y.to_owned() + }) + .collect(); + profile_converter.insert("%MitreTactics%".to_string(), tactics.join(" : ")); + }, + "%MitreTechniques%" => { + let techniques: &Vec = &tag_info. + iter() + .filter(|x| { + !TAGS_CONFIG.values().contains(x) + && (x.starts_with("attack.t") + || x.starts_with("attack.g") + || x.starts_with("attack.s")) + }) + .map(|y| { + let mut replaced_tag = y.replace("attack.", ""); + let (head, _) = replaced_tag.split_at_mut(1); + head.make_ascii_uppercase(); + replaced_tag.to_owned() + }) + .collect(); + profile_converter + .insert("%MitreTechniques%".to_string(), techniques.join(" : ")); + }, + "%OtherTags%" => { + let tags: &Vec = &tag_info.iter() + .filter(|x| { + !(TAGS_CONFIG.values().contains(x) + || x.starts_with("attack.t") + || x.starts_with("attack.g") + || x.starts_with("attack.s")) + }) + .map(|y| { + y.to_owned() + }) + .collect(); + profile_converter + .insert("%OtherTags%".to_string(), tags.join(" : ")); + }, _ => {} } } @@ -441,8 +525,14 @@ impl Detection { .as_vec() .unwrap_or(&Vec::default()) .iter() - .filter_map(|info| TAGS_CONFIG.get(info.as_str().unwrap_or(&String::default()))) - .map(|str| str.to_owned()) + .map(|info| { + if let Some(tag) = TAGS_CONFIG.get(info.as_str().unwrap_or(&String::default())) + { + tag.to_owned() + } else { + info.as_str().unwrap_or(&String::default()).to_owned() + } + }) .collect(), true => rule.yaml["tags"] .as_vec() @@ -451,7 +541,7 @@ impl Detection { .map( |info| match TAGS_CONFIG.get(info.as_str().unwrap_or(&String::default())) { Some(s) => s.to_owned(), - _ => info.as_str().unwrap_or("").replace("attack.", ""), + _ => info.as_str().unwrap_or("").to_string() }, ) .collect(), diff --git a/src/options/profile.rs b/src/options/profile.rs index eddf5109..89bfa56c 100644 --- a/src/options/profile.rs +++ b/src/options/profile.rs @@ -36,12 +36,14 @@ lazy_static! { "%Channel%", "%Level%", "%EventID%", - "%MitreAttack%", "%RecordID%", "%RuleTitle%", "%RecordInformation%", "%RuleFile%", - "%EvtxFile%" + "%EvtxFile%", + "%MitreTactics%", + "%MitreTechniques%", + "%OtherTags%" ]; pub static ref PRELOAD_PROFILE_REGEX: RegexSet = RegexSet::new(&*PRELOAD_PROFILE).unwrap(); }