From 6cfe49e968c4760c61546e2a5f1ea01050ca99b0 Mon Sep 17 00:00:00 2001 From: DastInDark <2350416+hitenkoku@users.noreply.github.com> Date: Tue, 19 Dec 2023 03:40:33 +0900 Subject: [PATCH 1/9] feat: added mitre tactics info to html report #1226 --- config/mitre_tactics.txt | 30 ++++++------ src/afterfact.rs | 33 ++++++++++++- src/detections/detection.rs | 97 +++++++++++++++++++++---------------- src/detections/message.rs | 7 +-- 4 files changed, 104 insertions(+), 63 deletions(-) diff --git a/config/mitre_tactics.txt b/config/mitre_tactics.txt index 01e797005..553e71a4e 100644 --- a/config/mitre_tactics.txt +++ b/config/mitre_tactics.txt @@ -1,15 +1,15 @@ -tag_full_str,tag_output_str -attack.reconnaissance,Recon -attack.resource_development,ResDev -attack.initial_access,InitAccess -attack.execution,Exec -attack.persistence,Persis -attack.privilege_escalation,PrivEsc -attack.defense_evasion,Evas -attack.credential_access,CredAccess -attack.discovery,Disc -attack.lateral_movement,LatMov -attack.collection,Collect -attack.command_and_control,C2 -attack.exfiltration,Exfil -attack.impact,Impact \ No newline at end of file +tag_full_str,tag_output_str,html_tag_output_str +attack.reconnaissance,Recon,01. Reconnaissance +attack.resource_development,ResDev,02. Resource Development +attack.initial_access,InitAccess,03. Initial Access +attack.execution,Exec,04. Execution +attack.persistence,Persis,05. Persistence +attack.privilege_escalation,PrivEsc,06. Privilege Escalation +attack.defense_evasion,Evas,07. Defense Evasion +attack.credential_access,CredAccess,08. Credential Access +attack.discovery,Disc,09. Discovery +attack.lateral_movement,LatMov,10. Lateral Movement +attack.collection,Collect,11. Collection +attack.command_and_control,C2,12. C2 +attack.exfiltration,Exfil,13. Exfiltration +attack.impact,Impact,14. Impact \ No newline at end of file diff --git a/src/afterfact.rs b/src/afterfact.rs index 554c9eb05..6c6255010 100644 --- a/src/afterfact.rs +++ b/src/afterfact.rs @@ -1,7 +1,9 @@ use crate::detections::configs::{ Action, OutputOption, StoredStatic, CONTROL_CHAT_REPLACE_MAP, CURRENT_EXE_PATH, GEOIP_DB_PARSER, }; -use crate::detections::message::{self, AlertMessage, LEVEL_FULL, MESSAGEKEYS}; +use crate::detections::message::{ + self, AlertMessage, COMPUTER_MITRE_ATTCK_MAP, LEVEL_FULL, MESSAGEKEYS, +}; use crate::detections::utils::{ self, format_time, get_writable_color, output_and_data_stack_for_html, write_color_buffer, }; @@ -855,6 +857,7 @@ fn emit_csv( } } if html_output_flag { + _output_html_computer_by_mitre_attck(&mut html_output_stock); htmlreport::add_md_data("Results Summary {#results_summary}", html_output_stock); } Ok(()) @@ -1855,6 +1858,34 @@ fn extract_author_name(yaml_path: &str) -> Nested { Nested::new() } +///MITRE ATTCKのTacticsの属性を持つルールに検知したコンピュータ名をhtml出力するための文字列をhtml_output_stockに追加する関数 +fn _output_html_computer_by_mitre_attck(html_output_stock: &mut Nested) { + html_output_stock.push("### MITRE ATT&CK Tactics:{#computers_with_mitre_attck_detections}"); + if COMPUTER_MITRE_ATTCK_MAP.is_empty() { + html_output_stock.push("#### No computer detected by MITRE ATT&CK Tactics:"); + } + for (idx, sorted_output_map) in COMPUTER_MITRE_ATTCK_MAP + .iter() + .sorted_by(|a, b| { + Ord::cmp( + &format!("{}-{}", &b.value()[0], b.key()), + &format!("{}-{}", &a.value()[0], a.key()), + ) + }) + .enumerate() + { + if idx == 0 { + html_output_stock.push("|Computer| MITRE ATT&CK Tactics|"); + html_output_stock.push("|---|---|"); + } + html_output_stock.push(format!( + "|{}|{}|", + sorted_output_map.key(), + sorted_output_map.value().join("
") + )); + } +} + #[cfg(test)] mod tests { use crate::afterfact::Colors; diff --git a/src/detections/detection.rs b/src/detections/detection.rs index e6c82d8c2..cdcfa2c1f 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -38,7 +38,7 @@ use tokio::{runtime::Runtime, spawn, task::JoinHandle}; use super::configs::{ EventKeyAliasConfig, StoredStatic, GEOIP_DB_PARSER, GEOIP_DB_YAML, GEOIP_FILTER, STORED_STATIC, }; -use super::message::{self, LEVEL_ABBR_MAP}; +use super::message::{self, COMPUTER_MITRE_ATTCK_MAP, LEVEL_ABBR_MAP}; // イベントファイルの1レコード分の情報を保持する構造体 #[derive(Clone, Debug)] @@ -278,6 +278,7 @@ impl Detection { let eventkey_alias = binding.as_ref().unwrap(); let is_json_timeline = matches!(stored_static.config.action, Some(Action::JsonTimeline(_))); + let mut computer_name_to_mitre_tactics = CompactString::default(); for (key, profile) in stored_static.profiles.as_ref().unwrap().iter() { match profile { Timestamp(_) => { @@ -294,16 +295,16 @@ impl Detection { ); } Computer(_) => { - profile_converter.insert( - key.as_str(), - Computer( - record_info.record["Event"]["System"]["Computer"] - .as_str() - .unwrap_or_default() - .replace('\"', "") - .into(), - ), + let computer_name = CompactString::from( + record_info.record["Event"]["System"]["Computer"] + .as_str() + .unwrap_or_default() + .replace('\"', ""), ); + if stored_static.html_report_flag { + computer_name_to_mitre_tactics = computer_name.clone(); + } + profile_converter.insert(key.as_str(), Computer(computer_name.into())); } Channel(_) => { profile_converter.insert( @@ -373,14 +374,40 @@ impl Detection { ); } MitreTactics(_) => { - let tactics = CompactString::from( - tag_info - .iter() - .filter(|x| tags_config_values.contains(&&CompactString::from(*x))) + let tactics = tag_info + .iter() + .filter(|x| tags_config_values.contains(&&CompactString::from(*x))); + // .map(|x| TAGS_CONFIG.get(x.into()).unwrap()); + let output_tactics_str = CompactString::from( + tactics + .clone() + .into_iter() + .filter_map(|x| x.split(',').next()) .join(" ¦ "), ); - profile_converter.insert(key.as_str(), MitreTactics(tactics.into())); + profile_converter.insert( + key.as_str(), + MitreTactics(output_tactics_str.clone().into()), + ); + + let html_output_tactics_str = tactics + .into_iter() + .map(|x| x.split(',').nth(1).unwrap_or_default()) + .collect_vec(); + if stored_static.html_report_flag && !html_output_tactics_str.is_empty() { + let mut v = COMPUTER_MITRE_ATTCK_MAP + .entry(computer_name_to_mitre_tactics.clone()) + .or_default(); + let (_, attck_tac) = v.pair_mut(); + for html_attck_tac in html_output_tactics_str { + if !attck_tac.contains(&html_attck_tac.into()) { + attck_tac.push(html_attck_tac.into()); + attck_tac.sort_unstable_by(|a, b| Ord::cmp(b, a)); + } + } + } + // profile_converter.insert(key.as_str(), MitreTactics(output_tactics_str.into())); } MitreTags(_) => { let techniques = tag_info @@ -929,33 +956,19 @@ impl Detection { /// rule内のtagsの内容を配列として返却する関数 fn get_tag_info(rule: &RuleNode) -> Nested { - match TAGS_CONFIG.is_empty() { - false => Nested::from_iter( - rule.yaml["tags"] - .as_vec() - .unwrap_or(&Vec::default()) - .iter() - .map(|info| { - if let Some(tag) = TAGS_CONFIG.get(info.as_str().unwrap_or_default()) { - tag.to_owned() - } else { - CompactString::from(info.as_str().unwrap_or_default()) - } - }), - ), - true => Nested::from_iter( - rule.yaml["tags"] - .as_vec() - .unwrap_or(&Vec::default()) - .iter() - .map(|info| { - match TAGS_CONFIG.get(info.as_str().unwrap_or(&String::default())) { - Some(s) => s.to_owned(), - _ => CompactString::from(info.as_str().unwrap_or("")), - } - }), - ), - } + Nested::from_iter( + rule.yaml["tags"] + .as_vec() + .unwrap_or(&Vec::default()) + .iter() + .map(|info| { + if let Some(tag) = TAGS_CONFIG.get(info.as_str().unwrap_or_default()) { + tag.to_owned() + } else { + CompactString::from(info.as_str().unwrap_or_default()) + } + }), + ) } ///aggregation conditionのcount部分の検知出力文の文字列を返す関数 diff --git a/src/detections/message.rs b/src/detections/message.rs index 3dadb7a0c..65bb7f6b2 100644 --- a/src/detections/message.rs +++ b/src/detections/message.rs @@ -55,6 +55,7 @@ lazy_static! { .unwrap(), true ); + pub static ref COMPUTER_MITRE_ATTCK_MAP : DashMap> = DashMap::new(); pub static ref LEVEL_ABBR_MAP:HashMap<&'static str, &'static str> = HashMap::from_iter(vec![ ("critical", "crit"), ("high", "high"), @@ -87,10 +88,6 @@ pub fn create_output_filter_config( } }; read_result.iter().for_each(|line| { - if line.len() != 2 { - return; - } - let key = if is_lower_case { line[0].trim().to_ascii_lowercase() } else { @@ -98,7 +95,7 @@ pub fn create_output_filter_config( }; ret.insert( CompactString::from(key), - CompactString::from(line[1].trim()), + CompactString::from(line[1..].iter().map(|x| x.trim()).join(",")), ); }); ret From 0c147b6fc1bc8b4362ef1b15187d71d1437ed53e Mon Sep 17 00:00:00 2001 From: DastInDark <2350416+hitenkoku@users.noreply.github.com> Date: Tue, 19 Dec 2023 03:41:12 +0900 Subject: [PATCH 2/9] UI(hayabusa_report.css): added style to mitre tactics table #1226 --- config/html_report/hayabusa_report.css | 29 ++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/config/html_report/hayabusa_report.css b/config/html_report/hayabusa_report.css index e90409fa4..4829eb076 100644 --- a/config/html_report/hayabusa_report.css +++ b/config/html_report/hayabusa_report.css @@ -77,6 +77,35 @@ h3 { border-bottom: solid 3px #ffffff; } +#computers_with_mitre_attck_detections { + color: #ffffff; + border-bottom: solid 3px #ffffff; +} + +table { + width: 100%; + margin: 16px; +} + +tbody tr td { + text-align: left; +} + +tr:nth-child(odd) { + background-color: #f4faff +} + +tr:nth-child(even) { + background-color: #ffffff +} + +th { + background-color: #ff0000; + color: #ffffff; + text-align: center; + padding: 8px; +} + li { padding: 5px; From b7c372399254e711c1294b65272229ec346c18df Mon Sep 17 00:00:00 2001 From: DastInDark <2350416+hitenkoku@users.noreply.github.com> Date: Tue, 19 Dec 2023 03:55:33 +0900 Subject: [PATCH 3/9] style(detection): fixed clippy error --- src/detections/detection.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/detections/detection.rs b/src/detections/detection.rs index cdcfa2c1f..05f443204 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -381,7 +381,6 @@ impl Detection { let output_tactics_str = CompactString::from( tactics .clone() - .into_iter() .filter_map(|x| x.split(',').next()) .join(" ¦ "), ); From e45228cef431eb466b88cbd5105d608c96f2fa66 Mon Sep 17 00:00:00 2001 From: DastInDark <2350416+hitenkoku@users.noreply.github.com> Date: Tue, 19 Dec 2023 03:59:41 +0900 Subject: [PATCH 4/9] UI(hayabusa_report): fixed mitre tactics table padding #1226 --- config/html_report/hayabusa_report.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/config/html_report/hayabusa_report.css b/config/html_report/hayabusa_report.css index 4829eb076..b0d6745c7 100644 --- a/config/html_report/hayabusa_report.css +++ b/config/html_report/hayabusa_report.css @@ -83,8 +83,10 @@ h3 { } table { + max-width: 860px; width: 100%; - margin: 16px; + box-sizing: border-box; + padding: 8px; } tbody tr td { From 306aa20470a5d643c8e88f6fdb097778d8386ad3 Mon Sep 17 00:00:00 2001 From: DastInDark <2350416+hitenkoku@users.noreply.github.com> Date: Tue, 19 Dec 2023 04:08:12 +0900 Subject: [PATCH 5/9] docs(CHANGELOG): added #1226 --- CHANGELOG-Japanese.md | 1 + CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG-Japanese.md b/CHANGELOG-Japanese.md index f1b296f58..64003184c 100644 --- a/CHANGELOG-Japanese.md +++ b/CHANGELOG-Japanese.md @@ -5,6 +5,7 @@ **改善:** - JSON出力において、MitreTactics、MitreTags, OtherTagsの出力を要素ごとに文字列で出力させるように修正した。 (#1230) (@hitenkoku) +- 検知した端末に対してMITRE ATT&CKの戦術をHTMLレポートに出力できるようにした。この機能を利用するためには利用したプロファイルに`%MitreTactics%`が存在する必要がある。 (#1226) (@hitenkoku) **バグ修正:** diff --git a/CHANGELOG.md b/CHANGELOG.md index ec5f51f3f..17b9ebfe0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ **Enhancements:** - `%MitreTactics%`, `%MitreTags%`, `%OtherTags%` fields are now outputted as an array of strings in JSON output. (#1230) (@hitenkoku) +- Added MITRE ATT&CK Tactics list up where detected for each computer to HTML report. This feature needs to exist `%MitreTactics%` in used profile. (#1230) **Bug Fixes:** From 8f18f7ede6d8d26f9819ea1d3419ec1b2d9884bf Mon Sep 17 00:00:00 2001 From: DastInDark <2350416+hitenkoku@users.noreply.github.com> Date: Tue, 19 Dec 2023 10:33:07 +0900 Subject: [PATCH 6/9] feat(afterfact, detection): modified the tactics order was sorted from top to bottom #1226 --- CHANGELOG.md | 2 +- src/afterfact.rs | 4 ++-- src/detections/detection.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17b9ebfe0..9c775cac1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Enhancements:** - `%MitreTactics%`, `%MitreTags%`, `%OtherTags%` fields are now outputted as an array of strings in JSON output. (#1230) (@hitenkoku) -- Added MITRE ATT&CK Tactics list up where detected for each computer to HTML report. This feature needs to exist `%MitreTactics%` in used profile. (#1230) +- Added MITRE ATT&CK Tactics list up where detected for each computer to HTML report. This feature needs to exist `%MitreTactics%` in used profile. (#1226) (@hitenkoku) **Bug Fixes:** diff --git a/src/afterfact.rs b/src/afterfact.rs index 6c6255010..5878a0380 100644 --- a/src/afterfact.rs +++ b/src/afterfact.rs @@ -1868,8 +1868,8 @@ fn _output_html_computer_by_mitre_attck(html_output_stock: &mut Nested) .iter() .sorted_by(|a, b| { Ord::cmp( - &format!("{}-{}", &b.value()[0], b.key()), - &format!("{}-{}", &a.value()[0], a.key()), + &format!("{}-{}", &b.value()[b.value().len() - 1], b.key()), + &format!("{}-{}", &a.value()[a.value().len() - 1], a.key()), ) }) .enumerate() diff --git a/src/detections/detection.rs b/src/detections/detection.rs index 05f443204..e6b48c19d 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -402,7 +402,7 @@ impl Detection { for html_attck_tac in html_output_tactics_str { if !attck_tac.contains(&html_attck_tac.into()) { attck_tac.push(html_attck_tac.into()); - attck_tac.sort_unstable_by(|a, b| Ord::cmp(b, a)); + attck_tac.sort_unstable(); } } } From 84851f8de5d6babc39f3ef7fa67f94af331b22f8 Mon Sep 17 00:00:00 2001 From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> Date: Tue, 19 Dec 2023 12:15:21 +0900 Subject: [PATCH 7/9] update message --- src/afterfact.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/afterfact.rs b/src/afterfact.rs index 5878a0380..54dda9fbc 100644 --- a/src/afterfact.rs +++ b/src/afterfact.rs @@ -1862,7 +1862,7 @@ fn extract_author_name(yaml_path: &str) -> Nested { fn _output_html_computer_by_mitre_attck(html_output_stock: &mut Nested) { html_output_stock.push("### MITRE ATT&CK Tactics:{#computers_with_mitre_attck_detections}"); if COMPUTER_MITRE_ATTCK_MAP.is_empty() { - html_output_stock.push("#### No computer detected by MITRE ATT&CK Tactics:"); + html_output_stock.push("#### No computers were detected with MITRE ATT&CK Tactics. Make sure you run Hayabusa with a profile that includes %MitreTactics% in order to get this info."); } for (idx, sorted_output_map) in COMPUTER_MITRE_ATTCK_MAP .iter() From 09c4848e340381ed85fa3dcf3a4ac52e793394a7 Mon Sep 17 00:00:00 2001 From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> Date: Tue, 19 Dec 2023 18:48:48 +0900 Subject: [PATCH 8/9] change red title to gray --- config/html_report/hayabusa_report.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/html_report/hayabusa_report.css b/config/html_report/hayabusa_report.css index b0d6745c7..ab163adb4 100644 --- a/config/html_report/hayabusa_report.css +++ b/config/html_report/hayabusa_report.css @@ -102,7 +102,7 @@ tr:nth-child(even) { } th { - background-color: #ff0000; + background-color: #999999; color: #ffffff; text-align: center; padding: 8px; From c1db3452522d18dc24e79be9802fc78df12c7d78 Mon Sep 17 00:00:00 2001 From: DastInDark <2350416+hitenkoku@users.noreply.github.com> Date: Wed, 20 Dec 2023 14:14:01 +0900 Subject: [PATCH 9/9] UI(afterfact): fixed message which is no detected mitre tactics #1226 --- src/afterfact.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/afterfact.rs b/src/afterfact.rs index 54dda9fbc..12adc6c4a 100644 --- a/src/afterfact.rs +++ b/src/afterfact.rs @@ -1862,7 +1862,7 @@ fn extract_author_name(yaml_path: &str) -> Nested { fn _output_html_computer_by_mitre_attck(html_output_stock: &mut Nested) { html_output_stock.push("### MITRE ATT&CK Tactics:{#computers_with_mitre_attck_detections}"); if COMPUTER_MITRE_ATTCK_MAP.is_empty() { - html_output_stock.push("#### No computers were detected with MITRE ATT&CK Tactics. Make sure you run Hayabusa with a profile that includes %MitreTactics% in order to get this info."); + html_output_stock.push("- No computers were detected with MITRE ATT&CK Tactics.
Make sure you run Hayabusa with a profile that includes %MitreTactics% in order to get this info.
"); } for (idx, sorted_output_map) in COMPUTER_MITRE_ATTCK_MAP .iter()