@@ -2,8 +2,6 @@ extern crate lazy_static;
|
||||
use crate::detections::configs;
|
||||
use crate::detections::utils;
|
||||
use crate::detections::utils::get_serde_number_to_string;
|
||||
use crate::filter::DataFilterRule;
|
||||
use crate::filter::FILTER_REGEX;
|
||||
use chrono::{DateTime, Local, TimeZone, Utc};
|
||||
use hashbrown::HashMap;
|
||||
use lazy_static::lazy_static;
|
||||
@@ -90,7 +88,6 @@ impl Message {
|
||||
fn parse_message(&mut self, event_record: &Value, output: String) -> String {
|
||||
let mut return_message: String = output;
|
||||
let mut hash_map: HashMap<String, String> = HashMap::new();
|
||||
let mut output_filter: Option<&DataFilterRule> = None;
|
||||
for caps in ALIASREGEX.captures_iter(&return_message) {
|
||||
let full_target_str = &caps[0];
|
||||
let target_length = full_target_str.chars().count() - 2; // The meaning of 2 is two percent
|
||||
@@ -114,17 +111,13 @@ impl Message {
|
||||
if let Some(record) = tmp_event_record.get(s) {
|
||||
is_exist_event_key = true;
|
||||
tmp_event_record = record;
|
||||
output_filter = FILTER_REGEX.get(&s.to_string());
|
||||
}
|
||||
}
|
||||
if is_exist_event_key {
|
||||
let mut hash_value = get_serde_number_to_string(tmp_event_record);
|
||||
if hash_value.is_some() {
|
||||
if output_filter.is_some() {
|
||||
hash_value =
|
||||
utils::replace_target_character(hash_value.as_ref(), output_filter);
|
||||
}
|
||||
hash_map.insert(full_target_str.to_string(), hash_value.unwrap());
|
||||
let hash_value = get_serde_number_to_string(tmp_event_record);
|
||||
if let Some(hash_value) = hash_value {
|
||||
let hash_value = hash_value.replace(r"(\\a|\\f|\\t|\\n|\\r|\\v)", " ");
|
||||
hash_map.insert(full_target_str.to_string(), hash_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,7 +448,11 @@ impl PipeElement {
|
||||
regex::escape(pattern)
|
||||
} else {
|
||||
// wildcardの場合、"*"は".*"という正規表現に変換し、"?"は"."に変換する。
|
||||
let wildcard_regex_value = if *pattern == "*" { ".*" } else { "." };
|
||||
let wildcard_regex_value = if *pattern == "*" {
|
||||
"(.|\\a|\\f|\\t|\\n|\\r|\\v)*"
|
||||
} else {
|
||||
"."
|
||||
};
|
||||
wildcard_regex_value.to_string()
|
||||
};
|
||||
|
||||
@@ -1516,7 +1520,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_pipe_pattern_wildcard_asterisk() {
|
||||
let value = PipeElement::pipe_pattern_wildcard(r"*ho*ge*".to_string());
|
||||
assert_eq!("(?i).*ho.*ge.*", value);
|
||||
assert_eq!("(?i)(.|\\a|\\f|\\t|\\n|\\r|\\v)*ho(.|\\a|\\f|\\t|\\n|\\r|\\v)*ge(.|\\a|\\f|\\t|\\n|\\r|\\v)*", value);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1532,7 +1536,10 @@ mod tests {
|
||||
// wildcardの「\\*」は文字列としての「\」と正規表現の「.*」を表す。
|
||||
// 文字列としての「\」はエスケープされるので、「\\.*」が正解
|
||||
let value = PipeElement::pipe_pattern_wildcard(r"\\*ho\\*ge\\*".to_string());
|
||||
assert_eq!(r"(?i)\\.*ho\\.*ge\\.*", value);
|
||||
assert_eq!(
|
||||
r"(?i)\\(.|\a|\f|\t|\n|\r|\v)*ho\\(.|\a|\f|\t|\n|\r|\v)*ge\\(.|\a|\f|\t|\n|\r|\v)*",
|
||||
value
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1562,13 +1569,19 @@ mod tests {
|
||||
#[test]
|
||||
fn test_pipe_pattern_wildcard_mixed() {
|
||||
let value = PipeElement::pipe_pattern_wildcard(r"\\*\****\*\\*".to_string());
|
||||
assert_eq!(r"(?i)\\.*\*.*.*.*\*\\.*", value);
|
||||
assert_eq!(
|
||||
r"(?i)\\(.|\a|\f|\t|\n|\r|\v)*\*(.|\a|\f|\t|\n|\r|\v)*(.|\a|\f|\t|\n|\r|\v)*(.|\a|\f|\t|\n|\r|\v)*\*\\(.|\a|\f|\t|\n|\r|\v)*",
|
||||
value
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pipe_pattern_wildcard_many_backshashs() {
|
||||
let value = PipeElement::pipe_pattern_wildcard(r"\\\*ho\\\*ge\\\".to_string());
|
||||
assert_eq!(r"(?i)\\\\.*ho\\\\.*ge\\\\\\", value);
|
||||
assert_eq!(
|
||||
r"(?i)\\\\(.|\a|\f|\t|\n|\r|\v)*ho\\\\(.|\a|\f|\t|\n|\r|\v)*ge\\\\\\",
|
||||
value
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::detections::{detection::EvtxRecordInfo, utils};
|
||||
use crate::filter::FILTER_REGEX;
|
||||
use downcast_rs::Downcast;
|
||||
use std::{sync::Arc, vec};
|
||||
use yaml_rust::Yaml;
|
||||
@@ -309,9 +308,6 @@ impl SelectionNode for LeafSelectionNode {
|
||||
]
|
||||
}
|
||||
*/
|
||||
|
||||
let filter_rule = FILTER_REGEX.get(self.get_key());
|
||||
|
||||
if self.get_key() == "EventData" {
|
||||
let values = utils::get_event_value("Event.EventData.Data", &event_record.record);
|
||||
if values.is_none() {
|
||||
@@ -326,15 +322,12 @@ impl SelectionNode for LeafSelectionNode {
|
||||
let eventdata_data = values.unwrap();
|
||||
if eventdata_data.is_boolean() || eventdata_data.is_i64() || eventdata_data.is_string()
|
||||
{
|
||||
let replaced_str = utils::replace_target_character(
|
||||
event_record.get_value(self.get_key()),
|
||||
filter_rule,
|
||||
);
|
||||
let event_value = event_record.get_value(self.get_key());
|
||||
return self
|
||||
.matcher
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.is_match(replaced_str.as_ref(), event_record);
|
||||
.is_match(event_value, event_record);
|
||||
}
|
||||
// 配列の場合は配列の要素のどれか一つでもルールに合致すれば条件に一致したことにする。
|
||||
if eventdata_data.is_array() {
|
||||
@@ -343,15 +336,12 @@ impl SelectionNode for LeafSelectionNode {
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|ary_element| {
|
||||
let replaced_str = utils::replace_target_character(
|
||||
utils::value_to_string(ary_element).as_ref(),
|
||||
filter_rule,
|
||||
);
|
||||
let event_value = utils::value_to_string(ary_element);
|
||||
return self
|
||||
.matcher
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.is_match(replaced_str.as_ref(), event_record);
|
||||
.is_match(event_value.as_ref(), event_record);
|
||||
});
|
||||
} else {
|
||||
return self
|
||||
@@ -362,14 +352,12 @@ impl SelectionNode for LeafSelectionNode {
|
||||
}
|
||||
}
|
||||
|
||||
let replaced_str =
|
||||
utils::replace_target_character(self.get_event_value(event_record), filter_rule);
|
||||
|
||||
let event_value = self.get_event_value(event_record);
|
||||
return self
|
||||
.matcher
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.is_match(replaced_str.as_ref(), event_record);
|
||||
.is_match(event_value, event_record);
|
||||
}
|
||||
|
||||
fn init(&mut self) -> Result<(), Vec<String>> {
|
||||
|
||||
@@ -3,7 +3,6 @@ extern crate csv;
|
||||
extern crate regex;
|
||||
|
||||
use crate::detections::configs;
|
||||
use crate::filter::DataFilterRule;
|
||||
|
||||
use tokio::runtime::Builder;
|
||||
use tokio::runtime::Runtime;
|
||||
@@ -40,26 +39,6 @@ pub fn check_regex(string: &str, regex_list: &[Regex]) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// replace string from all defined regex in input to replace_str
|
||||
pub fn replace_target_character<'a>(
|
||||
input_str: Option<&'a String>,
|
||||
replace_rule: Option<&'a DataFilterRule>,
|
||||
) -> Option<String> {
|
||||
input_str?;
|
||||
if replace_rule.is_none() {
|
||||
return Some(input_str.unwrap().to_string());
|
||||
}
|
||||
|
||||
let replace_regex_rule = &replace_rule.unwrap().regex_rule;
|
||||
let replace_str = &replace_rule.unwrap().replace_str;
|
||||
|
||||
Some(
|
||||
replace_regex_rule
|
||||
.replace_all(input_str.unwrap(), replace_str)
|
||||
.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn check_allowlist(target: &str, regexes: &[Regex]) -> bool {
|
||||
for regex in regexes {
|
||||
if regex.is_match(target) {
|
||||
@@ -247,7 +226,7 @@ pub fn create_rec_info(data: Value, path: String, keys: &[String]) -> EvtxRecord
|
||||
continue;
|
||||
}
|
||||
|
||||
rec.key_2_value.insert(key.to_string(), val.unwrap());
|
||||
rec.key_2_value.insert(key.trim().to_string(), val.unwrap());
|
||||
}
|
||||
|
||||
rec
|
||||
@@ -256,7 +235,6 @@ pub fn create_rec_info(data: Value, path: String, keys: &[String]) -> EvtxRecord
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::detections::utils;
|
||||
use crate::filter::DataFilterRule;
|
||||
use regex::Regex;
|
||||
use serde_json::Value;
|
||||
|
||||
@@ -347,25 +325,4 @@ mod tests {
|
||||
|
||||
assert!(utils::get_serde_number_to_string(&event_record["Event"]["EventData"]).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 指定された文字から指定されたregexぉ実行する関数が動作するかのテスト
|
||||
fn test_remove_space_control() {
|
||||
let test_filter_rule = DataFilterRule {
|
||||
regex_rule: Regex::new(r"[\r\n\t]+").unwrap(),
|
||||
replace_str: "".to_string(),
|
||||
};
|
||||
let none_test_str: Option<&String> = None;
|
||||
|
||||
assert!(utils::replace_target_character(none_test_str, None).is_none());
|
||||
|
||||
assert!(utils::replace_target_character(none_test_str, Some(&test_filter_rule)).is_none());
|
||||
|
||||
let tmp = "h\ra\ny\ta\tb\nu\r\nsa".to_string();
|
||||
let test_str: Option<&String> = Some(&tmp);
|
||||
assert_eq!(
|
||||
utils::replace_target_character(test_str, Some(&test_filter_rule)).unwrap(),
|
||||
"hayabusa"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ use crate::detections::configs;
|
||||
use crate::detections::print::AlertMessage;
|
||||
use crate::detections::print::ERROR_LOG_STACK;
|
||||
use crate::detections::print::QUIET_ERRORS_FLAG;
|
||||
use crate::detections::utils;
|
||||
use hashbrown::HashMap;
|
||||
use hashbrown::HashSet;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
@@ -14,7 +12,6 @@ use std::io::{BufRead, BufReader};
|
||||
lazy_static! {
|
||||
static ref IDS_REGEX: Regex =
|
||||
Regex::new(r"^[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}$").unwrap();
|
||||
pub static ref FILTER_REGEX: HashMap<String, DataFilterRule> = load_record_filters();
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -23,70 +20,6 @@ pub struct DataFilterRule {
|
||||
pub replace_str: String,
|
||||
}
|
||||
|
||||
fn load_record_filters() -> HashMap<String, DataFilterRule> {
|
||||
let file_path = "./rules/config/regex/record_data_filter.txt";
|
||||
let read_result = utils::read_csv(file_path);
|
||||
let mut ret = HashMap::new();
|
||||
if read_result.is_err() {
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
AlertMessage::warn(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
&format!("{} does not exist", file_path),
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
if !*QUIET_ERRORS_FLAG {
|
||||
ERROR_LOG_STACK
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(format!("{} does not exist", file_path));
|
||||
}
|
||||
return HashMap::default();
|
||||
}
|
||||
read_result.unwrap().into_iter().for_each(|line| {
|
||||
if line.len() != 3 {
|
||||
return;
|
||||
}
|
||||
|
||||
let empty = &"".to_string();
|
||||
let key = line.get(0).unwrap_or(empty).trim();
|
||||
let regex_str = line.get(1).unwrap_or(empty).trim();
|
||||
let replaced_str = line.get(2).unwrap_or(empty).trim();
|
||||
if key.is_empty() || regex_str.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let regex_rule: Option<Regex> = match Regex::new(regex_str) {
|
||||
Ok(regex) => Some(regex),
|
||||
Err(_err) => {
|
||||
let errmsg = "failed to read regex filter in record_data_filter.txt";
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), errmsg).ok();
|
||||
}
|
||||
if !*QUIET_ERRORS_FLAG {
|
||||
ERROR_LOG_STACK
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(format!("[ERROR] {}", errmsg));
|
||||
}
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if regex_rule.is_none() {
|
||||
return;
|
||||
}
|
||||
ret.insert(
|
||||
key.to_string(),
|
||||
DataFilterRule {
|
||||
regex_rule: regex_rule.unwrap(),
|
||||
replace_str: replaced_str.to_string(),
|
||||
},
|
||||
);
|
||||
});
|
||||
ret
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RuleExclude {
|
||||
pub no_use_rule: HashSet<String>,
|
||||
|
||||
Reference in New Issue
Block a user