display EventRecordID (#549)
* added -R --display-record-id #548 * fixed test data #548 * cargo fmt * added describe of -R --display-record-id option to README #548 * updated changelog #548 * readme update Co-authored-by: Tanaka Zakku <71482215+YamatoSecurity@users.noreply.github.com>
This commit is contained in:
@@ -6,11 +6,11 @@
|
||||
|
||||
- 検知されたイベントが5つ以上の時、イベント頻度のタイムラインを作成するようにした。 (#533) (@hitenkoku)
|
||||
- `--all-tags`オプションでルールにある全てのtagsを、outputで指定したcsvのMitreAttackの列に出力するようにした。 (#525) (@hitenkoku)
|
||||
- `-R` / `--display-record-id` オプションの追加。evtx file内のレコードを特定するレコードID`<Event><System><EventRecordID>`が出力できるようになった。 (#548) (@hitenkoku)
|
||||
|
||||
**改善:**
|
||||
|
||||
- ルールの`details`でeventkey_alias.txtやEvent.EventData内に存在しない情報を`n/a` (not available)と表記するようにした。(#528) (@hitenkoku)
|
||||
- ルールの`details`でeventkey_alias.txtやEvent.EventData内に存在しない情報を`n/a` (not available)と表記するようにした。 (#528) (@hitenkoku)
|
||||
- 読み込んだイベント数と検知しなかったイベント数を表示するようにした。 (#538) (@hitenkoku)
|
||||
- 新しいロゴ。 (@YamatoSecurity)
|
||||
- evtxファイルのファイルサイズの合計を出力するようにした。(#540) (@hitenkoku)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
- Added Event Frequency Timeline feature to visualize the number of events. (Note: There needs to be more than 5 events.) (#533)(@hitenkoku)
|
||||
- Display all the `tags` defined in a rule to the `MitreAttack` column when saving to CSV file with the `--all-tags` option. (#525) (@hitenkoku)
|
||||
- Added the `-R / --display-record-id` option: Display the event record ID (`<Event><System><EventRecordID>`). (#548) (@hitenkoku)
|
||||
|
||||
**Enhancements:**
|
||||
|
||||
|
||||
@@ -327,6 +327,7 @@ USAGE:
|
||||
-C --config=[RULECONFIGDIRECTORY] 'ルールフォルダのコンフィグディレクトリ(デフォルト: ./rules/config)'
|
||||
-o --output=[CSV_TIMELINE] 'タイムラインをCSV形式で保存する。(例: results.csv)'
|
||||
--all-tags '出力したCSVファイルにルール内のタグ情報を全て出力する。'
|
||||
-R --display-record-id 'EventRecordIDを出力する。'
|
||||
-v --verbose '詳細な情報を出力する。'
|
||||
-D --enable-deprecated-rules 'Deprecatedルールを有効にする。'
|
||||
-n --enable-noisy-rules 'Noisyルールを有効にする。'
|
||||
@@ -500,7 +501,8 @@ CSVファイルとして保存する場合、以下の列が追加されます:
|
||||
* `Rule Path`: アラートまたはイベントを生成した検知ルールへのパス。
|
||||
* `File Path`: アラートまたはイベントを起こしたevtxファイルへのパス。
|
||||
|
||||
`-F`もしくは`--full-data`オプションを指定した場合、全てのフィールド情報が新しいカラムで出力されます。
|
||||
`-R`もしくは`--display-record-id`オプションを指定した場合、`<Event><System><EventRecordID>`の情報が`RecordID`カラムに出力されます。
|
||||
`-F`もしくは`--full-data`オプションを指定した場合、全てのフィールド情報が`RecordInformation`カラムにで出力されます。
|
||||
|
||||
## MITRE ATT&CK戦術の省略
|
||||
|
||||
|
||||
@@ -325,6 +325,7 @@ USAGE:
|
||||
-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.'
|
||||
-R --display-record-id 'Display EventRecordID.'
|
||||
-v --verbose 'Output verbose information.'
|
||||
-D --enable-deprecated-rules 'Enable rules marked as deprecated.'
|
||||
-n --enable-noisy-rules 'Enable rules marked as noisy.'
|
||||
@@ -499,7 +500,8 @@ The following additional columns will be added to the output when saving to a CS
|
||||
* `Rule Path`: The path to the detection rule that generated the alert or event.
|
||||
* `File Path`: The path to the evtx file that caused the alert or event.
|
||||
|
||||
If you add the `-F` or `--full-data` option, a new column with all field information will also be added.
|
||||
If you add the `-R` or `--display-record-id` option, a `RecordId` column with event record ID values taken from `<Event><System><EventRecordID>` will also be added.
|
||||
If you add the `-F` or `--full-data` option, a `RecordInformation` column with all field information will also be added.
|
||||
|
||||
## MITRE ATT&CK Tactics Abbreviations
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ pub struct CsvFormat<'a> {
|
||||
rule_title: &'a str,
|
||||
details: &'a str,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
record_i_d: Option<&'a str>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
record_information: Option<&'a str>,
|
||||
rule_path: &'a str,
|
||||
file_path: &'a str,
|
||||
@@ -47,6 +49,8 @@ pub struct DisplayFormat<'a> {
|
||||
pub rule_title: &'a str,
|
||||
pub details: &'a str,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
record_i_d: Option<&'a str>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub record_information: Option<&'a str>,
|
||||
}
|
||||
|
||||
@@ -222,6 +226,10 @@ fn emit_csv<W: std::io::Write>(
|
||||
level = "info".to_string();
|
||||
}
|
||||
if displayflag {
|
||||
let record_id = detect_info
|
||||
.record_id
|
||||
.as_ref()
|
||||
.map(|recinfo| _format_cellpos(recinfo, ColPos::Other));
|
||||
let recinfo = detect_info
|
||||
.record_information
|
||||
.as_ref()
|
||||
@@ -241,6 +249,7 @@ fn emit_csv<W: std::io::Write>(
|
||||
rule_title: &_format_cellpos(&detect_info.alert, ColPos::Other),
|
||||
details: &_format_cellpos(&details, ColPos::Other),
|
||||
record_information: recinfo.as_deref(),
|
||||
record_i_d: record_id.as_deref(),
|
||||
};
|
||||
|
||||
disp_wtr_buf
|
||||
@@ -269,6 +278,7 @@ fn emit_csv<W: std::io::Write>(
|
||||
record_information: detect_info.record_information.as_deref(),
|
||||
file_path: &detect_info.filepath,
|
||||
rule_path: &detect_info.rulepath,
|
||||
record_i_d: detect_info.record_id.as_deref(),
|
||||
})?;
|
||||
}
|
||||
let level_suffix = *configs::LEVELMAP
|
||||
@@ -466,6 +476,7 @@ mod tests {
|
||||
let output = "pokepoke";
|
||||
let test_attack = "execution/txxxx.yyy";
|
||||
let test_recinfo = "record_infoinfo11";
|
||||
let test_record_id = "11111";
|
||||
{
|
||||
let mut messages = print::MESSAGES.lock().unwrap();
|
||||
messages.clear();
|
||||
@@ -501,6 +512,7 @@ mod tests {
|
||||
detail: String::default(),
|
||||
tag_info: test_attack.to_string(),
|
||||
record_information: Option::Some(test_recinfo.to_string()),
|
||||
record_id: Option::Some(test_record_id.to_string()),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -509,7 +521,7 @@ mod tests {
|
||||
.unwrap();
|
||||
let expect_tz = expect_time.with_timezone(&Local);
|
||||
let expect =
|
||||
"Timestamp,Computer,Channel,EventID,Level,MitreAttack,RuleTitle,Details,RecordInformation,RulePath,FilePath\n"
|
||||
"Timestamp,Computer,Channel,EventID,Level,MitreAttack,RuleTitle,Details,RecordID,RecordInformation,RulePath,FilePath\n"
|
||||
.to_string()
|
||||
+ &expect_tz
|
||||
.clone()
|
||||
@@ -530,6 +542,8 @@ mod tests {
|
||||
+ ","
|
||||
+ output
|
||||
+ ","
|
||||
+ test_record_id
|
||||
+ ","
|
||||
+ test_recinfo
|
||||
+ ","
|
||||
+ test_rulepath
|
||||
@@ -556,12 +570,13 @@ mod tests {
|
||||
let test_channel = "Sysmon";
|
||||
let output = "displaytest";
|
||||
let test_recinfo = "testinfo";
|
||||
let test_recid = "22222";
|
||||
|
||||
let test_timestamp = Utc
|
||||
.datetime_from_str("1996-02-27T01:05:01Z", "%Y-%m-%dT%H:%M:%SZ")
|
||||
.unwrap();
|
||||
let expect_header =
|
||||
"Timestamp|Computer|Channel|EventID|Level|RuleTitle|Details|RecordInformation\n";
|
||||
"Timestamp|Computer|Channel|EventID|Level|RuleTitle|Details|RecordID|RecordInformation\n";
|
||||
let expect_tz = test_timestamp.with_timezone(&Local);
|
||||
|
||||
let expect_no_header = expect_tz
|
||||
@@ -581,6 +596,8 @@ mod tests {
|
||||
+ "|"
|
||||
+ output
|
||||
+ "|"
|
||||
+ test_recid
|
||||
+ "|"
|
||||
+ test_recinfo
|
||||
+ "\n";
|
||||
let expect_with_header = expect_header.to_string() + &expect_no_header;
|
||||
@@ -595,6 +612,7 @@ mod tests {
|
||||
rule_title: test_title,
|
||||
details: output,
|
||||
record_information: Some(test_recinfo),
|
||||
record_i_d: Some(test_recid),
|
||||
},
|
||||
true
|
||||
),
|
||||
@@ -611,6 +629,7 @@ mod tests {
|
||||
rule_title: test_title,
|
||||
details: output,
|
||||
record_information: Some(test_recinfo),
|
||||
record_i_d: Some(test_recid),
|
||||
},
|
||||
false
|
||||
),
|
||||
|
||||
@@ -77,6 +77,7 @@ fn build_app<'a>() -> ArgMatches<'a> {
|
||||
-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.'
|
||||
-R --display-record-id 'Display EventRecordID.'
|
||||
-v --verbose 'Output verbose information.'
|
||||
-D --enable-deprecated-rules 'Enable rules marked as deprecated.'
|
||||
-n --enable-noisy-rules 'Enable rules marked as noisy.'
|
||||
|
||||
@@ -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::{CH_CONFIG, TAGS_CONFIG};
|
||||
use crate::detections::print::{CH_CONFIG, IS_DISPLAY_RECORD_ID, TAGS_CONFIG};
|
||||
use crate::detections::rule;
|
||||
use crate::detections::rule::AggResult;
|
||||
use crate::detections::rule::RuleNode;
|
||||
@@ -231,6 +231,14 @@ impl Detection {
|
||||
.record_information
|
||||
.as_ref()
|
||||
.map(|recinfo| recinfo.to_string());
|
||||
let rec_id = if *IS_DISPLAY_RECORD_ID {
|
||||
Some(
|
||||
get_serde_number_to_string(&record_info.record["Event"]["System"]["EventRecordID"])
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let detect_info = DetectInfo {
|
||||
filepath: record_info.evtx_filepath.to_string(),
|
||||
rulepath: rule.rulepath.to_string(),
|
||||
@@ -251,6 +259,7 @@ impl Detection {
|
||||
detail: String::default(),
|
||||
tag_info: tag_info.join(" | "),
|
||||
record_information: recinfo,
|
||||
record_id: rec_id,
|
||||
};
|
||||
MESSAGES.lock().unwrap().insert(
|
||||
&record_info.record,
|
||||
@@ -274,6 +283,11 @@ impl Detection {
|
||||
} else {
|
||||
Option::None
|
||||
};
|
||||
let rec_id = if *IS_DISPLAY_RECORD_ID {
|
||||
Some(String::default())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let detect_info = DetectInfo {
|
||||
filepath: "-".to_owned(),
|
||||
rulepath: rule.rulepath.to_owned(),
|
||||
@@ -285,6 +299,7 @@ impl Detection {
|
||||
detail: output,
|
||||
record_information: rec_info,
|
||||
tag_info: tag_info.join(" : "),
|
||||
record_id: rec_id,
|
||||
};
|
||||
|
||||
MESSAGES
|
||||
|
||||
@@ -33,6 +33,7 @@ pub struct DetectInfo {
|
||||
pub detail: String,
|
||||
pub tag_info: String,
|
||||
pub record_information: Option<String>,
|
||||
pub record_id: Option<String>,
|
||||
}
|
||||
|
||||
pub struct AlertMessage {}
|
||||
@@ -75,6 +76,11 @@ lazy_static! {
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("pivot-keywords-list");
|
||||
pub static ref IS_DISPLAY_RECORD_ID: bool = configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("display-record-id");
|
||||
}
|
||||
|
||||
impl Default for Message {
|
||||
@@ -309,6 +315,7 @@ mod tests {
|
||||
detail: String::default(),
|
||||
tag_info: "txxx.001".to_string(),
|
||||
record_information: Option::Some("record_information1".to_string()),
|
||||
record_id: Option::Some("11111".to_string()),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -341,6 +348,7 @@ mod tests {
|
||||
detail: String::default(),
|
||||
tag_info: "txxx.002".to_string(),
|
||||
record_information: Option::Some("record_information2".to_string()),
|
||||
record_id: Option::Some("22222".to_string()),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -373,6 +381,7 @@ mod tests {
|
||||
detail: String::default(),
|
||||
tag_info: "txxx.003".to_string(),
|
||||
record_information: Option::Some("record_information3".to_string()),
|
||||
record_id: Option::Some("33333".to_string()),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -400,12 +409,13 @@ mod tests {
|
||||
detail: String::default(),
|
||||
tag_info: "txxx.004".to_string(),
|
||||
record_information: Option::Some("record_information4".to_string()),
|
||||
record_id: Option::None,
|
||||
},
|
||||
);
|
||||
|
||||
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\", 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\") }]} }";
|
||||
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\"), record_id: None }], 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\"), record_id: Some(\"11111\") }, 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\"), record_id: Some(\"22222\") }], 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\"), record_id: Some(\"33333\") }]} }";
|
||||
assert_eq!(display, expect);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user