feature fillter no use rules

This commit is contained in:
kazuminn
2021-11-30 22:54:36 +09:00
parent bc230f7cd5
commit 341a5e4f86
8 changed files with 95 additions and 12 deletions

0
config/exclude-rules.txt Normal file
View File

0
config/noisy-rules.txt Normal file
View File

View File

@@ -58,6 +58,7 @@ fn build_app<'a>() -> ArgMatches<'a> {
-u --utc 'Output time in UTC format (default: local time)' -u --utc 'Output time in UTC format (default: local time)'
-d --directory=[DIRECTORY] 'Directory of multiple .evtx files' -d --directory=[DIRECTORY] 'Directory of multiple .evtx files'
-s --statistics 'Prints statistics of event IDs' -s --statistics 'Prints statistics of event IDs'
-n --show-noisyalerts 'do not exclude noisy rules'
-t --threadnum=[NUM] 'Thread number (default: optimal number for performance)' -t --threadnum=[NUM] 'Thread number (default: optimal number for performance)'
--contributors 'Prints the list of contributors'"; --contributors 'Prints the list of contributors'";
App::new(&program) App::new(&program)

View File

@@ -11,6 +11,7 @@ use crate::detections::print::MESSAGES;
use crate::detections::rule; use crate::detections::rule;
use crate::detections::rule::RuleNode; use crate::detections::rule::RuleNode;
use crate::detections::utils::get_serde_number_to_string; use crate::detections::utils::get_serde_number_to_string;
use crate::fillter::RuleFill;
use crate::yaml::ParseYaml; use crate::yaml::ParseYaml;
use std::sync::Arc; use std::sync::Arc;
@@ -51,10 +52,15 @@ impl Detection {
} }
// ルールファイルをパースします。 // ルールファイルをパースします。
pub fn parse_rule_files(level: String, rulespath: Option<&str>) -> Vec<RuleNode> { pub fn parse_rule_files(
level: String,
rulespath: Option<&str>,
fill_ids: RuleFill,
) -> Vec<RuleNode> {
// ルールファイルのパースを実行 // ルールファイルのパースを実行
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, fill_ids);
if result_readdir.is_err() { if result_readdir.is_err() {
AlertMessage::alert( AlertMessage::alert(
&mut std::io::stderr().lock(), &mut std::io::stderr().lock(),

6
src/fillter.rs Normal file
View File

@@ -0,0 +1,6 @@
use std::collections::HashMap;
#[derive(Clone, Debug)]
pub struct RuleFill {
pub no_use_rule: HashMap<String, bool>,
}

View File

@@ -1,5 +1,6 @@
pub mod afterfact; pub mod afterfact;
pub mod detections; pub mod detections;
pub mod fillter;
pub mod notify; pub mod notify;
pub mod omikuji; pub mod omikuji;
pub mod timeline; pub mod timeline;

View File

@@ -6,10 +6,12 @@ use evtx::{EvtxParser, ParserSettings};
use hayabusa::detections::detection; use hayabusa::detections::detection;
use hayabusa::detections::detection::EvtxRecordInfo; use hayabusa::detections::detection::EvtxRecordInfo;
use hayabusa::detections::print::AlertMessage; use hayabusa::detections::print::AlertMessage;
use hayabusa::fillter;
use hayabusa::omikuji::Omikuji; use hayabusa::omikuji::Omikuji;
use hayabusa::{afterfact::after_fact, detections::utils}; use hayabusa::{afterfact::after_fact, detections::utils};
use hayabusa::{detections::configs, timeline::timeline::Timeline}; use hayabusa::{detections::configs, timeline::timeline::Timeline};
use hhmmss::Hhmmss; use hhmmss::Hhmmss;
use std::collections::HashMap;
use std::{ use std::{
fs::{self, File}, fs::{self, File},
path::PathBuf, path::PathBuf,
@@ -119,9 +121,29 @@ fn analysis_files(evtx_files: Vec<PathBuf>) {
.unwrap_or("INFO") .unwrap_or("INFO")
.to_uppercase(); .to_uppercase();
println!("Analyzing event files: {:?}", evtx_files.len()); println!("Analyzing event files: {:?}", evtx_files.len());
//除外ルール前処理
let mut ids = String::from_utf8(fs::read("config/exclude-rules.txt").unwrap()).unwrap();
if !configs::CONFIG
.read()
.unwrap()
.args
.is_present("show-noisyalerts")
{
ids += &String::from_utf8(fs::read("config/noisy-rules.txt").unwrap()).unwrap();
}
let mut fill_ids = fillter::RuleFill {
no_use_rule: HashMap::new(),
};
for v in ids.split_whitespace().next() {
fill_ids.no_use_rule.insert(v.to_string(), true);
}
let rule_files = detection::Detection::parse_rule_files( let rule_files = detection::Detection::parse_rule_files(
level, level,
configs::CONFIG.read().unwrap().args.value_of("rules"), configs::CONFIG.read().unwrap().args.value_of("rules"),
fill_ids,
); );
let mut detection = detection::Detection::new(rule_files); let mut detection = detection::Detection::new(rule_files);
for evtx_file in evtx_files { for evtx_file in evtx_files {

View File

@@ -3,6 +3,7 @@ extern crate yaml_rust;
use crate::detections::configs; use crate::detections::configs;
use crate::detections::print::AlertMessage; use crate::detections::print::AlertMessage;
use crate::fillter::RuleFill;
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fs; use std::fs;
@@ -42,13 +43,18 @@ impl ParseYaml {
Ok(file_content) Ok(file_content)
} }
pub fn read_dir<P: AsRef<Path>>(&mut self, path: P, level: &str) -> io::Result<String> { pub fn read_dir<P: AsRef<Path>>(
&mut self,
path: P,
level: &str,
fill_ids: RuleFill,
) -> io::Result<String> {
let mut entries = fs::read_dir(path)?; let mut entries = fs::read_dir(path)?;
let yaml_docs = entries.try_fold(vec![], |mut ret, entry| { let yaml_docs = entries.try_fold(vec![], |mut ret, entry| {
let entry = entry?; let entry = entry?;
// フォルダは再帰的に呼び出す。 // フォルダは再帰的に呼び出す。
if entry.file_type()?.is_dir() { if entry.file_type()?.is_dir() {
self.read_dir(entry.path(), level)?; self.read_dir(entry.path(), level, fill_ids.clone())?;
return io::Result::Ok(ret); return io::Result::Ok(ret);
} }
// ファイル以外は無視 // ファイル以外は無視
@@ -115,6 +121,7 @@ impl ParseYaml {
.unwrap_or(&0) .unwrap_or(&0)
+ 1, + 1,
); );
if configs::CONFIG.read().unwrap().args.is_present("verbose") { if configs::CONFIG.read().unwrap().args.is_present("verbose") {
println!("Loaded yml file path: {}", filepath); println!("Loaded yml file path: {}", filepath);
} }
@@ -130,6 +137,18 @@ impl ParseYaml {
return Option::None; return Option::None;
} }
//除外されたルールは無視する
match fill_ids
.no_use_rule
.get(&yaml_doc["id"].as_str().unwrap().to_string())
{
Some(_) => (),
None => {
self.ignorerule_count += 1;
return Option::None;
}
}
return Option::Some((filepath, yaml_doc)); return Option::Some((filepath, yaml_doc));
}) })
.collect(); .collect();
@@ -148,7 +167,14 @@ mod tests {
#[test] #[test]
fn test_read_dir_yaml() { fn test_read_dir_yaml() {
let mut yaml = yaml::ParseYaml::new(); let mut yaml = yaml::ParseYaml::new();
&yaml.read_dir("test_files/rules/yaml/".to_string(), &"".to_owned()); let mut fill_ids = fillter::RuleFill {
no_use_rule: HashMap::new(),
};
&yaml.read_dir(
"test_files/rules/yaml/".to_string(),
&"".to_owned(),
fill_ids,
);
assert_ne!(yaml.files.len(), 0); assert_ne!(yaml.files.len(), 0);
} }
@@ -183,7 +209,10 @@ mod tests {
fn test_default_level_read_yaml() { fn test_default_level_read_yaml() {
let mut yaml = yaml::ParseYaml::new(); let mut yaml = yaml::ParseYaml::new();
let path = Path::new("test_files/rules/level_yaml"); let path = Path::new("test_files/rules/level_yaml");
yaml.read_dir(path.to_path_buf(), &"").unwrap(); let mut fill_ids = RuleFill {
no_use_rule: HashMap::new(),
};
yaml.read_dir(path.to_path_buf(), &"", fill_ids).unwrap();
assert_eq!(yaml.files.len(), 4); assert_eq!(yaml.files.len(), 4);
} }
@@ -191,36 +220,54 @@ mod tests {
fn test_info_level_read_yaml() { fn test_info_level_read_yaml() {
let mut yaml = yaml::ParseYaml::new(); let mut yaml = yaml::ParseYaml::new();
let path = Path::new("test_files/rules/level_yaml"); let path = Path::new("test_files/rules/level_yaml");
yaml.read_dir(path.to_path_buf(), &"INFO").unwrap(); let mut fill_ids = RuleFill {
no_use_rule: HashMap::new(),
};
yaml.read_dir(path.to_path_buf(), &"INFO", fill_ids)
.unwrap();
assert_eq!(yaml.files.len(), 5); assert_eq!(yaml.files.len(), 5);
} }
#[test] #[test]
fn test_low_level_read_yaml() { fn test_low_level_read_yaml() {
let mut yaml = yaml::ParseYaml::new(); let mut yaml = yaml::ParseYaml::new();
let path = Path::new("test_files/rules/level_yaml"); let path = Path::new("test_files/rules/level_yaml");
let mut fill_ids = RuleFill {
yaml.read_dir(path.to_path_buf(), &"LOW").unwrap(); no_use_rule: HashMap::new(),
};
yaml.read_dir(path.to_path_buf(), &"LOW", fill_ids).unwrap();
assert_eq!(yaml.files.len(), 4); assert_eq!(yaml.files.len(), 4);
} }
#[test] #[test]
fn test_medium_level_read_yaml() { fn test_medium_level_read_yaml() {
let mut yaml = yaml::ParseYaml::new(); let mut yaml = yaml::ParseYaml::new();
let path = Path::new("test_files/rules/level_yaml"); let path = Path::new("test_files/rules/level_yaml");
yaml.read_dir(path.to_path_buf(), &"MEDIUM").unwrap(); let mut fill_ids = RuleFill {
no_use_rule: HashMap::new(),
};
yaml.read_dir(path.to_path_buf(), &"MEDIUM", fill_ids)
.unwrap();
assert_eq!(yaml.files.len(), 3); assert_eq!(yaml.files.len(), 3);
} }
#[test] #[test]
fn test_high_level_read_yaml() { fn test_high_level_read_yaml() {
let mut yaml = yaml::ParseYaml::new(); let mut yaml = yaml::ParseYaml::new();
let path = Path::new("test_files/rules/level_yaml"); let path = Path::new("test_files/rules/level_yaml");
yaml.read_dir(path.to_path_buf(), &"HIGH").unwrap(); let mut fill_ids = RuleFill {
no_use_rule: HashMap::new(),
};
yaml.read_dir(path.to_path_buf(), &"HIGH", fill_ids)
.unwrap();
assert_eq!(yaml.files.len(), 2); assert_eq!(yaml.files.len(), 2);
} }
#[test] #[test]
fn test_critical_level_read_yaml() { fn test_critical_level_read_yaml() {
let mut yaml = yaml::ParseYaml::new(); let mut yaml = yaml::ParseYaml::new();
let path = Path::new("test_files/rules/level_yaml"); let path = Path::new("test_files/rules/level_yaml");
yaml.read_dir(path.to_path_buf(), &"CRITICAL").unwrap(); let mut fill_ids = RuleFill {
no_use_rule: HashMap::new(),
};
yaml.read_dir(path.to_path_buf(), &"CRITICAL", fill_ids)
.unwrap();
assert_eq!(yaml.files.len(), 1); assert_eq!(yaml.files.len(), 1);
} }
} }