525 show technique tags (#534)

* added --all-tags optiojn #525

- exclude load output_tag.txt when --all-tags option is true

* fixed output to MitreAttack column #525

* added test

* added period

* updated usage in readme

* added test file

* added all-tags option in readme

* readme update

* fixed all-tags option description in help option

Co-authored-by: Tanaka Zakku <71482215+YamatoSecurity@users.noreply.github.com>
This commit is contained in:
DustInDark
2022-05-23 00:19:04 +09:00
committed by GitHub
parent 69564103de
commit 684c8a9688
6 changed files with 90 additions and 21 deletions
+2 -1
View File
@@ -76,6 +76,7 @@ fn build_app<'a>() -> ArgMatches<'a> {
-r --rules=[RULEDIRECTORY/RULEFILE] 'Rule file or directory (default: ./rules)'
-C --config=[RULECONFIGDIRECTORY] 'Rule config folder. (Default: ./rules/config)'
-o --output=[CSV_TIMELINE] 'Save the timeline in CSV format. (Example: results.csv)'
--all-tags 'Output all tags when saving to a CSV file.'
-v --verbose 'Output verbose information.'
-D --enable-deprecated-rules 'Enable rules marked as deprecated.'
-n --enable-noisy-rules 'Enable rules marked as noisy.'
@@ -87,7 +88,7 @@ fn build_app<'a>() -> ArgMatches<'a> {
--rfc-2822 'Output date and time in RFC 2822 format. (Example: Mon, 07 Aug 2006 12:34:56 -0600)'
--rfc-3339 'Output date and time in RFC 3339 format. (Example: 2006-08-07T12:34:56.485214 -06:00)'
-U --utc 'Output time in UTC format. (Default: local time)'
--no-color 'Disable color output'
--no-color 'Disable color output.'
-t --thread-number=[NUMBER] 'Thread number. (Default: Optimal number for performance.)'
-s --statistics 'Prints statistics of event IDs.'
-L --logon-summary 'Successful and failed logons summary.'
+20 -7
View File
@@ -206,13 +206,26 @@ impl Detection {
/// 条件に合致したレコードを表示するための関数
fn insert_message(rule: &RuleNode, record_info: &EvtxRecordInfo) {
let tag_info: Vec<String> = rule.yaml["tags"]
.as_vec()
.unwrap_or(&Vec::default())
.iter()
.filter_map(|info| TAGS_CONFIG.get(info.as_str().unwrap_or(&String::default())))
.map(|str| str.to_owned())
.collect();
let tag_info: Vec<String> = match TAGS_CONFIG.is_empty() {
false => rule.yaml["tags"]
.as_vec()
.unwrap_or(&Vec::default())
.iter()
.filter_map(|info| TAGS_CONFIG.get(info.as_str().unwrap_or(&String::default())))
.map(|str| str.to_owned())
.collect(),
true => 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(),
_ => info.as_str().unwrap_or("").replace("attack.", ""),
},
)
.collect(),
};
let recinfo = record_info
.record_information
+56 -8
View File
@@ -60,10 +60,16 @@ lazy_static! {
.unwrap()
.args
.is_present("logon-summary");
pub static ref TAGS_CONFIG: HashMap<String, String> =
Message::create_output_filter_config("config/output_tag.txt");
pub static ref CH_CONFIG: HashMap<String, String> =
Message::create_output_filter_config("config/channel_abbreviations.txt");
pub static ref TAGS_CONFIG: HashMap<String, String> = Message::create_output_filter_config(
"config/output_tag.txt",
true,
configs::CONFIG.read().unwrap().args.is_present("all-tags")
);
pub static ref CH_CONFIG: HashMap<String, String> = Message::create_output_filter_config(
"config/channel_abbreviations.txt",
false,
configs::CONFIG.read().unwrap().args.is_present("all-tags")
);
pub static ref PIVOT_KEYWORD_LIST_FLAG: bool = configs::CONFIG
.read()
.unwrap()
@@ -85,7 +91,15 @@ impl Message {
/// ファイルパスで記載されたtagでのフル名、表示の際に置き換えられる文字列のHashMapを作成する関数。tagではこのHashMapのキーに対応しない出力は出力しないものとする
/// ex. attack.impact,Impact
pub fn create_output_filter_config(path: &str) -> HashMap<String, String> {
pub fn create_output_filter_config(
path: &str,
read_tags: bool,
pass_flag: bool,
) -> HashMap<String, String> {
let mut ret: HashMap<String, String> = HashMap::new();
if read_tags && pass_flag {
return ret;
}
let read_result = utils::read_csv(path);
if read_result.is_err() {
AlertMessage::alert(
@@ -95,7 +109,6 @@ impl Message {
.ok();
return HashMap::default();
}
let mut ret: HashMap<String, String> = HashMap::new();
read_result.unwrap().into_iter().for_each(|line| {
if line.len() != 2 {
return;
@@ -516,13 +529,48 @@ mod tests {
#[test]
/// test of loading output filter config by output_tag.txt
fn test_load_output_tag() {
let actual = Message::create_output_filter_config("test_files/config/output_tag.txt");
let actual =
Message::create_output_filter_config("test_files/config/output_tag.txt", true, false);
let expected: HashMap<String, String> = HashMap::from([
("attack.impact".to_string(), "Impact".to_string()),
("xxx".to_string(), "yyy".to_string()),
]);
_check_hashmap_element(&expected, actual);
}
assert_eq!(actual.len(), expected.len());
#[test]
/// test of loading pass by output_tag.txt
fn test_no_load_output_tag() {
let actual =
Message::create_output_filter_config("test_files/config/output_tag.txt", true, true);
let expected: HashMap<String, String> = HashMap::new();
_check_hashmap_element(&expected, actual);
}
#[test]
/// loading test to channel_abbrevations.txt
fn test_load_abbrevations() {
let actual = Message::create_output_filter_config(
"test_files/config/channel_abbreviations.txt",
false,
true,
);
let actual2 = Message::create_output_filter_config(
"test_files/config/channel_abbreviations.txt",
false,
false,
);
let expected: HashMap<String, String> = HashMap::from([
("Security".to_string(), "Sec".to_string()),
("xxx".to_string(), "yyy".to_string()),
]);
_check_hashmap_element(&expected, actual);
_check_hashmap_element(&expected, actual2);
}
/// check two HashMap element length and value
fn _check_hashmap_element(expected: &HashMap<String, String>, actual: HashMap<String, String>) {
assert_eq!(expected.len(), actual.len());
for (k, v) in expected.iter() {
assert!(actual.get(k).unwrap_or(&String::default()) == v);
}