diff --git a/CHANGELOG-Japanese.md b/CHANGELOG-Japanese.md index 88617cce..1df746a5 100644 --- a/CHANGELOG-Japanese.md +++ b/CHANGELOG-Japanese.md @@ -2,10 +2,18 @@ ## v1.3.1 [2022/xx/xx] +**新機能:** + +- ルール内の`details`で複数の`Data`レコードから特定のデータを指定して出力できるようにした。 (#487) (@hitenkoku) + **改善:** - LinuxとmacOSのバイナリサイズをより小さくするために、デバッグシンボルをストリップします。(#568) (@YamatoSecurity) +**バグ修正:** + +- 対応するオプションを付与していないときにもRecordIDとRecordInformationの列が出力されていたのを修正した。 (#577) (@hitenkoku) + ## v1.3.0 [2022/06/06] **新機能:** diff --git a/CHANGELOG.md b/CHANGELOG.md index 82736a6e..cce74669 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,18 @@ ## v1.3.1 [2022/xx/xx] +**New Features:** + +- You can now specify specific fields when there are multiple fields with the same name (Ex: `Data`). In the `details` line in a rule, specify a placeholder like `%Data[1]%` to display the first `Data` field. (#487) (@hitenkoku) + **Enhancements:** - Strip debug symbols by default for smaller Linux and macOS binaries. (#568) (@YamatoSecurity) +**Bug Fixes:** + +- fixed bug that RecordID and RecordInformation column is showed when options is not enabled. (#577) (@hitenkoku) + ## v1.3.0 [2022/06/06] **New Features:** diff --git a/Cargo.lock b/Cargo.lock index 42cf7f6b..a37bde5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -568,6 +568,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.0.1" @@ -699,6 +714,7 @@ dependencies = [ "linked-hash-map", "lock_api", "num_cpus", + "openssl", "pbr", "prettytable-rs", "quick-xml 0.23.0", @@ -1103,12 +1119,47 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" +[[package]] +name = "openssl" +version = "0.10.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-src" +version = "111.20.0+1.1.1o" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92892c4f87d56e376e469ace79f1128fdaded07646ddf73aa0be4706ff712dec" +dependencies = [ + "cc", +] + [[package]] name = "openssl-sys" version = "0.9.74" @@ -1118,6 +1169,7 @@ dependencies = [ "autocfg", "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] @@ -1801,9 +1853,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" dependencies = [ "cfg-if", "pin-project-lite", diff --git a/README-Japanese.md b/README-Japanese.md index 6395da26..545ba66e 100644 --- a/README-Japanese.md +++ b/README-Japanese.md @@ -576,8 +576,8 @@ Hayabusaの結果は`level`毎に文字色が変わります。 ## イベント頻度タイムライン -`--visualize-timeline`オプションを使うことで、検知したイベントの数が5以上の時、頻度のタイムライン(スパークライン)を画面に出力します。 -マーカーの数は最大10個です。デフォルトのCommand PromptとPowerShell Promptでは文字化けされるので、Windows TerminalやiTerm2等のターミナルをご利用ください。 +`-V`または`--visualize-timeline`オプションを使うことで、検知したイベントの数が5以上の時、頻度のタイムライン(スパークライン)を画面に出力します。 +マーカーの数は最大10個です。デフォルトのCommand PromptとPowerShell Promptでは文字化けがでるので、Windows TerminalやiTerm2等のターミナルをご利用ください。 ## 最多検知日の出力 diff --git a/src/afterfact.rs b/src/afterfact.rs index 32c40b35..86c5317b 100644 --- a/src/afterfact.rs +++ b/src/afterfact.rs @@ -414,7 +414,23 @@ enum ColPos { fn _get_serialized_disp_output(dispformat: Option) -> String { if dispformat.is_none() { - return "Timestamp|Computer|Channel|EventID|Level|RuleTitle|Details|RecordID|RecordInformation\n".to_string(); + let mut titles = vec![ + "Timestamp", + "Computer", + "Channel", + "EventID", + "Level", + "RuleTitle", + "Details", + ]; + let arg_match = &configs::CONFIG.read().unwrap().args; + if arg_match.is_present("display-record-id") { + titles.push("RecordID"); + } + if arg_match.is_present("full-data") { + titles.push("RecordInformation"); + } + return format!("{}\n", titles.join("|")); } let mut disp_serializer = csv::WriterBuilder::new() .double_quote(false) @@ -734,8 +750,7 @@ mod tests { 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|RecordID|RecordInformation\n"; + let expect_header = "Timestamp|Computer|Channel|EventID|Level|RuleTitle|Details\n"; let expect_tz = test_timestamp.with_timezone(&Local); let expect_no_header = expect_tz diff --git a/src/detections/print.rs b/src/detections/print.rs index 5d5d6b43..5680ab63 100644 --- a/src/detections/print.rs +++ b/src/detections/print.rs @@ -42,7 +42,8 @@ pub struct AlertMessage {} lazy_static! { pub static ref MESSAGES: Mutex = Mutex::new(Message::new()); - pub static ref ALIASREGEX: Regex = Regex::new(r"%[a-zA-Z0-9-_]+%").unwrap(); + pub static ref ALIASREGEX: Regex = Regex::new(r"%[a-zA-Z0-9-_\[\]]+%").unwrap(); + pub static ref SUFFIXREGEX: Regex = Regex::new(r"\[([0-9]+)\]").unwrap(); pub static ref ERROR_LOG_PATH: String = format!( "./logs/errorlog-{}.log", Local::now().format("%Y%m%d_%H%M%S") @@ -171,6 +172,18 @@ impl Message { tmp_event_record = record; } } + let suffix_match = SUFFIXREGEX.captures(&target_str); + let suffix: i64 = match suffix_match { + Some(cap) => cap.get(1).map_or(-1, |a| a.as_str().parse().unwrap_or(-1)), + None => -1, + }; + if suffix >= 1 { + tmp_event_record = tmp_event_record + .get("Data") + .unwrap() + .get((suffix - 1) as usize) + .unwrap_or(tmp_event_record); + } let hash_value = get_serde_number_to_string(tmp_event_record); if hash_value.is_some() { if let Some(hash_value) = hash_value { @@ -541,6 +554,105 @@ mod tests { ); } #[test] + /// output test when no exist info in target record output and described key-value data in eventkey_alias.txt + fn test_parse_message_multiple_no_suffix_in_record() { + let mut message = Message::new(); + let json_str = r##" + { + "Event": { + "EventData": { + "CommandLine": "parsetest3", + "Data": [ + "data1", + "data2", + "data3" + ] + }, + "System": { + "TimeCreated_attributes": { + "SystemTime": "1996-02-27T01:05:01Z" + } + } + } + } + "##; + let event_record: Value = serde_json::from_str(json_str).unwrap(); + let expected = "commandline:parsetest3 data:[\"data1\",\"data2\",\"data3\"]"; + assert_eq!( + message.parse_message( + &event_record, + "commandline:%CommandLine% data:%Data%".to_owned() + ), + expected, + ); + } + #[test] + /// output test when no exist info in target record output and described key-value data in eventkey_alias.txt + fn test_parse_message_multiple_with_suffix_in_record() { + let mut message = Message::new(); + let json_str = r##" + { + "Event": { + "EventData": { + "CommandLine": "parsetest3", + "Data": [ + "data1", + "data2", + "data3" + ] + }, + "System": { + "TimeCreated_attributes": { + "SystemTime": "1996-02-27T01:05:01Z" + } + } + } + } + "##; + let event_record: Value = serde_json::from_str(json_str).unwrap(); + let expected = "commandline:parsetest3 data:data2"; + assert_eq!( + message.parse_message( + &event_record, + "commandline:%CommandLine% data:%Data[2]%".to_owned() + ), + expected, + ); + } + #[test] + /// output test when no exist info in target record output and described key-value data in eventkey_alias.txt + fn test_parse_message_multiple_no_exist_in_record() { + let mut message = Message::new(); + let json_str = r##" + { + "Event": { + "EventData": { + "CommandLine": "parsetest3", + "Data": [ + "data1", + "data2", + "data3" + ] + }, + "System": { + "TimeCreated_attributes": { + "SystemTime": "1996-02-27T01:05:01Z" + } + } + } + } + "##; + let event_record: Value = serde_json::from_str(json_str).unwrap(); + let expected = "commandline:parsetest3 data:n/a"; + assert_eq!( + message.parse_message( + &event_record, + "commandline:%CommandLine% data:%Data[0]%".to_owned() + ), + expected, + ); + } + #[test] /// test of loading output filter config by output_tag.txt fn test_load_output_tag() { let actual =