Feature/output unique detection#209 (#225)

* checked contributors #141

- because RustyBlue code contributor(not hayabusa contributor) was mixed in hayabusa contributor

* changed yaml count name

* changed ruletype string #157

* fixed output of parse error #157

* fixed output

* added level unique detection output #209
This commit is contained in:
DustInDark
2021-11-24 21:15:43 +09:00
committed by GitHub
parent 015899bc51
commit b48f774b93
8 changed files with 57 additions and 36 deletions

View File

@@ -1,16 +1,12 @@
Hayabusa was possible thanks to the following people (in alphabetical order): Hayabusa was possible thanks to the following people (in alphabetical order):
Akira Nishikawa (@nishikawaakira): Previous lead developer, core hayabusa rule support, etc... Akira Nishikawa (@nishikawaakira): Previous lead developer, core hayabusa rule support, etc...
Dai (@__da13__): Developer
DustInDark(@hitenkoku): Core developer, project management, sigma count implementation, rule creation, countless feature additions and fixes, etc… DustInDark(@hitenkoku): Core developer, project management, sigma count implementation, rule creation, countless feature additions and fixes, etc…
Garigariganzy (@garigariganzy31): Developer, event ID statistics implementation, etc... Garigariganzy (@garigariganzy31): Developer, event ID statistics implementation, etc...
ItiB (@itiB_S144) : Core developer, sigmac hayabusa backend, rule creation, etc... ItiB (@itiB_S144) : Core developer, sigmac hayabusa backend, rule creation, etc...
James Takai / hachiyone(@hach1yon): Current lead developer, tokio multi-threading, sigma aggregation logic, sigmac backend, rule creation, etc… James Takai / hachiyone(@hach1yon): Current lead developer, tokio multi-threading, sigma aggregation logic, sigmac backend, rule creation, etc…
Kazuminn (@k2warugaki): Developer Kazuminn (@k2warugaki): Developer
Mimura (@mimura1133): Developer
Yusuke Matsui (@apt773): AD hacking working group leader, rule testing, documentation, research, support, etc... Yusuke Matsui (@apt773): AD hacking working group leader, rule testing, documentation, research, support, etc...
Siam (@siamease): Developer
Tsubokku (@ytsuboi0322): Japanese translations
Zach Mathis (@yamatosecurity, Yamato Security Founder): Project leader, tool and concept design, rule creation and tuning, etc… Zach Mathis (@yamatosecurity, Yamato Security Founder): Project leader, tool and concept design, rule creation and tuning, etc…

View File

@@ -82,7 +82,7 @@ fn emit_csv<W: std::io::Write>(writer: &mut W) -> Result<(), Box<dyn Error>> {
wtr.flush()?; wtr.flush()?;
println!(""); println!("");
println!("Events Detected:{:?}", detect_count); println!("Total Events Detected:{:?}", detect_count);
Ok(()) Ok(())
} }

View File

@@ -5,7 +5,7 @@ use std::collections::HashMap;
use std::sync::RwLock; use std::sync::RwLock;
lazy_static! { lazy_static! {
pub static ref CONFIG: RwLock<ConfigReader> = RwLock::new(ConfigReader::new()); pub static ref CONFIG: RwLock<ConfigReader> = RwLock::new(ConfigReader::new());
pub static ref LEVELMAP: HashMap<String, u8> = { pub static ref LEVELMAP: HashMap<String, u128> = {
let mut levelmap = HashMap::new(); let mut levelmap = HashMap::new();
levelmap.insert("INFO".to_owned(), 1); levelmap.insert("INFO".to_owned(), 1);
levelmap.insert("LOW".to_owned(), 2); levelmap.insert("LOW".to_owned(), 2);

View File

@@ -5,6 +5,7 @@ use serde_json::Value;
use std::collections::HashMap; use std::collections::HashMap;
use tokio::{runtime::Runtime, spawn, task::JoinHandle}; use tokio::{runtime::Runtime, spawn, task::JoinHandle};
use crate::detections::configs;
use crate::detections::print::AlertMessage; use crate::detections::print::AlertMessage;
use crate::detections::print::MESSAGES; use crate::detections::print::MESSAGES;
use crate::detections::rule; use crate::detections::rule;
@@ -54,11 +55,6 @@ impl Detection {
// ルールファイルのパースを実行 // ルールファイルのパースを実行
let mut rulefile_loader = ParseYaml::new(); 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);
Detection::print_rule_load_info(
rulefile_loader.rulecounter,
rulefile_loader.parseerror_count,
rulefile_loader.ignore_count,
);
if result_readdir.is_err() { if result_readdir.is_err() {
AlertMessage::alert( AlertMessage::alert(
&mut std::io::stderr().lock(), &mut std::io::stderr().lock(),
@@ -67,7 +63,7 @@ impl Detection {
.ok(); .ok();
return vec![]; return vec![];
} }
let mut parseerror_count = rulefile_loader.errorrule_count;
let return_if_success = |mut rule: RuleNode| { let return_if_success = |mut rule: RuleNode| {
let err_msgs_result = rule.init(); let err_msgs_result = rule.init();
if err_msgs_result.is_ok() { if err_msgs_result.is_ok() {
@@ -83,18 +79,24 @@ impl Detection {
err_msgs.iter().for_each(|err_msg| { err_msgs.iter().for_each(|err_msg| {
AlertMessage::warn(&mut std::io::stdout().lock(), err_msg.to_string()).ok(); AlertMessage::warn(&mut std::io::stdout().lock(), err_msg.to_string()).ok();
}); });
parseerror_count += 1;
println!(""); // 一行開けるためのprintln println!(""); // 一行開けるためのprintln
}); });
return Option::None; return Option::None;
}; };
// parse rule files // parse rule files
return rulefile_loader let ret = rulefile_loader
.files .files
.into_iter() .into_iter()
.map(|rule_file_tuple| rule::create_rule(rule_file_tuple.0, rule_file_tuple.1)) .map(|rule_file_tuple| rule::create_rule(rule_file_tuple.0, rule_file_tuple.1))
.filter_map(return_if_success) .filter_map(return_if_success)
.collect(); .collect();
Detection::print_rule_load_info(
&rulefile_loader.rulecounter,
&parseerror_count,
&rulefile_loader.ignorerule_count,
);
return ret;
} }
// 複数のイベントレコードに対して、複数のルールを1個実行します。 // 複数のイベントレコードに対して、複数のルールを1個実行します。
@@ -141,6 +143,34 @@ impl Detection {
} }
} }
pub fn print_unique_results(&self) {
let rules = &self.rules;
let levellabel = Vec::from(["Critical", "High", "Medium", "Low", "Info", "Undeifned"]);
// levclcounts is [(Undeifned), (Info), (Low),(Medium),(High),(Critical)]
let mut levelcounts = Vec::from([0, 0, 0, 0, 0, 0]);
for rule in rules.into_iter() {
if rule.check_exist_countdata() {
let suffix = configs::LEVELMAP
.get(
&rule.yaml["level"]
.as_str()
.unwrap_or("")
.to_owned()
.to_uppercase(),
)
.unwrap_or(&0);
levelcounts[*suffix as usize] += 1;
}
}
let mut total_unique = 0;
levelcounts.reverse();
for (i, value) in levelcounts.iter().enumerate() {
println!("{} alerts {}", levellabel[i], value);
total_unique += value;
}
println!("Unique Events Detected: {}", total_unique);
}
// 複数のイベントレコードに対して、ルールを1個実行します。 // 複数のイベントレコードに対して、ルールを1個実行します。
fn execute_rule(mut rule: RuleNode, records: Arc<Vec<EvtxRecordInfo>>) -> RuleNode { fn execute_rule(mut rule: RuleNode, records: Arc<Vec<EvtxRecordInfo>>) -> RuleNode {
let records = &*records; let records = &*records;
@@ -210,9 +240,9 @@ impl Detection {
return ret; return ret;
} }
pub fn print_rule_load_info( pub fn print_rule_load_info(
rc: HashMap<String, u128>, rc: &HashMap<String, u128>,
parseerror_count: u128, parseerror_count: &u128,
ignore_count: u128, ignore_count: &u128,
) { ) {
let mut total = parseerror_count + ignore_count; let mut total = parseerror_count + ignore_count;
rc.into_iter().for_each(|(key, value)| { rc.into_iter().for_each(|(key, value)| {

View File

@@ -161,7 +161,7 @@ impl Message {
detect_count += detect_infos.len(); detect_count += detect_infos.len();
} }
println!(""); println!("");
println!("Events Detected:{:?}", detect_count); println!("Total Events Detected:{:?}", detect_count);
} }
pub fn iter(&self) -> &BTreeMap<DateTime<Utc>, Vec<DetectInfo>> { pub fn iter(&self) -> &BTreeMap<DateTime<Utc>, Vec<DetectInfo>> {

View File

@@ -102,6 +102,9 @@ impl RuleNode {
} }
return ret; return ret;
} }
pub fn check_exist_countdata(&self) -> bool {
self.countdata.len() > 0
}
} }
/// Ruleファイルのdetectionを表すード /// Ruleファイルのdetectionを表すード

View File

@@ -130,8 +130,8 @@ fn analysis_files(evtx_files: Vec<PathBuf>) {
} }
detection = analysis_file(evtx_file, detection); detection = analysis_file(evtx_file, detection);
} }
after_fact(); after_fact();
detection.print_unique_results();
} }
// Windowsイベントログファイルを1ファイル分解析する。 // Windowsイベントログファイルを1ファイル分解析する。

View File

@@ -15,8 +15,8 @@ use yaml_rust::YamlLoader;
pub struct ParseYaml { pub struct ParseYaml {
pub files: Vec<(String, yaml_rust::Yaml)>, pub files: Vec<(String, yaml_rust::Yaml)>,
pub rulecounter: HashMap<String, u128>, pub rulecounter: HashMap<String, u128>,
pub ignore_count: u128, pub ignorerule_count: u128,
pub parseerror_count: u128, pub errorrule_count: u128,
} }
impl ParseYaml { impl ParseYaml {
@@ -24,8 +24,8 @@ impl ParseYaml {
ParseYaml { ParseYaml {
files: Vec::new(), files: Vec::new(),
rulecounter: HashMap::new(), rulecounter: HashMap::new(),
ignore_count: 0, ignorerule_count: 0,
parseerror_count: 0, errorrule_count: 0,
} }
} }
@@ -73,7 +73,7 @@ impl ParseYaml {
read_content.unwrap_err() read_content.unwrap_err()
), ),
)?; )?;
self.parseerror_count += 1; self.errorrule_count += 1;
return io::Result::Ok(ret); return io::Result::Ok(ret);
} }
@@ -88,7 +88,7 @@ impl ParseYaml {
yaml_contents.unwrap_err() yaml_contents.unwrap_err()
), ),
)?; )?;
self.parseerror_count += 1; self.errorrule_count += 1;
return io::Result::Ok(ret); return io::Result::Ok(ret);
} }
@@ -105,21 +105,13 @@ impl ParseYaml {
.filter_map(|(filepath, yaml_doc)| { .filter_map(|(filepath, yaml_doc)| {
// ignoreフラグがONになっているルールは無視する。 // ignoreフラグがONになっているルールは無視する。
if yaml_doc["ignore"].as_bool().unwrap_or(false) { if yaml_doc["ignore"].as_bool().unwrap_or(false) {
self.ignore_count += 1; self.ignorerule_count += 1;
return Option::None; return Option::None;
} }
self.rulecounter.insert( self.rulecounter.insert(
yaml_doc["rulesection"] yaml_doc["ruletype"].as_str().unwrap_or("other").to_string(),
.as_str()
.unwrap_or("other")
.to_string(),
self.rulecounter self.rulecounter
.get( .get(&yaml_doc["ruletype"].as_str().unwrap_or("other").to_string())
&yaml_doc["rulesection"]
.as_str()
.unwrap_or("other")
.to_string(),
)
.unwrap_or(&0) .unwrap_or(&0)
+ 1, + 1,
); );