Added Channel column and Channel Abbreviations (#508)

* added Channel output #504

* added test #504

* fixed clippy warnings

* fixed convert serde value to Channel #504

* added channel output config #504

* added doc #504

* added Channel column and Channel addreviation

* fixed file name typo

* changed channel position #504

* fixed markdown warnings in CHANGELOG

* readme update

Co-authored-by: Tanaka Zakku <71482215+YamatoSecurity@users.noreply.github.com>
This commit is contained in:
DustInDark
2022-04-20 16:12:53 +09:00
committed by GitHub
parent dbf3c55bc4
commit b43eb853e9
9 changed files with 175 additions and 23 deletions
+33 -12
View File
@@ -19,6 +19,7 @@ use std::process;
pub struct CsvFormat<'a> {
timestamp: &'a str,
computer: &'a str,
channel: &'a str,
event_i_d: &'a str,
level: &'a str,
mitre_attack: &'a str,
@@ -35,6 +36,7 @@ pub struct CsvFormat<'a> {
pub struct DisplayFormat<'a> {
timestamp: &'a str,
pub computer: &'a str,
pub channel: &'a str,
pub event_i_d: &'a str,
pub level: &'a str,
pub rule_title: &'a str,
@@ -167,6 +169,7 @@ fn emit_csv<W: std::io::Write>(
level: &_format_cell(&level, ColPos::Other, colors),
computer: &_format_cell(&detect_info.computername, ColPos::Other, colors),
event_i_d: &_format_cell(&detect_info.eventid, ColPos::Other, colors),
channel: &_format_cell(&detect_info.channel, ColPos::Other, colors),
rule_title: &_format_cell(&detect_info.alert, ColPos::Other, colors),
details: &_format_cell(&details, ColPos::Other, colors),
record_information: recinfo.as_deref(),
@@ -179,6 +182,7 @@ fn emit_csv<W: std::io::Write>(
level: &level,
computer: &detect_info.computername,
event_i_d: &detect_info.eventid,
channel: &detect_info.channel,
mitre_attack: &detect_info.tag_info,
rule_title: &detect_info.alert,
details: &detect_info.detail,
@@ -323,6 +327,7 @@ mod tests {
use crate::afterfact::emit_csv;
use crate::detections::print;
use crate::detections::print::DetectInfo;
use crate::detections::print::CH_CONFIG;
use chrono::{Local, TimeZone, Utc};
use serde_json::Value;
use std::fs::File;
@@ -337,12 +342,13 @@ mod tests {
}
fn test_emit_csv_output() {
let testfilepath: &str = "test.evtx";
let testrulepath: &str = "test-rule.yml";
let test_filepath: &str = "test.evtx";
let test_rulepath: &str = "test-rule.yml";
let test_title = "test_title";
let test_level = "high";
let test_computername = "testcomputer";
let test_eventid = "1111";
let test_channel = "Sec";
let output = "pokepoke";
let test_attack = "execution/txxxx.yyy";
let test_recinfo = "record_infoinfo11";
@@ -368,11 +374,15 @@ mod tests {
&event,
output.to_string(),
DetectInfo {
filepath: testfilepath.to_string(),
rulepath: testrulepath.to_string(),
filepath: test_filepath.to_string(),
rulepath: test_rulepath.to_string(),
level: test_level.to_string(),
computername: test_computername.to_string(),
eventid: test_eventid.to_string(),
channel: CH_CONFIG
.get("Security")
.unwrap_or(&String::default())
.to_string(),
alert: test_title.to_string(),
detail: String::default(),
tag_info: test_attack.to_string(),
@@ -385,7 +395,7 @@ mod tests {
.unwrap();
let expect_tz = expect_time.with_timezone(&Local);
let expect =
"Timestamp,Computer,EventID,Level,MitreAttack,RuleTitle,Details,RecordInformation,RulePath,FilePath\n"
"Timestamp,Computer,Channel,EventID,Level,MitreAttack,RuleTitle,Details,RecordInformation,RulePath,FilePath\n"
.to_string()
+ &expect_tz
.clone()
@@ -394,6 +404,8 @@ mod tests {
+ ","
+ test_computername
+ ","
+ test_channel
+ ","
+ test_eventid
+ ","
+ test_level
@@ -406,9 +418,9 @@ mod tests {
+ ","
+ test_recinfo
+ ","
+ testrulepath
+ test_rulepath
+ ","
+ testfilepath
+ test_filepath
+ "\n";
let mut file: Box<dyn io::Write> = Box::new(File::create("./test_emit_csv.csv").unwrap());
assert!(emit_csv(&mut file, false, None).is_ok());
@@ -423,12 +435,13 @@ mod tests {
}
fn check_emit_csv_display() {
let testfilepath: &str = "test2.evtx";
let testrulepath: &str = "test-rule2.yml";
let test_filepath: &str = "test2.evtx";
let test_rulepath: &str = "test-rule2.yml";
let test_title = "test_title2";
let test_level = "medium";
let test_computername = "testcomputer2";
let test_eventid = "2222";
let expect_channel = "Sysmon";
let output = "displaytest";
let test_attack = "execution/txxxx.zzz";
{
@@ -453,11 +466,15 @@ mod tests {
&event,
output.to_string(),
DetectInfo {
filepath: testfilepath.to_string(),
rulepath: testrulepath.to_string(),
filepath: test_filepath.to_string(),
rulepath: test_rulepath.to_string(),
level: test_level.to_string(),
computername: test_computername.to_string(),
eventid: test_eventid.to_string(),
channel: CH_CONFIG
.get("Microsoft-Windows-Sysmon/Operational")
.unwrap_or(&String::default())
.to_string(),
alert: test_title.to_string(),
detail: String::default(),
tag_info: test_attack.to_string(),
@@ -471,7 +488,7 @@ mod tests {
.unwrap();
let expect_tz = expect_time.with_timezone(&Local);
let expect_header =
"Timestamp|Computer|EventID|Level|RuleTitle|Details|RecordInformation\n";
"Timestamp|Computer|Channel|EventID|Level|RuleTitle|Details|RecordInformation\n";
let expect_colored = expect_header.to_string()
+ &get_white_color_string(
&expect_tz
@@ -482,6 +499,8 @@ mod tests {
+ " | "
+ &get_white_color_string(test_computername)
+ " | "
+ &get_white_color_string(expect_channel)
+ " | "
+ &get_white_color_string(test_eventid)
+ " | "
+ &get_white_color_string(test_level)
@@ -500,6 +519,8 @@ mod tests {
+ " | "
+ test_computername
+ " | "
+ expect_channel
+ " | "
+ test_eventid
+ " | "
+ test_level
+9 -1
View File
@@ -9,7 +9,7 @@ use crate::detections::print::MESSAGES;
use crate::detections::print::PIVOT_KEYWORD_LIST_FLAG;
use crate::detections::print::QUIET_ERRORS_FLAG;
use crate::detections::print::STATISTICS_FLAG;
use crate::detections::print::TAGS_CONFIG;
use crate::detections::print::{CH_CONFIG, TAGS_CONFIG};
use crate::detections::rule;
use crate::detections::rule::AggResult;
use crate::detections::rule::RuleNode;
@@ -219,6 +219,13 @@ impl Detection {
.replace('\"', ""),
eventid: get_serde_number_to_string(&record_info.record["Event"]["System"]["EventID"])
.unwrap_or_else(|| "-".to_owned()),
channel: CH_CONFIG
.get(
&get_serde_number_to_string(&record_info.record["Event"]["System"]["Channel"])
.unwrap_or_default(),
)
.unwrap_or(&String::default())
.to_string(),
alert: rule.yaml["title"].as_str().unwrap_or("").to_string(),
detail: String::default(),
tag_info: tag_info.join(" | "),
@@ -252,6 +259,7 @@ impl Detection {
level: rule.yaml["level"].as_str().unwrap_or("").to_owned(),
computername: "-".to_owned(),
eventid: "-".to_owned(),
channel: "-".to_owned(),
alert: rule.yaml["title"].as_str().unwrap_or("").to_owned(),
detail: output,
record_information: rec_info,
+13 -6
View File
@@ -28,6 +28,7 @@ pub struct DetectInfo {
pub level: String,
pub computername: String,
pub eventid: String,
pub channel: String,
pub alert: String,
pub detail: String,
pub tag_info: String,
@@ -55,7 +56,9 @@ lazy_static! {
.args
.is_present("statistics");
pub static ref TAGS_CONFIG: HashMap<String, String> =
Message::create_tags_config("config/output_tag.txt");
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 PIVOT_KEYWORD_LIST_FLAG: bool = configs::CONFIG
.read()
.unwrap()
@@ -77,7 +80,7 @@ impl Message {
/// ファイルパスで記載されたtagでのフル名、表示の際に置き換えられる文字列のHashMapを作成する関数。tagではこのHashMapのキーに対応しない出力は出力しないものとする
/// ex. attack.impact,Impact
pub fn create_tags_config(path: &str) -> HashMap<String, String> {
pub fn create_output_filter_config(path: &str) -> HashMap<String, String> {
let read_result = utils::read_csv(path);
if read_result.is_err() {
AlertMessage::alert(
@@ -283,6 +286,7 @@ mod tests {
level: "high".to_string(),
computername: "testcomputer1".to_string(),
eventid: "1".to_string(),
channel: String::default(),
alert: "test1".to_string(),
detail: String::default(),
tag_info: "txxx.001".to_string(),
@@ -314,6 +318,7 @@ mod tests {
level: "high".to_string(),
computername: "testcomputer2".to_string(),
eventid: "2".to_string(),
channel: String::default(),
alert: "test2".to_string(),
detail: String::default(),
tag_info: "txxx.002".to_string(),
@@ -345,6 +350,7 @@ mod tests {
level: "high".to_string(),
computername: "testcomputer3".to_string(),
eventid: "3".to_string(),
channel: String::default(),
alert: "test3".to_string(),
detail: String::default(),
tag_info: "txxx.003".to_string(),
@@ -371,6 +377,7 @@ mod tests {
level: "medium".to_string(),
computername: "testcomputer4".to_string(),
eventid: "4".to_string(),
channel: String::default(),
alert: "test4".to_string(),
detail: String::default(),
tag_info: "txxx.004".to_string(),
@@ -380,7 +387,7 @@ mod tests {
let display = format!("{}", format_args!("{:?}", message));
println!("display::::{}", display);
let expect = "Message { map: {1970-01-01T00:00:00Z: [DetectInfo { filepath: \"a\", rulepath: \"test_rule4\", level: \"medium\", computername: \"testcomputer4\", eventid: \"4\", alert: \"test4\", detail: \"CommandLine4: hoge\", tag_info: \"txxx.004\", record_information: Some(\"record_information4\") }], 1996-02-27T01:05:01Z: [DetectInfo { filepath: \"a\", rulepath: \"test_rule\", level: \"high\", computername: \"testcomputer1\", eventid: \"1\", alert: \"test1\", detail: \"CommandLine1: hoge\", tag_info: \"txxx.001\", record_information: Some(\"record_information1\") }, DetectInfo { filepath: \"a\", rulepath: \"test_rule2\", level: \"high\", computername: \"testcomputer2\", eventid: \"2\", alert: \"test2\", detail: \"CommandLine2: hoge\", tag_info: \"txxx.002\", record_information: Some(\"record_information2\") }], 2000-01-21T09:06:01Z: [DetectInfo { filepath: \"a\", rulepath: \"test_rule3\", level: \"high\", computername: \"testcomputer3\", eventid: \"3\", alert: \"test3\", detail: \"CommandLine3: hoge\", tag_info: \"txxx.003\", record_information: Some(\"record_information3\") }]} }";
let expect = "Message { map: {1970-01-01T00:00:00Z: [DetectInfo { filepath: \"a\", rulepath: \"test_rule4\", level: \"medium\", computername: \"testcomputer4\", eventid: \"4\", channel: \"\", alert: \"test4\", detail: \"CommandLine4: hoge\", tag_info: \"txxx.004\", record_information: Some(\"record_information4\") }], 1996-02-27T01:05:01Z: [DetectInfo { filepath: \"a\", rulepath: \"test_rule\", level: \"high\", computername: \"testcomputer1\", eventid: \"1\", channel: \"\", alert: \"test1\", detail: \"CommandLine1: hoge\", tag_info: \"txxx.001\", record_information: Some(\"record_information1\") }, DetectInfo { filepath: \"a\", rulepath: \"test_rule2\", level: \"high\", computername: \"testcomputer2\", eventid: \"2\", channel: \"\", alert: \"test2\", detail: \"CommandLine2: hoge\", tag_info: \"txxx.002\", record_information: Some(\"record_information2\") }], 2000-01-21T09:06:01Z: [DetectInfo { filepath: \"a\", rulepath: \"test_rule3\", level: \"high\", computername: \"testcomputer3\", eventid: \"3\", channel: \"\", alert: \"test3\", detail: \"CommandLine3: hoge\", tag_info: \"txxx.003\", record_information: Some(\"record_information3\") }]} }";
assert_eq!(display, expect);
}
@@ -474,7 +481,7 @@ mod tests {
);
}
#[test]
/// outputで指定されているキー(eventkey_alias.txt内で設定済み)が対象のレコード内に該当する情報がない場合の出力テスト
/// output test when no exist info in target record output and described key-value data in eventkey_alias.txt
fn test_parse_message_not_exist_value_in_record() {
let mut message = Message::new();
let json_str = r##"
@@ -502,9 +509,9 @@ mod tests {
);
}
#[test]
/// output_tag.txtの読み込みテスト
/// test of loading output filter config by output_tag.txt
fn test_load_output_tag() {
let actual = Message::create_tags_config("test_files/config/output_tag.txt");
let actual = Message::create_output_filter_config("test_files/config/output_tag.txt");
let expected: HashMap<String, String> = HashMap::from([
("attack.impact".to_string(), "Impact".to_string()),
("xxx".to_string(), "yyy".to_string()),
+4 -4
View File
@@ -184,11 +184,11 @@ pub fn get_event_value<'a>(key: &str, event_value: &'a Value) -> Option<&'a Valu
pub fn get_thread_num() -> usize {
let def_thread_num_str = num_cpus::get().to_string();
let conf = configs::CONFIG.read().unwrap();
let threadnum = &conf
.args
conf.args
.value_of("thread-number")
.unwrap_or(def_thread_num_str.as_str());
threadnum.parse::<usize>().unwrap()
.unwrap_or(def_thread_num_str.as_str())
.parse::<usize>()
.unwrap()
}
pub fn create_tokio_runtime() -> Runtime {