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:
+33
-12
@@ -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,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
@@ -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()),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user