Skip to content

Commit

Permalink
Merge pull request #1234 from Yamato-Security/1226-add-mitre-tactics-…
Browse files Browse the repository at this point in the history
…info-to-html-report

add mitre tactics info to html report
  • Loading branch information
hitenkoku authored Dec 20, 2023
2 parents d907edf + 6eb9d70 commit 3d71dae
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 66 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG-Japanese.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# 変更点

## 2.12.0 [xxxx/xx/xx]
## 2.12.0 [2023/12/24] "SECCON Christmas Release"

**改善:**

- JSON出力において、MitreTactics、MitreTags, OtherTagsの出力を要素ごとに文字列で出力させるように修正した。 (#1230) (@hitenkoku)
- `csv-timeline` or `json-timeline` コマンドが利用されたときにissueやpull-requestの連絡先についてのメッセージを追加した。 (#1236) (@hitenkoku)
- 検知した端末に対してMITRE ATT&CKの戦術をHTMLレポートに出力できるようにした。この機能を利用するためには利用したプロファイルに`%MitreTactics%`が存在する必要がある。 (#1226) (@hitenkoku)
- `csv-timeline`または`json-timeline`コマンドが利用されたときにissueやpull-requestの連絡先についてのメッセージを追加した。 (#1236) (@hitenkoku)

**バグ修正:**

Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# Changes

## 2.12.0 [xxxx/xx/xx]
## 2.12.0 [2023/12/24] "SECCON Christmas Release"

**Enhancements:**

- `%MitreTactics%`, `%MitreTags%`, `%OtherTags%` fields are now outputted as an array of strings in JSON output. (#1230) (@hitenkoku)
- Added a summary of MITRE ATT&CK tactics that were detected for each computer in the HTML report. In order to use this feature, you need to use a profile that includes the `%MitreTactics%` field. (#1226) (@hitenkoku)
- Output messages about reporting issues and false positives when using `csv-timeline` or `json-timeline` commands. (#1236) (@hitenkoku)

**Bug Fixes:**
Expand Down
31 changes: 31 additions & 0 deletions config/html_report/hayabusa_report.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 15 additions & 15 deletions config/mitre_tactics.txt
Original file line number Diff line number Diff line change
@@ -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
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
33 changes: 32 additions & 1 deletion src/afterfact.rs
Original file line number Diff line number Diff line change
@@ -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,
};
Expand Down Expand Up @@ -855,6 +857,7 @@ fn emit_csv<W: std::io::Write>(
}
}
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(())
Expand Down Expand Up @@ -1855,6 +1858,34 @@ fn extract_author_name(yaml_path: &str) -> Nested<String> {
Nested::new()
}

///MITRE ATTCKのTacticsの属性を持つルールに検知したコンピュータ名をhtml出力するための文字列をhtml_output_stockに追加する関数
fn _output_html_computer_by_mitre_attck(html_output_stock: &mut Nested<String>) {
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.<br>Make sure you run Hayabusa with a profile that includes %MitreTactics% in order to get this info.<br>");
}
for (idx, sorted_output_map) in COMPUTER_MITRE_ATTCK_MAP
.iter()
.sorted_by(|a, b| {
Ord::cmp(
&format!("{}-{}", &b.value()[b.value().len() - 1], b.key()),
&format!("{}-{}", &a.value()[a.value().len() - 1], 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("<br>")
));
}
}

#[cfg(test)]
mod tests {
use crate::afterfact::Colors;
Expand Down
96 changes: 54 additions & 42 deletions src/detections/detection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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(_) => {
Expand All @@ -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(
Expand Down Expand Up @@ -373,14 +374,39 @@ 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()
.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();
}
}
}
// profile_converter.insert(key.as_str(), MitreTactics(output_tactics_str.into()));
}
MitreTags(_) => {
let techniques = tag_info
Expand Down Expand Up @@ -929,33 +955,19 @@ impl Detection {

/// rule内のtagsの内容を配列として返却する関数
fn get_tag_info(rule: &RuleNode) -> Nested<String> {
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部分の検知出力文の文字列を返す関数
Expand Down
7 changes: 2 additions & 5 deletions src/detections/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ lazy_static! {
.unwrap(),
true
);
pub static ref COMPUTER_MITRE_ATTCK_MAP : DashMap<CompactString, Vec<CompactString>> = DashMap::new();
pub static ref LEVEL_ABBR_MAP:HashMap<&'static str, &'static str> = HashMap::from_iter(vec![
("critical", "crit"),
("high", "high"),
Expand Down Expand Up @@ -87,18 +88,14 @@ 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 {
line[0].trim().to_string()
};
ret.insert(
CompactString::from(key),
CompactString::from(line[1].trim()),
CompactString::from(line[1..].iter().map(|x| x.trim()).join(",")),
);
});
ret
Expand Down

0 comments on commit 3d71dae

Please sign in to comment.