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:
@@ -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…
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)| {
|
||||||
|
|||||||
@@ -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>> {
|
||||||
|
|||||||
@@ -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を表すノード
|
||||||
|
|||||||
@@ -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ファイル分解析する。
|
||||||
|
|||||||
26
src/yaml.rs
26
src/yaml.rs
@@ -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,
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user