Merge branch 'main' of https://github.com/Yamato-Security/hayabusa into feature/463-statistics-add-channel

This commit is contained in:
garigariganzy
2022-09-22 04:11:29 +09:00
19 changed files with 781 additions and 299 deletions

2
.gitignore vendored
View File

@@ -7,4 +7,6 @@ test_*
.env
/logs
*.csv
*.json
*.jsonl
hayabusa*

View File

@@ -1,16 +1,22 @@
# 変更点
## v1.6.0 [2022/XX/XX]
## v1.6.0 [2022/09/16]
**新機能:**
- XXX
- 解析結果をJSONに出力する機能(`-j, --json-timeline`)を追加した。 (#654) (@hitenkoku)
- 解析結果をJSONL形式で出力する機能 (`-J, --jsonl` )を追加した。 (#694) (@hitenkoku)
**改善:**
- 結果概要に各レベルで検知した上位5つのルールを表示するようにした。 (#667) (@hitenkoku)
- 結果概要を出力しないようにするために `--no-summary` オプションを追加した。 (#672) (@hitenkoku)
- 結果概要の表示を短縮させた。 (#675 #678) (@hitenkoku)
- channel_abbreviations.txtによるChannelフィールドのチェックを大文字小文字の区別をなくした。 (#685) (@hitenkoku)
- 出力結果の区切り文字を`|`から`‖`に変更した。 (#687) (@hitenkoku)
- 結果概要の検知数と総イベント数の数に色付けを行い見やすくした。 (#690) (@hitenkoku)
- evtxクレートを0.8.0にアップデート。(ヘッダーや日付の値が無効な場合の処理が改善された。)
- 出力プロファイルの更新。(@YamatoSecurity)
**バグ修正:**

View File

@@ -1,16 +1,22 @@
# Changes
## v1.6.0 [2022/XX/XX]
## v1.6.0 [2022/09/16]
**New Features:**
- XXX
- You can now save the timeline to JSON files with the `-j, --json` option. (#654) (@hitenkoku)
- You can now save the timeline to JSONL files with the `-J, --jsonl` option. (#694) (@hitenkoku)
**Enhancements:**
- Added top alerts to results summary. (#667) (@hitenkoku)
- Added `--no-summary` option to not display the results summary. (#672) (@hitenkoku)
- Made the results summary more compact. (#675 #678) (@hitenkoku)
- Made Channel field in channel_abbreviations.txt case-insensitive. (#685) (@hitenkoku)
- Changed pipe separator character in output from `|` to `‖`. (#687) (@hitenkoku)
- Added color to Saved alerts and events / Total events analyzed. (#690) (@hitenkoku)
- Updated evtx crate to 0.8.0. (better handling when headers or date values are invalid.)
- Updated output profiles. (@YamatoSecurity)
**Bug Fixes:**
@@ -37,6 +43,7 @@
- Customizable output of fields defined at `config/profiles.yaml` and `config/default_profile.yaml`. (#165) (@hitenkoku)
- Implemented the `null` keyword for rule detection. It is used to check if a target field exists or not. (#643) (@hitenkoku)
- Added output to JSON option (`-j` and `--json-timeline` ) (#654) (@hitenkoku)
**Enhancements:**

268
Cargo.lock generated
View File

@@ -21,36 +21,27 @@ dependencies = [
[[package]]
name = "aho-corasick"
version = "0.7.18"
version = "0.7.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
dependencies = [
"memchr",
]
[[package]]
name = "android_system_properties"
version = "0.1.4"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7ed72e1635e121ca3e79420540282af22da58be50de153d36f81ddc6b83aa9e"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.62"
version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305"
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
[[package]]
name = "arrayvec"
@@ -164,7 +155,7 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
dependencies = [
"camino",
"cargo-platform",
"semver 1.0.13",
"semver 1.0.14",
"serde",
"serde_json",
]
@@ -202,24 +193,9 @@ dependencies = [
[[package]]
name = "clap"
version = "2.34.0"
version = "3.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim 0.8.0",
"textwrap 0.11.0",
"unicode-width",
"vec_map",
]
[[package]]
name = "clap"
version = "3.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29e724a68d9319343bb3328c9cc2dfde263f4b3142ee1059a9980580171c954b"
checksum = "1ed5341b2301a26ab80be5cbdced622e80ed808483c52e45e3310a877d3b37d7"
dependencies = [
"atty",
"bitflags",
@@ -227,16 +203,16 @@ dependencies = [
"clap_lex",
"indexmap",
"once_cell",
"strsim 0.10.0",
"strsim",
"termcolor",
"textwrap 0.15.0",
"textwrap",
]
[[package]]
name = "clap_derive"
version = "3.2.17"
version = "3.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13547f7012c01ab4a0e8f8967730ada8f9fdf419e8b6c792788f39cf4e46eefa"
checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
dependencies = [
"heck",
"proc-macro-error",
@@ -395,24 +371,24 @@ dependencies = [
[[package]]
name = "dashmap"
version = "5.3.4"
version = "5.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3495912c9c1ccf2e18976439f4443f3fee0fd61f424ff99fde6a66b15ecb448f"
checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
dependencies = [
"cfg-if",
"hashbrown",
"lock_api",
"once_cell",
"parking_lot_core",
]
[[package]]
name = "dialoguer"
version = "0.9.0"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61579ada4ec0c6031cfac3f86fdba0d195a7ebeb5e36693bd53cb5999a25beeb"
checksum = "a92e7e37ecef6857fdc0c0c5d42fd5b0938e46590c2183cc92dd310a6d078eb1"
dependencies = [
"console",
"lazy_static",
"tempfile",
"zeroize",
]
@@ -577,21 +553,22 @@ dependencies = [
[[package]]
name = "evtx"
version = "0.7.3"
source = "git+https://github.com/Yamato-Security/hayabusa-evtx.git#f2689c0343d0487521b9572dc3b9e4c179bcc5c9"
version = "0.8.2"
source = "git+https://github.com/Yamato-Security/hayabusa-evtx.git?rev=95b1c6a#95b1c6a1eebe6e2dc7be896974e92e912ddb6780"
dependencies = [
"anyhow",
"bitflags",
"byteorder",
"chrono",
"clap 2.34.0",
"clap",
"crc32fast",
"dialoguer",
"encoding",
"hashbrown",
"indoc",
"jemallocator",
"log",
"quick-xml 0.23.0",
"quick-xml",
"rayon",
"rpmalloc",
"serde",
@@ -654,11 +631,10 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
dependencies = [
"matches",
"percent-encoding",
]
@@ -670,30 +646,30 @@ checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
[[package]]
name = "futures-channel"
version = "0.3.23"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bfc52cbddcfd745bf1740338492bb0bd83d76c67b445f91c5fb29fae29ecaa1"
checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050"
dependencies = [
"futures-core",
]
[[package]]
name = "futures-core"
version = "0.3.23"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2acedae88d38235936c3922476b10fced7b2b68136f5e3c03c2d5be348a1115"
checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf"
[[package]]
name = "futures-task"
version = "0.3.23"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "842fc63b931f4056a24d59de13fb1272134ce261816e063e634ad0c15cdc5306"
checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1"
[[package]]
name = "futures-util"
version = "0.3.23"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0828a5471e340229c11c77ca80017937ce3c58cb788a17e5f1c2d5c485a9577"
checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90"
dependencies = [
"futures-core",
"futures-task",
@@ -744,12 +720,12 @@ dependencies = [
[[package]]
name = "hayabusa"
version = "1.6.0-dev"
version = "1.6.0"
dependencies = [
"base64",
"bytesize",
"chrono",
"clap 3.2.17",
"clap",
"comfy-table",
"crossbeam-utils",
"csv",
@@ -773,7 +749,7 @@ dependencies = [
"openssl",
"pbr",
"prettytable-rs",
"quick-xml 0.24.0",
"quick-xml",
"rand",
"regex",
"serde",
@@ -841,9 +817,9 @@ dependencies = [
[[package]]
name = "httparse"
version = "1.7.1"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c"
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
[[package]]
name = "httpdate"
@@ -884,24 +860,24 @@ dependencies = [
[[package]]
name = "iana-time-zone"
version = "0.1.46"
version = "0.1.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad2bfd338099682614d3ee3fe0cd72e0b6a41ca6a87f6a74a3bd593c91650501"
checksum = "237a0714f28b1ee39ccec0770ccb544eb02c9ef2c82bb096230eefcffa6468b0"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"js-sys",
"once_cell",
"wasm-bindgen",
"winapi",
]
[[package]]
name = "idna"
version = "0.2.3"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
dependencies = [
"matches",
"unicode-bidi",
"unicode-normalization",
]
@@ -960,9 +936,9 @@ dependencies = [
[[package]]
name = "itertools"
version = "0.10.3"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
checksum = "d8bf247779e67a9082a4790b45e71ac7cfd1321331a5c856a74a9faebdab78d0"
dependencies = [
"either",
]
@@ -1011,9 +987,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.59"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2"
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
dependencies = [
"wasm-bindgen",
]
@@ -1027,7 +1003,7 @@ dependencies = [
"anyhow",
"atty",
"chrono",
"clap 3.2.17",
"clap",
"file-chunker",
"indicatif",
"memmap2",
@@ -1122,12 +1098,6 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "matches"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "memchr"
version = "2.5.0"
@@ -1154,9 +1124,9 @@ dependencies = [
[[package]]
name = "miniz_oxide"
version = "0.5.3"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
dependencies = [
"adler",
]
@@ -1229,6 +1199,15 @@ dependencies = [
"libc",
]
[[package]]
name = "num_threads"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
dependencies = [
"libc",
]
[[package]]
name = "number_prefix"
version = "0.4.0"
@@ -1237,9 +1216,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "once_cell"
version = "1.13.1"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
[[package]]
name = "openssl"
@@ -1339,9 +1318,9 @@ dependencies = [
[[package]]
name = "percent-encoding"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
name = "pin-project-lite"
@@ -1445,18 +1424,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quick-xml"
version = "0.23.0"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9279fbdacaad3baf559d8cabe0acc3d06e30ea14931af31af79578ac0946decc"
dependencies = [
"memchr",
]
[[package]]
name = "quick-xml"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678404d55890514fa1c01fe98cf280b674db93944fdcb70310dd3be1d0d63be7"
checksum = "11bafc859c6815fbaffbbbf4229ecb767ac913fecb27f9ad4343662e9ef099ea"
dependencies = [
"memchr",
"serde",
@@ -1494,9 +1464,9 @@ dependencies = [
[[package]]
name = "rand_core"
version = "0.6.3"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
@@ -1658,9 +1628,9 @@ dependencies = [
[[package]]
name = "semver"
version = "1.0.13"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711"
checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
dependencies = [
"serde",
]
@@ -1749,13 +1719,13 @@ dependencies = [
[[package]]
name = "simplelog"
version = "0.10.2"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85d04ae642154220ef00ee82c36fb07853c10a4f2a0ca6719f9991211d2eb959"
checksum = "48dfff04aade74dd495b007c831cd6f4e0cee19c344dd9dc0884c0289b70a786"
dependencies = [
"chrono",
"log",
"termcolor",
"time 0.3.14",
]
[[package]]
@@ -1781,9 +1751,9 @@ checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
[[package]]
name = "socket2"
version = "0.4.6"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10c98bba371b9b22a71a9414e420f92ddeb2369239af08200816169d5e2dd7aa"
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
dependencies = [
"libc",
"winapi",
@@ -1853,12 +1823,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "strsim"
version = "0.10.0"
@@ -1951,33 +1915,24 @@ dependencies = [
[[package]]
name = "textwrap"
version = "0.11.0"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "textwrap"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
[[package]]
name = "thiserror"
version = "1.0.32"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.32"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783"
dependencies = [
"proc-macro2",
"quote",
@@ -2005,11 +1960,23 @@ dependencies = [
"libc",
"standback",
"stdweb",
"time-macros",
"time-macros 0.1.1",
"version_check",
"winapi",
]
[[package]]
name = "time"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b"
dependencies = [
"itoa 1.0.3",
"libc",
"num_threads",
"time-macros 0.2.4",
]
[[package]]
name = "time-macros"
version = "0.1.1"
@@ -2020,6 +1987,12 @@ dependencies = [
"time-macros-impl",
]
[[package]]
name = "time-macros"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
[[package]]
name = "time-macros-impl"
version = "0.1.2"
@@ -2050,9 +2023,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.20.1"
version = "1.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581"
checksum = "0020c875007ad96677dcc890298f4b942882c5d4eb7cc8f439fc3bf813dc9c95"
dependencies = [
"autocfg",
"bytes",
@@ -2129,9 +2102,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]]
name = "unicode-ident"
version = "1.0.3"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
[[package]]
name = "unicode-normalization"
@@ -2144,19 +2117,18 @@ dependencies = [
[[package]]
name = "unicode-width"
version = "0.1.9"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "url"
version = "2.2.2"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
dependencies = [
"form_urlencoded",
"idna",
"matches",
"percent-encoding",
]
@@ -2166,12 +2138,6 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.4"
@@ -2213,9 +2179,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.82"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d"
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -2223,9 +2189,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.82"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f"
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
dependencies = [
"bumpalo",
"log",
@@ -2238,9 +2204,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.82"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602"
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -2248,9 +2214,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.82"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
dependencies = [
"proc-macro2",
"quote",
@@ -2261,9 +2227,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.82"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
[[package]]
name = "winapi"

View File

@@ -1,6 +1,6 @@
[package]
name = "hayabusa"
version = "1.6.0-dev"
version = "1.6.0"
authors = ["Yamato Security @SecurityYamato"]
edition = "2021"
@@ -8,8 +8,8 @@ edition = "2021"
itertools = "*"
dashmap = "*"
clap = { version = "3.*", features = ["derive", "cargo"]}
evtx = { git = "https://github.com/Yamato-Security/hayabusa-evtx.git" , features = ["fast-alloc"]}
quick-xml = {version = "0.*", features = ["serialize"] }
evtx = { git = "https://github.com/Yamato-Security/hayabusa-evtx.git" , features = ["fast-alloc"] , rev = "95b1c6a" }
quick-xml = {version = "0.23.*", features = ["serialize"] }
serde = { version = "1.*", features = ["derive"] }
serde_json = { version = "1.0"}
serde_derive = "1.*"

View File

@@ -14,8 +14,9 @@
[tag-5]: https://rust-reportcard.xuri.me/badge/github.com/Yamato-Security/hayabusa
[tag-6]: https://img.shields.io/badge/Maintenance%20Level-Actively%20Developed-brightgreen.svg
[tag-7]: https://img.shields.io/badge/Twitter-00acee?logo=twitter&logoColor=white
[tag-8]: https://img.shields.io/badge/CODE%20BLUE%20Bluebox-2022-blue
![tag-1] ![tag-2] <a href="https://github.com/Yamato-Security/hayabusa/releases">![tag-3]</a> ![tag-4]
![tag-1] ![tag-2] <a href="https://github.com/Yamato-Security/hayabusa/releases">![tag-3]</a> ![tag-4] ![tag-8]
<a href="https://rust-reportcard.xuri.me/report/github.com/Yamato-Security/hayabusa">![tag-5]</a> ![tag-6] <a href="https://twitter.com/SecurityYamato">![tag-7]</a>
@@ -68,14 +69,17 @@ Hayabusaは、日本の[Yamato Security](https://yamatosecurity.connpass.com/)
- [1. `minimal`プロファイルの出力](#1-minimalプロファイルの出力)
- [2. `standard`プロファイルの出力](#2-standardプロファイルの出力)
- [3. `verbose`プロファイルの出力](#3-verboseプロファイルの出力)
- [4. `verbose-all-field-info`プロファイルの出力](#4-verbose-all-field-infoプロファイルの出力)
- [5. `verbose-details-and-all-field-info`プロファイルの出力](#5-verbose-details-and-all-field-infoプロファイルの出力)
- [6. `timesketch`プロファイルの出力](#6-timesketchプロファイルの出力)
- [4. `all-field-info`プロファイルの出力](#4-all-field-infoプロファイルの出力)
- [5. `all-field-info-verbose`プロファイルの出力](#5-all-field-info-verboseプロファイルの出力)
- [6. `super-verbose`プロファイルの出力](#6-super-verboseプロファイルの出力)
- [7. `timesketch`プロファイルの出力](#7-timesketchプロファイルの出力)
- [8. `timesketch`プロファイルの出力](#8-timesketchプロファイルの出力)
- [プロファイルの比較](#プロファイルの比較)
- [Profile Field Aliases](#profile-field-aliases)
- [Levelの省略](#levelの省略)
- [MITRE ATT&CK戦術の省略](#mitre-attck戦術の省略)
- [Channel情報の省略](#channel情報の省略)
- [その他のの省略](#その他のの省略)
- [プログレスバー](#プログレスバー)
- [標準出力へのカラー設定](#標準出力へのカラー設定)
- [結果のサマリ](#結果のサマリ)
@@ -182,6 +186,7 @@ CSVのタイムラインをTimesketchにインポートする方法は[こちら
* 詳細な調査のために全フィールド情報の出力。
* 成功と失敗したユーザログオンの要約。
* [Velociraptor](https://docs.velociraptor.app/)と組み合わせた企業向けの広範囲なすべてのエンドポイントに対するスレットハンティングとDFIR。
* CSV、JSON、JSONLの出力。
# ダウンロード
@@ -202,7 +207,7 @@ git clone https://github.com/Yamato-Security/hayabusa.git --recursive
`git pull --recurse-submodules`コマンド、もしくは以下のコマンドで`rules`フォルダを同期し、Hayabusaの最新のルールを更新することができます:
```bash
hayabusa-1.5.1-win-x64.exe -u
hayabusa-1.6.0-win-x64.exe -u
```
アップデートが失敗した場合は、`rules`フォルダの名前を変更してから、もう一回アップデートしてみて下さい。
@@ -307,20 +312,20 @@ Windows PC起動後の初回実行時に時間がかかる場合があります
コマンドプロンプトやWindows Terminalから32ビットもしくは64ビットのWindowsバイナリをHayabusaのルートディレクトリから実行します。
例: `hayabusa-1.5.1-windows-x64.exe`
例: `hayabusa-1.6.0-windows-x64.exe`
## Linux
まず、バイナリに実行権限を与える必要があります。
```bash
chmod +x ./hayabusa-1.5.1-linux-x64-gnu
chmod +x ./hayabusa-1.6.0-linux-x64-gnu
```
次に、Hayabusaのルートディレクトリから実行します
```bash
./hayabusa-1.5.1-linux-x64-gnu
./hayabusa-1.6.0-linux-x64-gnu
```
## macOS
@@ -328,13 +333,13 @@ chmod +x ./hayabusa-1.5.1-linux-x64-gnu
まず、ターミナルやiTerm2からバイナリに実行権限を与える必要があります。
```bash
chmod +x ./hayabusa-1.5.1-mac-intel
chmod +x ./hayabusa-1.6.0-mac-intel
```
次に、Hayabusaのルートディレクトリから実行してみてください
```bash
./hayabusa-1.5.1-mac-intel
./hayabusa-1.6.0-mac-intel
```
macOSの最新版では、以下のセキュリティ警告が出る可能性があります
@@ -348,7 +353,7 @@ macOSの環境設定から「セキュリティとプライバシー」を開き
その後、ターミナルからもう一回実行してみてください:
```bash
./hayabusa-1.5.1-mac-intel
./hayabusa-1.6.0-mac-intel
```
以下の警告が出るので、「開く」をクリックしてください。
@@ -358,6 +363,7 @@ macOSの環境設定から「セキュリティとプライバシー」を開き
これで実行できるようになります。
# 使用方法
## 主なコマンド
* デフォルト: ファストフォレンジックタイムラインの作成。
@@ -387,6 +393,8 @@ ADVANCED:
--target-file-ext <EVTX_FILE_EXT>... evtx以外の拡張子を解析対象に追加する。 (例1: evtx_data 例evtx1 evtx2)
OUTPUT:
-j, --json タイムラインの出力をJSON形式で保存する例: -j -o results.json
-J, --jsonl タイムラインの出力をJSONL形式で保存する (例: -J -o results.jsonl)
-o, --output <FILE> タイムラインをCSV形式で保存する (例: results.csv)
-P, --profile <PROFILE> 利用する出力プロファイル名を指定する (minimal, standard, verbose, verbose-all-field-info, verbose-details-and-all-field-info)
@@ -407,13 +415,13 @@ FILTERING:
--timeline-start <DATE> 解析対象とするイベントログの開始時刻 (例: "2020-02-22 00:00:00 +09:00")
OTHER-ACTIONS:
--contributors コントリビュータの一覧表示
-L, --logon-summary 成功と失敗したログオン情報の要約を出力する
--contributors コントリビュータの一覧表示
-L, --logon-summary 成功と失敗したログオン情報の要約を出力する
--level-tuning [<FILE>] ルールlevelのチューニング (デフォルト: ./rules/config/level_tuning.txt)
-p, --pivot-keywords-list ピボットキーワードの一覧作成
-s, --statistics イベントIDの統計情報を表示する
--set-default-profile <PROFILE> デフォルトの出力コンフィグを設定する
-u, --update-rules rulesフォルダをhayabusa-rulesのgithubリポジトリの最新版に更新する
-p, --pivot-keywords-list ピボットキーワードの一覧作成
-s, --statistics イベントIDの統計情報を表示する
--set-default-profile <PROFILE> デフォルトの出力コンフィグを設定する
-u, --update-rules rulesフォルダをhayabusa-rulesのgithubリポジトリの最新版に更新する
TIME-FORMAT:
--European-time ヨーロッパ形式で日付と時刻を出力する (例: 22-02-2022 22:00:00.123 +02:00)
@@ -429,84 +437,90 @@ TIME-FORMAT:
* つのWindowsイベントログファイルに対してHayabusaを実行する:
```bash
hayabusa-1.5.1-win-x64.exe -f eventlog.evtx
hayabusa-1.6.0-win-x64.exe -f eventlog.evtx
```
* `verbose`プロファイルで複数のWindowsイベントログファイルのあるsample-evtxディレクトリに対して、Hayabusaを実行する:
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx -P verbose
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -P verbose
```
* 全てのフィールド情報も含めてつのCSVファイルにエクスポートして、Excel、Timeline Explorer、Elastic Stack等でさらに分析することができる(注意: `verbose-details-and-all-field-info`プロファイルを使すると、出力するファイルのサイズがとても大きくなる!):
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx -o results.csv -P `verbose-details-and-all-field-info`
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -o results.csv -P verbose-details-and-all-field-info
```
* タイムラインをJSON形式で保存する:
```bash
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -o results.json -j
```
* Hayabusaルールのみを実行するデフォルトでは`-r .\rules`にあるすべてのルールが利用される):
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa -o results.csv
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa -o results.csv
```
* Windowsでデフォルトで有効になっているログに対してのみ、Hayabusaルールを実行する:
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa\default -o results.csv
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa\default -o results.csv
```
* Sysmonログに対してのみHayabusaルールを実行する:
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa\sysmon -o results.csv
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa\sysmon -o results.csv
```
* Sigmaルールのみを実行する:
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\sigma -o results.csv
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\sigma -o results.csv
```
* 廃棄(deprecated)されたルール(`status``deprecated`になっているルール)とノイジールール(`.\rules\config\noisy_rules.txt`にルールIDが書かれているルール)を有効にする:
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx --enable-deprecated-rules --enable-noisy-rules -o results.csv
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx --enable-deprecated-rules --enable-noisy-rules -o results.csv
```
* ログオン情報を分析するルールのみを実行し、UTCタイムゾーンで出力する:
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa\default\events\Security\Logons -U -o results.csv
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa\default\events\Security\Logons -U -o results.csv
```
* 起動中のWindows端末上で実行しAdministrator権限が必要、アラート悪意のある可能性のある動作のみを検知する:
```bash
hayabusa-1.5.1-win-x64.exe -l -m low
hayabusa-1.6.0-win-x64.exe -l -m low
```
* criticalレベルのアラートからピボットキーワードの一覧を作成する(結果は結果毎に`keywords-Ip Address.txt``keywords-Users.txt`等に出力される):
```bash
hayabusa-1.5.1-win-x64.exe -l -m critical -p -o keywords
hayabusa-1.6.0-win-x64.exe -l -m critical -p -o keywords
```
* イベントIDの統計情報を出力する:
```bash
hayabusa-1.5.1-win-x64.exe -f Security.evtx -s
hayabusa-1.6.0-win-x64.exe -f Security.evtx -s
```
* ログオンサマリを出力する:
```bash
hayabusa-1.5.1-win-x64.exe -L -f Security.evtx -s
hayabusa-1.6.0-win-x64.exe -L -f Security.evtx -s
```
* 詳細なメッセージを出力する(処理に時間がかかるファイル、パースエラー等を特定するのに便利):
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx -v
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -v
```
* Verbose出力の例:
@@ -527,7 +541,7 @@ Checking target evtx FilePath: "./hayabusa-sample-evtx/YamatoSecurity/T1218.004_
* 結果を[Timesketch](https://timesketch.org/)にインポートできるCSV形式に保存する:
```bash
hayabusa-1.5.1-win-x64.exe -d ../hayabusa-sample-evtx --RFC-3339 -o timesketch-import.csv -P timesketch -U
hayabusa-1.6.0-win-x64.exe -d ../hayabusa-sample-evtx --RFC-3339 -o timesketch-import.csv -P timesketch -U
```
* エラーログの出力をさせないようにする:
@@ -574,8 +588,11 @@ Hayabusaの`config/profiles.yaml`設定ファイルでは、5つのプロフ
1. `minimal`
2. `standard` (デフォルト)
3. `verbose`
4. `verbose-all-field-info`
5. `verbose-details-and-all-field-info`
4. `all-field-info`
5. `all-field-info-verbose`
6. `super-verbose`
7. `timesketch-minimal`
8. `timesketch-verbose`
このファイルを編集することで、簡単に独自のプロファイルをカスタマイズしたり、追加したりすることができます。
`--set-default-profile <profile>`オプションでデフォルトのプロファイルを変更することもできます。
@@ -586,31 +603,43 @@ Hayabusaの`config/profiles.yaml`設定ファイルでは、5つのプロフ
### 2. `standard`プロファイルの出力
`%Timestamp%`, `%Computer%`, `%Channel%`, `%EventID%`, `%Level%`, `%MitreTactics%`, `%RecordID%`, `%RuleTitle%`, `%Details%`
`%Timestamp%`, `%Computer%`, `%Channel%`, `%EventID%`, `%Level%`, `%RecordID%`, `%RuleTitle%`, `%Details%`
### 3. `verbose`プロファイルの出力
`%Timestamp%`, `%Computer%`, `%Channel%`, `%EventID%`, `%Level%`, `%MitreTactics`, `%MitreTags%`, `%OtherTags%`, `%RecordID%`, `%RuleTitle%`, `%Details%`, `%RuleFile%`, `%EvtxFile%`
### 4. `verbose-all-field-info`プロファイルの出力
### 4. `all-field-info`プロファイルの出力
最小限の`details`情報を出力する代わりに、イベントにあるすべての`EventData`フィールド情報が出力されます。
`%Timestamp%`, `%Computer%`, `%Channel%`, `%EventID%`, `%Level%`, `%RecordID%`, `%RuleTitle%`, `%AllFieldInfo%`, `%RuleFile%`, `%EvtxFile%`
### 5. `all-field-info-verbose`プロファイルの出力
`all-field-info`とタグ情報が出力されます。
`%Timestamp%`, `%Computer%`, `%Channel%`, `%EventID%`, `%Level%`, `%MitreTactics`, `%MitreTags%`, `%OtherTags%`, `%RecordID%`, `%RuleTitle%`, `%AllFieldInfo%`, `%RuleFile%`, `%EvtxFile%`
### 5. `verbose-details-and-all-field-info`プロファイルの出力
### 6. `super-verbose`プロファイルの出力
`verbose`プロファイルで出力される情報とイベントにあるすべての`EventData`フィールド情報が出力されます。
(注意: 出力ファイルサイズは2倍になります)
`%Timestamp%`, `%Computer%`, `%Channel%`, `%EventID%`, `%Level%`, `%MitreTactics`, `%MitreTags%`, `%OtherTags%`, `%RecordID%`, `%RuleTitle%`, `%Details%`, `%RuleFile%`, `%EvtxFile%`, `%AllFieldInfo%`
### 6. `timesketch`プロファイルの出力
### 7. `timesketch`プロファイルの出力
[Timesketch](https://timesketch.org/)にインポートできる`verbose`プロファイル。
`%Timestamp%`, `hayabusa`, `%RuleTitle%`, `%Computer%`, `%Channel%`, `%EventID%`, `%Level%`, `%MitreTactics`, `%MitreTags%`, `%OtherTags%`, `%RecordID%`, `%Details%`, `%RuleFile%`, `%EvtxFile%`
### 8. `timesketch`プロファイルの出力
[Timesketch](https://timesketch.org/)にインポートできる`verbose`プロファイル。
`%Timestamp%`, `hayabusa`, `%RuleTitle%`, `%Computer%`, `%Channel%`, `%EventID%`, `%Level%`, `%MitreTactics`, `%MitreTags%`, `%OtherTags%`, `%RecordID%`, `%Details%`, `%RuleFile%`, `%EvtxFile%`, `%AllFieldInfo%`
### プロファイルの比較
以下のベンチマークは、2018年製のマックブックプロ上で7.5GBのEVTXデータに対して実施されました。
@@ -620,9 +649,9 @@ Hayabusaの`config/profiles.yaml`設定ファイルでは、5つのプロフ
| minimal | 16分18秒 | 690 MB |
| standard | 16分23秒 | 710 MB |
| verbose | 17分 | 990 MB |
| timesketch | 17分 | 1015 MB |
| verbose-all-field-info | 16分50秒 | 1.6 GB |
| verbose-details-and-all-field-info | 17分12秒 | 2.1 GB |
| timesketch-minimal | 17分 | 1015 MB |
| all-field-info-verbose | 16分50秒 | 1.6 GB |
| super-verbose | 17分12秒 | 2.1 GB |
### Profile Field Aliases
@@ -710,6 +739,39 @@ Hayabusaの`config/profiles.yaml`設定ファイルでは、5つのプロフ
* `WinRM` : `Microsoft-Windows-WinRM/Operational`
* `WMI` : `Microsoft-Windows-WMI-Activity/Operational`
# その他のの省略
できるだけ簡潔にするために、以下の略語を使用しています:
- `Acct` -> Account
- `Addr` -> Address
- `Auth` -> Authentication
- `Cli` -> Client
- `Cmd` -> Command
- `Comp` -> Computer
- `Conn` -> Connection
- `Dir` -> Directory
- `Dst` -> Destination
- `Exec` -> Execution
- `Grp` -> Group
- `LID` -> Logon ID
- `Net` -> Network
- `Obj` -> Object
- `Proto` -> Protocol
- `Sig` -> Signature
- `Susp` -> Suspicious
- `Src` -> Source
- `Svc` -> Service
- `Svr` -> Server
- `Tgt` -> Target
- `Op` -> Operation
- `Pkg` -> Package
- `Priv` -> Privilege
- `Proc` -> Process
- `PID` -> Process ID
- `PGUID` -> Process GUID (Global Unique ID)
- `Ver` -> Version
## プログレスバー
プログレス・バーは、複数のevtxファイルに対してのみ機能します。
@@ -791,7 +853,7 @@ Hayabusaルールは、Windowsのイベントログ解析専用に設計され
## 検知レベルのlevelチューニング
Hayabusaルール、Sigmaルールはそれぞれの作者が検知した際のリスクレベルを決めています。
ユーザが独自のリスクレベルに設定するには`./rules/config/level_tuning.txt`に変換情報を書き、`hayabusa-1.5.1-win-x64.exe --level-tuning`を実行することでルールファイルが書き換えられます。
ユーザが独自のリスクレベルに設定するには`./rules/config/level_tuning.txt`に変換情報を書き、`hayabusa-1.6.0-win-x64.exe --level-tuning`を実行することでルールファイルが書き換えられます。
ルールファイルが直接書き換えられることに注意して使用してください。
`./rules/config/level_tuning.txt`の例:
@@ -824,7 +886,7 @@ id,new_level
* [EvtxToElk](https://www.dragos.com/blog/industry-news/evtxtoelk-a-python-module-to-load-windows-event-logs-into-elasticsearch/) - Elastic StackにEvtxデータを送信するPythonツール。
* [EVTX ATTACK Samples](https://github.com/sbousseaden/EVTX-ATTACK-SAMPLES) - [SBousseaden](https://twitter.com/SBousseaden) によるEVTX攻撃サンプルイベントログファイル。
* [EVTX-to-MITRE-Attack](https://github.com/mdecrevoisier/EVTX-to-MITRE-Attack) - [Michel de CREVOISIER](https://twitter.com/mdecrevoisier)によるATT&CKにマッピングされたEVTX攻撃サンプルログのレポジトリ。
* [EVTX parser](https://github.com/omerbenamram/evtx) - [@OBenamram](https://twitter.com/obenamram) によって書かれた、私たちが使用しRustライブラリ。
* [EVTX parser](https://github.com/omerbenamram/evtx) - [@OBenamram](https://twitter.com/obenamram) によって書かれた、Hayabusaが使用しているRustライブラリ。
* [Grafiki](https://github.com/lucky-luk3/Grafiki) - SysmonとPowerShellログの可視化ツール。
* [LogonTracer](https://github.com/JPCERTCC/LogonTracer) - [JPCERTCC](https://twitter.com/jpcert) による、横方向の動きを検知するためにログオンを視覚化するグラフィカルなインターフェース。
* [RustyBlue](https://github.com/Yamato-Security/RustyBlue) - 大和セキュリティによるDeepBlueCLIのRust版。
@@ -850,12 +912,15 @@ Windows機での悪性な活動を検知する為には、デフォルトのロ
フォレンジックに有用な証拠を作り、高い精度で検知をさせるためには、sysmonをインストールする必要があります。以下のサイトを参考に設定することをおすすめします。:
* [Sysmon Modular](https://github.com/olafhartong/sysmon-modular)
* [TrustedSec Sysmon Community Guide](https://github.com/trustedsec/SysmonCommunityGuide)
* [SwiftOnSecurityのSysmon設定ファイル](https://github.com/SwiftOnSecurity/sysmon-config)
* [Neo23x0によるSwiftOnSecurityのSysmon設定ファイルのフォーク](https://github.com/Neo23x0/sysmon-config)
* [ion-stormによるSwiftOnSecurityのSysmon設定ファイルのフォーク](https://github.com/ion-storm/sysmon-config)
# コミュニティによるドキュメンテーション
## 英語
* 2022/06/19 [VelociraptorチュートリアルとHayabusaの統合方法](https://www.youtube.com/watch?v=Q1IoGX--814) by [Eric Cupuano](https://twitter.com/eric_capuano)
* 2022/06/19 [VelociraptorチュートリアルとHayabusaの統合方法](https://www.youtube.com/watch?v=Q1IoGX--814) by [Eric Capuano](https://twitter.com/eric_capuano)
* 2022/01/24 [Hayabusa結果をneo4jで可視化する方法](https://www.youtube.com/watch?v=7sQqz2ek-ko) by Matthew Seyer ([@forensic_matt](https://twitter.com/forensic_matt))
## 日本語

146
README.md
View File

@@ -14,8 +14,9 @@
[tag-5]: https://rust-reportcard.xuri.me/badge/github.com/Yamato-Security/hayabusa
[tag-6]: https://img.shields.io/badge/Maintenance%20Level-Actively%20Developed-brightgreen.svg
[tag-7]: https://img.shields.io/badge/Twitter-00acee?logo=twitter&logoColor=white
[tag-8]: https://img.shields.io/badge/CODE%20BLUE%20Bluebox-2022-blue
![tag-1] ![tag-2] <a href="https://github.com/Yamato-Security/hayabusa/releases">![tag-3]</a> ![tag-4]
![tag-1] ![tag-2] <a href="https://github.com/Yamato-Security/hayabusa/releases">![tag-3]</a> ![tag-4] ![tag-8]
<a href="https://rust-reportcard.xuri.me/report/github.com/Yamato-Security/hayabusa">![tag-5]</a> ![tag-6] <a href="https://twitter.com/SecurityYamato">![tag-7]</a>
# About Hayabusa
@@ -66,14 +67,17 @@ Hayabusa is a **Windows event log fast forensics timeline generator** and **thre
- [1. `minimal` profile output](#1-minimal-profile-output)
- [2. `standard` profile output](#2-standard-profile-output)
- [3. `verbose` profile output](#3-verbose-profile-output)
- [4. `verbose-all-field-info` profile output](#4-verbose-all-field-info-profile-output)
- [5. `verbose-details-and-all-field-info` profile output](#5-verbose-details-and-all-field-info-profile-output)
- [6. `timesketch` profile output](#6-timesketch-profile-output)
- [4. `all-field-info` profile output](#4-all-field-info-profile-output)
- [5. `all-field-info-verbose` profile output](#5-all-field-info-verbose-profile-output)
- [6. `super-verbose` profile output](#6-super-verbose-profile-output)
- [7. `timesketch-minimal` profile output](#7-timesketch-minimal-profile-output)
- [8. `timesketch-verbose` profile output](#8-timesketch-verbose-profile-output)
- [Profile Comparison](#profile-comparison)
- [Profile Field Aliases](#profile-field-aliases)
- [Level Abbrevations](#level-abbrevations)
- [MITRE ATT&CK Tactics Abbreviations](#mitre-attck-tactics-abbreviations)
- [Channel Abbreviations](#channel-abbreviations)
- [Other Abbreviations](#other-abbreviations)
- [Progress Bar](#progress-bar)
- [Color Output](#color-output)
- [Results Summary](#results-summary-1)
@@ -168,12 +172,13 @@ You can learn how to import CSV files into Timesketch [here](doc/TimesketchImpor
* Currently it supports the most sigma rules compared to other similar tools and even supports count rules and new aggregators such as `|equalsfield`.
* Event log statistics. (Useful for getting a picture of what types of events there are and for tuning your log settings.)
* Rule tuning configuration by excluding unneeded or noisy rules.
* MITRE ATT&CK mapping of tactics (only in saved CSV files).
* MITRE ATT&CK mapping of tactics.
* Rule level tuning.
* Create a list of unique pivot keywords to quickly identify abnormal users, hostnames, processes, etc... as well as correlate events.
* Output all fields for more thorough investigations.
* Successful and failed logon summary.
* Enterprise-wide threat hunting and DFIR on all endpoints with [Velociraptor](https://docs.velociraptor.app/).
* Output to CSV, JSON or JSONL.
# Downloads
@@ -194,7 +199,7 @@ Note: If you forget to use --recursive option, the `rules` folder, which is mana
You can sync the `rules` folder and get latest Hayabusa rules with `git pull --recurse-submodules` or use the following command:
```bash
hayabusa-1.5.1-win-x64.exe -u
hayabusa-1.6.0-win-x64.exe -u
```
If the update fails, you may need to rename the `rules` folder and try again.
@@ -299,20 +304,20 @@ You may experience slow runtime especially on the first run after a reboot due t
In a Command/PowerShell Prompt or Windows Terminal, just run the appropriate 32-bit or 64-bit Windows binary.
Example: `hayabusa-1.5.1-windows-x64.exe`
Example: `hayabusa-1.6.0-windows-x64.exe`
## Linux
You first need to make the binary executable.
```bash
chmod +x ./hayabusa-1.5.1-linux-x64-gnu
chmod +x ./hayabusa-1.6.0-linux-x64-gnu
```
Then run it from the Hayabusa root directory:
```bash
./hayabusa-1.5.1-linux-x64-gnu
./hayabusa-1.6.0-linux-x64-gnu
```
## macOS
@@ -320,13 +325,13 @@ Then run it from the Hayabusa root directory:
From Terminal or iTerm2, you first need to make the binary executable.
```bash
chmod +x ./hayabusa-1.5.1-mac-intel
chmod +x ./hayabusa-1.6.0-mac-intel
```
Then, try to run it from the Hayabusa root directory:
```bash
./hayabusa-1.5.1-mac-intel
./hayabusa-1.6.0-mac-intel
```
On the latest version of macOS, you may receive the following security error when you try to run it:
@@ -340,7 +345,7 @@ Click "Cancel" and then from System Preferences, open "Security & Privacy" and f
After that, try to run it again.
```bash
./hayabusa-1.5.1-mac-intel
./hayabusa-1.6.0-mac-intel
```
The following warning will pop up, so please click "Open".
@@ -379,6 +384,8 @@ ADVANCED:
--target-file-ext <EVTX_FILE_EXT>... Specify additional target file extensions (ex: evtx_data) (ex: evtx1 evtx2)
OUTPUT:
-j, --json Save the timeline in JSON format (ex: -j -o results.json)
-J, --jsonl Save the timeline in JSONL format (ex: -J -o results.jsonl)
-o, --output <FILE> Save the timeline in CSV format (ex: results.csv)
-P, --profile <PROFILE> Specify output profile (minimal, standard, verbose, verbose-all-field-info, verbose-details-and-all-field-info)
@@ -421,85 +428,91 @@ TIME-FORMAT:
* Run hayabusa against one Windows event log file with default standard profile:
```bash
hayabusa-1.5.1-win-x64.exe -f eventlog.evtx
hayabusa-1.6.0-win-x64.exe -f eventlog.evtx
```
* Run hayabusa against the sample-evtx directory with multiple Windows event log files with the verbose profile:
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx -P verbose
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -P verbose
```
* Export to a single CSV file for further analysis with excel, timeline explorer, elastic stack, etc... and include all field information (Warning: your file output size will become much larger with the `verbose-details-and-all-field-info` profile!):
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx -o results.csv -F
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -o results.csv -P verbose-details-and-all-field-info
```
* Save the timline in JSON format:
```bash
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -o results.json -j
```
* Only run hayabusa rules (the default is to run all the rules in `-r .\rules`):
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa -o results.csv
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa -o results.csv
```
* Only run hayabusa rules for logs that are enabled by default on Windows:
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa\default -o results.csv
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa\default -o results.csv
```
* Only run hayabusa rules for sysmon logs:
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa\sysmon -o results.csv
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa\sysmon -o results.csv
```
* Only run sigma rules:
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\sigma -o results.csv
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\sigma -o results.csv
```
* Enable deprecated rules (those with `status` marked as `deprecated`) and noisy rules (those whose rule ID is listed in `.\rules\config\noisy_rules.txt`):
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx --enable-noisy-rules --enable-deprecated-rules -o results.csv
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx --enable-noisy-rules --enable-deprecated-rules -o results.csv
```
* Only run rules to analyze logons and output in the UTC timezone:
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa\default\events\Security\Logons -U -o results.csv
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa\default\events\Security\Logons -U -o results.csv
```
* Run on a live Windows machine (requires Administrator privileges) and only detect alerts (potentially malicious behavior):
```bash
hayabusa-1.5.1-win-x64.exe -l -m low
hayabusa-1.6.0-win-x64.exe -l -m low
```
* Create a list of pivot keywords from critical alerts and save the results. (Results will be saved to `keywords-Ip Addresses.txt`, `keywords-Users.txt`, etc...):
```bash
hayabusa-1.5.1-win-x64.exe -l -m critical -p -o keywords
hayabusa-1.6.0-win-x64.exe -l -m critical -p -o keywords
```
* Print Event ID statistics:
```bash
hayabusa-1.5.1-win-x64.exe -f Security.evtx -s
hayabusa-1.6.0-win-x64.exe -f Security.evtx -s
```
* Print logon summary:
```bash
hayabusa-1.5.1-win-x64.exe -L -f Security.evtx -s
hayabusa-1.6.0-win-x64.exe -L -f Security.evtx -s
```
* Print verbose information (useful for determining which files take long to process, parsing errors, etc...):
```bash
hayabusa-1.5.1-win-x64.exe -d .\hayabusa-sample-evtx -v
hayabusa-1.6.0-win-x64.exe -d .\hayabusa-sample-evtx -v
```
* Verbose output example:
@@ -520,7 +533,7 @@ Checking target evtx FilePath: "./hayabusa-sample-evtx/YamatoSecurity/T1218.004_
* Output to a CSV format compatible to import into [Timesketch](https://timesketch.org/):
```bash
hayabusa-1.5.1-win-x64.exe -d ../hayabusa-sample-evtx --RFC-3339 -o timesketch-import.csv -P timesketch -U
hayabusa-1.6.0-win-x64.exe -d ../hayabusa-sample-evtx --RFC-3339 -o timesketch-import.csv -P timesketch -U
```
* Quiet error mode:
@@ -569,8 +582,11 @@ Hayabusa has 5 pre-defined profiles to use in `config/profiles.yaml`:
1. `minimal`
2. `standard` (default)
3. `verbose`
4. `verbose-all-field-info`
5. `verbose-details-and-all-field-info`
4. `all-field-info`
5. `all-field-info-verbose`
6. `super-verbose`
7. `timesketch-minimal`
8. `timesketch-verbose`
You can easily customize or add your own profiles by editing this file.
You can also easily change the default profile with `--set-default-profile <profile>`.
@@ -581,30 +597,42 @@ You can also easily change the default profile with `--set-default-profile <prof
### 2. `standard` profile output
`%Timestamp%`, `%Computer%`, `%Channel%`, `%EventID%`, `%Level%`, `%MitreTactics%`, `%RecordID%`, `%RuleTitle%`, `%Details%`
`%Timestamp%`, `%Computer%`, `%Channel%`, `%EventID%`, `%Level%`, `%RecordID%`, `%RuleTitle%`, `%Details%`
### 3. `verbose` profile output
`%Timestamp%`, `%Computer%`, `%Channel%`, `%EventID%`, `%Level%`, `%MitreTactics`, `%MitreTags%`, `%OtherTags%`, `%RecordID%`, `%RuleTitle%`, `%Details%`, `%RuleFile%`, `%EvtxFile%`
### 4. `verbose-all-field-info` profile output
### 4. `all-field-info` profile output
Instead of outputting the minimal `details` information, all field information in the `EventData` section will be outputted.
`%Timestamp%`, `%Computer%`, `%Channel%`, `%EventID%`, `%Level%`, `%RecordID%`, `%RuleTitle%`, `%AllFieldInfo%`, `%RuleFile%`, `%EvtxFile%`
### 5. `all-field-info-verbose` profile output
`all-field-info` profile plus tag information.
`%Timestamp%`, `%Computer%`, `%Channel%`, `%EventID%`, `%Level%`, `%MitreTactics`, `%MitreTags%`, `%OtherTags%`, `%RecordID%`, `%RuleTitle%`, `%AllFieldInfo%`, `%RuleFile%`, `%EvtxFile%`
### 5. `verbose-details-and-all-field-info` profile output
### 6. `super-verbose` profile output
`verbose` profile plus all field information. (Warning: this will usually double the output file size!)
`%Timestamp%`, `%Computer%`, `%Channel%`, `%EventID%`, `%Level%`, `%MitreTactics`, `%MitreTags%`, `%OtherTags%`, `%RecordID%`, `%RuleTitle%`, `%Details%`, `%RuleFile%`, `%EvtxFile%`, `%AllFieldInfo%`
### 6. `timesketch` profile output
### 7. `timesketch-minimal` profile output
The `verbose` profile that is compatible with importing into [Timesketch](https://timesketch.org/).
`%Timestamp%`, `hayabusa`, `%RuleTitle%`, `%Computer%`, `%Channel%`, `%EventID%`, `%Level%`, `%MitreTactics`, `%MitreTags%`, `%OtherTags%`, `%RecordID%`, `%Details%`, `%RuleFile%`, `%EvtxFile%`
### 8. `timesketch-verbose` profile output
The `super-verbose` profile that is compatible with importing into [Timesketch](https://timesketch.org/).
`%Timestamp%`, `hayabusa`, `%RuleTitle%`, `%Computer%`, `%Channel%`, `%EventID%`, `%Level%`, `%MitreTactics`, `%MitreTags%`, `%OtherTags%`, `%RecordID%`, `%Details%`, `%RuleFile%`, `%EvtxFile%`, `%AllFieldInfo%`
### Profile Comparison
The following benchmarks were conducted on a 2018 MBP with 7.5GB of evtx data.
@@ -614,9 +642,9 @@ The following benchmarks were conducted on a 2018 MBP with 7.5GB of evtx data.
| minimal | 16 minutes 18 seconds | 690 MB |
| standard | 16 minutes 23 seconds | 710 MB |
| verbose | 17 minutes | 990 MB |
| timesketch | 17 minutes | 1015 MB |
| verbose-all-field-info | 16 minutes 50 seconds | 1.6 GB |
| verbose-details-and-all-field-info | 17 minutes 12 seconds | 2.1 GB |
| timesketch-minimal | 17 minutes | 1015 MB |
| all-field-info-verbose | 16 minutes 50 seconds | 1.6 GB |
| super-verbose | 17 minutes 12 seconds | 2.1 GB |
### Profile Field Aliases
@@ -704,6 +732,39 @@ You can freely edit these abbreviations in the `./rules/config/channel_abbreviat
* `WinRM` : `Microsoft-Windows-WinRM/Operational`
* `WMI` : `Microsoft-Windows-WMI-Activity/Operational`
# Other Abbreviations
The following abbreviations are used in rules in order to make the output as concise as possible:
- `Acct` -> Account
- `Addr` -> Address
- `Auth` -> Authentication
- `Cli` -> Client
- `Cmd` -> Command
- `Comp` -> Computer
- `Conn` -> Connection
- `Dir` -> Directory
- `Dst` -> Destination
- `Exec` -> Execution
- `Grp` -> Group
- `LID` -> Logon ID
- `Net` -> Network
- `Obj` -> Object
- `Proto` -> Protocol
- `Sig` -> Signature
- `Susp` -> Suspicious
- `Src` -> Source
- `Svc` -> Service
- `Svr` -> Server
- `Tgt` -> Target
- `Op` -> Operation
- `Pkg` -> Package
- `Priv` -> Privilege
- `Proc` -> Process
- `PID` -> Process ID
- `PGUID` -> Process GUID (Global Unique ID)
- `Ver` -> Version
## Progress Bar
The progress bar will only work with multiple evtx files.
@@ -784,7 +845,7 @@ You can also add a rule ID to `./rules/config/noisy_rules.txt` in order to ignor
Hayabusa and Sigma rule authors will determine the risk level of the alert when writing their rules.
However, the actual risk level will differ between environments.
You can tune the risk level of the rules by adding them to `./rules/config/level_tuning.txt` and executing `hayabusa-1.5.1-win-x64.exe --level-tuning` which will update the `level` line in the rule file.
You can tune the risk level of the rules by adding them to `./rules/config/level_tuning.txt` and executing `hayabusa-1.6.0-win-x64.exe --level-tuning` which will update the `level` line in the rule file.
Please note that the rule file will be updated directly.
`./rules/config/level_tuning.txt` sample line:
@@ -818,7 +879,7 @@ There is no "one tool to rule them all" and we have found that each has its own
* [EvtxToElk](https://www.dragos.com/blog/industry-news/evtxtoelk-a-python-module-to-load-windows-event-logs-into-elasticsearch/) - Python tool to send Evtx data to Elastic Stack.
* [EVTX ATTACK Samples](https://github.com/sbousseaden/EVTX-ATTACK-SAMPLES) - EVTX attack sample event log files by [SBousseaden](https://twitter.com/SBousseaden).
* [EVTX-to-MITRE-Attack](https://github.com/mdecrevoisier/EVTX-to-MITRE-Attack) - EVTX attack sample event log files mapped to ATT&CK by [Michel de CREVOISIER](https://twitter.com/mdecrevoisier)
* [EVTX parser](https://github.com/omerbenamram/evtx) - the Rust library we used written by [@OBenamram](https://twitter.com/obenamram).
* [EVTX parser](https://github.com/omerbenamram/evtx) - the Rust evtx library we use written by [@OBenamram](https://twitter.com/obenamram).
* [Grafiki](https://github.com/lucky-luk3/Grafiki) - Sysmon and PowerShell log visualizer.
* [LogonTracer](https://github.com/JPCERTCC/LogonTracer) - A graphical interface to visualize logons to detect lateral movement by [JPCERTCC](https://twitter.com/jpcert_en).
* [RustyBlue](https://github.com/Yamato-Security/RustyBlue) - Rust port of DeepBlueCLI by Yamato Security.
@@ -840,15 +901,18 @@ In order to properly detect malicious activity on Windows machines, you will nee
# Sysmon Related Projects
To create the most forensic evidence and detect with the highest accuracy, you need to install sysmon. We recommend the following sites:
* [Sysmon Modular](https://github.com/olafhartong/sysmon-modular)
To create the most forensic evidence and detect with the highest accuracy, you need to install sysmon. We recommend the following sites and config files:
* [TrustedSec Sysmon Community Guide](https://github.com/trustedsec/SysmonCommunityGuide)
* [Sysmon Modular](https://github.com/olafhartong/sysmon-modular)
* [SwiftOnSecurity Sysmon Config](https://github.com/SwiftOnSecurity/sysmon-config)
* [SwiftOnSecurity Sysmon Config fork by Neo23x0](https://github.com/Neo23x0/sysmon-config)
* [SwiftOnSecurity Sysmon Config fork by ion-storm](https://github.com/ion-storm/sysmon-config)
# Community Documentation
## English
* 2022/06/19 [Velociraptor Walkthrough and Hayabusa Integration](https://www.youtube.com/watch?v=Q1IoGX--814) by [Eric Cupuano](https://twitter.com/eric_capuano)
* 2022/06/19 [Velociraptor Walkthrough and Hayabusa Integration](https://www.youtube.com/watch?v=Q1IoGX--814) by [Eric Capuano](https://twitter.com/eric_capuano)
* 2022/01/24 [Graphing Hayabusa results in neo4j](https://www.youtube.com/watch?v=7sQqz2ek-ko) by Matthew Seyer ([@forensic_matt](https://twitter.com/forensic_matt))
## Japanese

View File

@@ -4,7 +4,6 @@ Computer: "%Computer%"
Channel: "%Channel%"
EventID: "%EventID%"
Level: "%Level%"
MitreTactics: "%MitreTactics%"
RecordID: "%RecordID%"
RuleTitle: "%RuleTitle%"
Details: "%Details%"

View File

@@ -14,7 +14,6 @@ standard:
Channel: "%Channel%"
EventID: "%EventID%"
Level: "%Level%"
MitreTactics: "%MitreTactics%"
RecordID: "%RecordID%"
RuleTitle: "%RuleTitle%"
Details: "%Details%"
@@ -35,8 +34,21 @@ verbose:
RuleFile: "%RuleFile%"
EvtxFile: "%EvtxFile%"
#Verbose profile with all field information instead of the minimal fields defined in Details.
verbose-all-field-info:
#Verbose profile with all field information instead of the minimal fields defined in the Details field.
all-field-info:
Timestamp: "%Timestamp%"
Computer: "%Computer%"
Channel: "%Channel%"
EventID: "%EventID%"
Level: "%Level%"
RecordID: "%RecordID%"
RuleTitle: "%RuleTitle%"
AllFieldInfo: "%RecordInformation%"
RuleFile: "%RuleFile%"
EvtxFile: "%EvtxFile%"
#Verbose profile with all field information and tags.
all-field-info-verbose:
Timestamp: "%Timestamp%"
Computer: "%Computer%"
Channel: "%Channel%"
@@ -52,7 +64,7 @@ verbose-all-field-info:
EvtxFile: "%EvtxFile%"
#Verbose profile plus all field information. (Warning: this will more than double the output file size!)
verbose-details-and-all-field-info:
super-verbose:
Timestamp: "%Timestamp%"
Computer: "%Computer%"
Channel: "%Channel%"
@@ -69,7 +81,24 @@ verbose-details-and-all-field-info:
AllFieldInfo: "%RecordInformation%"
#Output that is compatible to import the CSV into Timesketch
timesketch:
timesketch-minimal:
datetime: "%Timestamp%"
timestamp_desc: "hayabusa"
message: "%RuleTitle%"
Computer: "%Computer%"
Channel: "%Channel%"
EventID: "%EventID%"
Level: "%Level%"
MitreTactics: "%MitreTactics%"
MitreTags: "%MitreTags%"
OtherTags: "%OtherTags%"
RecordID: "%RecordID%"
Details: "%Details%"
RuleFile: "%RuleFile%"
EvtxFile: "%EvtxFile%"
#Output that is compatible to import the CSV into Timesketch
timesketch-verbose:
datetime: "%Timestamp%"
timestamp_desc: "hayabusa"
message: "%RuleTitle%"

2
rules

Submodule rules updated: ff5732fa17...fa75078de6

Binary file not shown.

Before

Width:  |  Height:  |  Size: 899 KiB

After

Width:  |  Height:  |  Size: 669 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 488 KiB

After

Width:  |  Height:  |  Size: 491 KiB

View File

@@ -9,11 +9,14 @@ use bytesize::ByteSize;
use chrono::{DateTime, Local, TimeZone, Utc};
use comfy_table::modifiers::UTF8_ROUND_CORNERS;
use comfy_table::presets::UTF8_FULL;
use csv::QuoteStyle;
use csv::{QuoteStyle, WriterBuilder};
use dashmap::Map;
use itertools::Itertools;
use krapslog::{build_sparkline, build_time_markers};
use lazy_static::lazy_static;
use linked_hash_map::LinkedHashMap;
use std::str::FromStr;
use comfy_table::*;
use hashbrown::{HashMap, HashSet};
@@ -189,7 +192,13 @@ pub fn after_fact(all_record_cnt: usize) {
Box::new(BufWriter::new(io::stdout()))
};
let color_map = set_output_color();
if let Err(err) = emit_csv(&mut target, displayflag, color_map, all_record_cnt as u128) {
if let Err(err) = emit_csv(
&mut target,
displayflag,
color_map,
all_record_cnt as u128,
PROFILES.clone().unwrap_or_default(),
) {
fn_emit_csv_err(Box::new(err));
}
}
@@ -199,10 +208,22 @@ fn emit_csv<W: std::io::Write>(
displayflag: bool,
color_map: HashMap<String, Colors>,
all_record_cnt: u128,
profile: LinkedHashMap<String, String>,
) -> io::Result<()> {
let disp_wtr = BufferWriter::stdout(ColorChoice::Always);
let mut disp_wtr_buf = disp_wtr.buffer();
let mut wtr = csv::WriterBuilder::new().from_writer(writer);
let json_output_flag = configs::CONFIG.read().unwrap().args.json_timeline;
let jsonl_output_flag = configs::CONFIG.read().unwrap().args.jsonl_timeline;
let mut wtr = if json_output_flag || jsonl_output_flag {
WriterBuilder::new()
.delimiter(b'\n')
.double_quote(false)
.quote_style(QuoteStyle::Never)
.from_writer(writer)
} else {
WriterBuilder::new().from_writer(writer)
};
disp_wtr_buf.set_color(ColorSpec::new().set_fg(None)).ok();
@@ -231,11 +252,20 @@ fn emit_csv<W: std::io::Write>(
let mut timestamps: Vec<i64> = Vec::new();
let mut plus_header = true;
let mut detected_record_idset: HashSet<String> = HashSet::new();
for time in message::MESSAGES.clone().into_read_only().keys().sorted() {
if json_output_flag {
wtr.write_field("[")?;
}
for (processed_message_cnt, time) in message::MESSAGES
.clone()
.into_read_only()
.keys()
.sorted()
.enumerate()
{
let multi = message::MESSAGES.get(time).unwrap();
let (_, detect_infos) = multi.pair();
timestamps.push(_get_timestamp(time));
for detect_info in detect_infos {
for (info_idx, detect_info) in detect_infos.iter().enumerate() {
if !detect_info.detail.starts_with("[condition]") {
detected_record_idset.insert(format!("{}_{}", time, detect_info.eventid));
}
@@ -245,7 +275,7 @@ fn emit_csv<W: std::io::Write>(
write_color_buffer(
&disp_wtr,
get_writable_color(None),
&_get_serialized_disp_output(PROFILES.as_ref().unwrap(), true),
&_get_serialized_disp_output(&profile, true),
false,
)
.ok();
@@ -263,6 +293,27 @@ fn emit_csv<W: std::io::Write>(
false,
)
.ok();
} else if json_output_flag {
// JSON output
wtr.write_field(" {")?;
wtr.write_field(&output_json_str(
&detect_info.ext_field,
&profile,
jsonl_output_flag,
))?;
if processed_message_cnt != message::MESSAGES._len() - 1
|| info_idx != detect_infos.len() - 1
{
wtr.write_field(" },")?;
} else {
wtr.write_field(" }")?;
}
} else if jsonl_output_flag {
// JSONL output format
wtr.write_field(format!(
"{{ {} }}",
&output_json_str(&detect_info.ext_field, &profile, jsonl_output_flag)
))?;
} else {
// csv output format
if plus_header {
@@ -332,6 +383,10 @@ fn emit_csv<W: std::io::Write>(
.insert(detect_info.level.to_lowercase(), detect_counts_by_date);
}
}
if json_output_flag {
wtr.write_field("]")?;
}
if displayflag {
println!();
} else {
@@ -385,17 +440,51 @@ fn emit_csv<W: std::io::Write>(
};
write_color_buffer(
&disp_wtr,
get_writable_color(None),
get_writable_color(Some(Color::Rgb(255, 255, 0))),
"Events with hits",
false,
)
.ok();
write_color_buffer(&disp_wtr, get_writable_color(None), " / ", false).ok();
write_color_buffer(
&disp_wtr,
get_writable_color(Some(Color::Rgb(0, 255, 255))),
"Total events",
false,
)
.ok();
write_color_buffer(&disp_wtr, get_writable_color(None), ": ", false).ok();
write_color_buffer(
&disp_wtr,
get_writable_color(Some(Color::Rgb(255, 255, 0))),
&(all_record_cnt - reducted_record_cnt).to_formatted_string(&Locale::en),
false,
)
.ok();
write_color_buffer(&disp_wtr, get_writable_color(None), " / ", false).ok();
write_color_buffer(
&disp_wtr,
get_writable_color(Some(Color::Rgb(0, 255, 255))),
&all_record_cnt.to_formatted_string(&Locale::en),
false,
)
.ok();
write_color_buffer(&disp_wtr, get_writable_color(None), " (", false).ok();
write_color_buffer(
&disp_wtr,
get_writable_color(Some(Color::Rgb(0, 255, 0))),
&format!(
"Saved alerts and events / Total events analyzed: {} / {} (Data reduction: {} events ({:.2}%))",
(all_record_cnt - reducted_record_cnt).to_formatted_string(&Locale::en),
all_record_cnt.to_formatted_string(&Locale::en),
"Data reduction: {} events ({:.2}%)",
reducted_record_cnt.to_formatted_string(&Locale::en),
reducted_percent
),
true,
false,
)
.ok();
write_color_buffer(&disp_wtr, get_writable_color(None), ")", false).ok();
println!();
println!();
_print_unique_results(
@@ -435,21 +524,27 @@ fn _get_serialized_disp_output(data: &LinkedHashMap<String, String>, header: boo
let data_length = &data.len();
let mut ret: Vec<String> = vec![];
if header {
for k in data.keys() {
ret.push(k.to_owned());
for (i, k) in data.keys().enumerate() {
if i == 0 {
ret.push(_format_cellpos(k, ColPos::First))
} else if i == data_length - 1 {
ret.push(_format_cellpos(k, ColPos::Last))
} else {
ret.push(_format_cellpos(k, ColPos::Other))
}
}
} else {
for (i, (_, v)) in data.iter().enumerate() {
if i == 0 {
ret.push(_format_cellpos(v, ColPos::First))
ret.push(_format_cellpos(v, ColPos::First).replace('|', "🦅"))
} else if i == data_length - 1 {
ret.push(_format_cellpos(v, ColPos::Last))
ret.push(_format_cellpos(v, ColPos::Last).replace('|', "🦅"))
} else {
ret.push(_format_cellpos(v, ColPos::Other))
ret.push(_format_cellpos(v, ColPos::Other).replace('|', "🦅"))
}
}
}
let mut disp_serializer = csv::WriterBuilder::new()
let mut disp_serializer = WriterBuilder::new()
.double_quote(false)
.quote_style(QuoteStyle::Never)
.delimiter(b'|')
@@ -457,7 +552,10 @@ fn _get_serialized_disp_output(data: &LinkedHashMap<String, String>, header: boo
.from_writer(vec![]);
disp_serializer.write_record(ret).ok();
String::from_utf8(disp_serializer.into_inner().unwrap_or_default()).unwrap_or_default()
String::from_utf8(disp_serializer.into_inner().unwrap_or_default())
.unwrap_or_default()
.replace('|', "")
.replace('🦅', "|")
}
/// return str position in output file
@@ -731,6 +829,246 @@ fn _get_timestamp(time: &DateTime<Utc>) -> i64 {
}
}
/// json出力の際に配列として対応させるdetails,MitreTactics,MitreTags,OtherTagsに該当する場合に配列を返す関数
fn _get_json_vec(target_alias_context: &str, target_data: &String) -> Vec<String> {
if target_alias_context.contains("%MitreTactics%")
|| target_alias_context.contains("%OtherTags%")
|| target_alias_context.contains("%MitreTags%")
{
let ret: Vec<String> = target_data
.to_owned()
.split(": ")
.map(|x| x.to_string())
.collect();
ret
} else if target_alias_context.contains("%Details%") {
let ret: Vec<String> = target_data
.to_owned()
.split(" ¦ ")
.map(|x| x.to_string())
.collect();
if target_data == &ret[0] && !target_data.contains(": ") {
vec![]
} else {
ret
}
} else {
vec![]
}
}
/// JSONの出力フォーマットに合わせた文字列を出力する関数
fn _create_json_output_format(
key: &String,
value: &str,
key_quote_exclude_flag: bool,
concat_flag: bool,
) -> String {
let head = if key_quote_exclude_flag {
key.to_string()
} else {
format!("\"{}\"", key)
};
// 4 space is json indent.
if let Ok(i) = i64::from_str(value) {
format!(" {}: {}", head, i)
} else if let Ok(b) = bool::from_str(value) {
format!(" {}: {}", head, b)
} else if concat_flag {
format!(" {}: {}", head, value)
} else {
format!(" {}: \"{}\"", head, value)
}
}
/// JSONの値に対して文字列の出力形式をJSON出力でエラーにならないようにするための変換を行う関数
fn _convert_valid_json_str(input: &[&str], concat_flag: bool) -> String {
let tmp = if input.len() == 1 {
input[0].to_string()
} else if concat_flag {
input.join(": ")
} else {
input[1..].join(": ")
};
let char_cnt = tmp.char_indices().count();
let con_val = tmp.as_str();
if char_cnt == 0 {
tmp
} else if con_val.starts_with('\"') {
let addition_header = if !con_val.starts_with('\"') { "\"" } else { "" };
let addition_quote = if !con_val.ends_with('\"') && concat_flag {
"\""
} else if !con_val.ends_with('\"') {
"\\\""
} else {
""
};
[
addition_header,
con_val
.to_string()
.replace('\\', "\\\\")
.replace('\"', "\\\"")
.trim(),
addition_quote,
]
.join("")
} else {
con_val
.replace('\\', "\\\\")
.replace('\"', "\\\"")
.trim()
.to_string()
}
}
/// JSONに出力する1検知分のオブジェクトの文字列を出力する関数
fn output_json_str(
ext_field: &LinkedHashMap<String, String>,
profile: &LinkedHashMap<String, String>,
jsonl_output_flag: bool,
) -> String {
let mut target: Vec<String> = vec![];
for (k, v) in ext_field.iter() {
let output_value_fmt = profile.get(k).unwrap();
let vec_data = _get_json_vec(output_value_fmt, v);
if vec_data.is_empty() {
let tmp_val: Vec<&str> = v.split(": ").collect();
let output_val =
_convert_valid_json_str(&tmp_val, output_value_fmt.contains("%RecordInformation%"));
target.push(_create_json_output_format(
k,
&output_val,
k.starts_with('\"'),
output_val.starts_with('\"'),
));
} else if output_value_fmt.contains("%Details%") {
let mut stocked_value = vec![];
let mut key_index_stock = vec![];
for detail_contents in vec_data.iter() {
// 分解してキーとなりえる箇所を抽出する
let space_split: Vec<&str> = detail_contents.split(' ').collect();
let mut tmp_stock = vec![];
for sp in space_split.iter() {
if sp.ends_with(':') && sp != &":" {
stocked_value.push(tmp_stock);
tmp_stock = vec![];
key_index_stock.push(sp.replace(':', "").to_owned());
} else {
tmp_stock.push(sp.to_owned());
}
}
stocked_value.push(tmp_stock);
}
let mut key_idx = 0;
let mut output_value_stock = String::default();
for (value_idx, value) in stocked_value.iter().enumerate() {
let mut tmp = if key_idx >= key_index_stock.len() {
String::default()
} else if value_idx == 0 && !value.is_empty() {
k.to_string()
} else {
key_index_stock[key_idx].to_string()
};
if !output_value_stock.is_empty() {
output_value_stock.push_str(" | ");
}
output_value_stock.push_str(&value.join(" "));
//``1つまえのキーの段階で以降にvalueの配列で区切りとなる空の配列が存在しているかを確認する
let is_remain_split_stock = if key_idx == key_index_stock.len() - 2
&& value_idx < stocked_value.len() - 1
&& !output_value_stock.is_empty()
{
let mut ret = true;
for remain_value in stocked_value[value_idx + 1..].iter() {
if remain_value.is_empty() {
ret = false;
break;
}
}
ret
} else {
false
};
if (value_idx < stocked_value.len() - 1 && stocked_value[value_idx + 1].is_empty())
|| is_remain_split_stock
{
// 次の要素を確認して、存在しないもしくは、キーが入っているとなった場合現在ストックしている内容が出力していいことが確定するので出力処理を行う
let output_tmp = format!("{}: {}", tmp, output_value_stock);
let output: Vec<&str> = output_tmp.split(": ").collect();
let key = _convert_valid_json_str(&[output[0]], false);
let fmted_val = _convert_valid_json_str(&output, false);
target.push(_create_json_output_format(
&key,
&fmted_val,
key.starts_with('\"'),
fmted_val.starts_with('\"'),
));
output_value_stock.clear();
tmp = String::default();
key_idx += 1;
}
if value_idx == stocked_value.len() - 1 {
let output_tmp = format!("{}: {}", tmp, output_value_stock);
let output: Vec<&str> = output_tmp.split(": ").collect();
let key = _convert_valid_json_str(&[output[0]], false);
let fmted_val = _convert_valid_json_str(&output, false);
target.push(_create_json_output_format(
&key,
&fmted_val,
key.starts_with('\"'),
fmted_val.starts_with('\"'),
));
key_idx += 1;
}
}
} else if output_value_fmt.contains("%MitreTags%")
|| output_value_fmt.contains("%MitreTactics%")
|| output_value_fmt.contains("%OtherTags%")
{
let tmp_val: Vec<&str> = v.split(": ").collect();
let key = _convert_valid_json_str(&[k.as_str()], false);
let values: Vec<&&str> = tmp_val.iter().filter(|x| x.trim() != "").collect();
let mut value: Vec<String> = vec![];
if values.is_empty() {
continue;
}
for (idx, tag_val) in values.iter().enumerate() {
if idx == 0 {
value.push("[\n".to_string());
}
let insert_val = format!(" \"{}\"", tag_val.trim());
value.push(insert_val);
if idx != values.len() - 1 {
value.push(",\n".to_string());
}
}
value.push("\n ]".to_string());
let fmted_val = if jsonl_output_flag {
value.iter().map(|x| x.replace('\n', "")).join("")
} else {
value.join("")
};
target.push(_create_json_output_format(
&key,
&fmted_val,
key.starts_with('\"'),
true,
));
}
}
if jsonl_output_flag {
// JSONL output
target.into_iter().map(|x| x.replace(" ", "")).join(",")
} else {
// JSON format output
target.join(",\n")
}
}
#[cfg(test)]
mod tests {
use crate::afterfact::_get_serialized_disp_output;
@@ -795,7 +1133,7 @@ mod tests {
(
"%Channel%".to_owned(),
mock_ch_filter
.get("Security")
.get(&"Security".to_ascii_lowercase())
.unwrap_or(&String::default())
.to_string(),
),
@@ -820,7 +1158,7 @@ mod tests {
eventid: test_eventid.to_string(),
detail: String::default(),
record_information: Option::Some(test_recinfo.to_string()),
ext_field: output_profile,
ext_field: output_profile.clone(),
},
expect_time,
&mut profile_converter,
@@ -860,7 +1198,7 @@ mod tests {
+ test_attack
+ "\n";
let mut file: Box<dyn io::Write> = Box::new(File::create("./test_emit_csv.csv").unwrap());
assert!(emit_csv(&mut file, false, HashMap::new(), 1).is_ok());
assert!(emit_csv(&mut file, false, HashMap::new(), 1, output_profile).is_ok());
match read_to_string("./test_emit_csv.csv") {
Err(_) => panic!("Failed to open file."),
Ok(s) => {
@@ -884,28 +1222,28 @@ 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|RecordID|RuleTitle|Details|RecordInformation\n";
let expect_header = "TimestampComputerChannelEventIDLevelRecordIDRuleTitleDetailsRecordInformation\n";
let expect_tz = test_timestamp.with_timezone(&Local);
let expect_no_header = expect_tz
.clone()
.format("%Y-%m-%d %H:%M:%S%.3f %:z")
.to_string()
+ " | "
+ " "
+ test_computername
+ " | "
+ " "
+ test_channel
+ " | "
+ " "
+ test_eventid
+ " | "
+ " "
+ test_level
+ " | "
+ " "
+ test_recid
+ " | "
+ " "
+ test_title
+ " | "
+ " "
+ output
+ " | "
+ " "
+ test_recinfo
+ "\n";
let mut data: LinkedHashMap<String, String> = LinkedHashMap::new();

View File

@@ -237,6 +237,14 @@ pub struct Config {
#[clap(help_heading = Some("OTHER-ACTIONS"), long = "set-default-profile", value_name = "PROFILE")]
pub set_default_profile: Option<String>,
/// Save the timeline in JSON format (ex: -j -o results.json)
#[clap(help_heading = Some("OUTPUT"), short = 'j', long = "json", requires = "output")]
pub json_timeline: bool,
/// Save the timeline in JSONL format (ex: -J -o results.jsonl)
#[clap(help_heading = Some("OUTPUT"), short = 'J', long = "jsonl", requires = "output")]
pub jsonl_timeline: bool,
/// Do not display result summary
#[clap(help_heading = Some("DISPLAY-SETTINGS"), long = "no-summary")]
pub no_summary: bool,

View File

@@ -264,7 +264,10 @@ impl Detection {
"%Channel%" => {
profile_converter.insert(
"%Channel%".to_string(),
CH_CONFIG.get(ch_str).unwrap_or(ch_str).to_string(),
CH_CONFIG
.get(&ch_str.to_ascii_lowercase())
.unwrap_or(ch_str)
.to_string(),
);
}
"%Level%" => {
@@ -323,7 +326,7 @@ impl Detection {
.filter(|x| TAGS_CONFIG.values().contains(x))
.map(|y| y.to_owned())
.collect();
profile_converter.insert("%MitreTactics%".to_string(), tactics.join(" : "));
profile_converter.insert("%MitreTactics%".to_string(), tactics.join(" ¦ "));
}
"%MitreTags%" => {
let techniques: &Vec<String> = &tag_info
@@ -339,7 +342,7 @@ impl Detection {
make_ascii_titlecase(&mut replaced_tag)
})
.collect();
profile_converter.insert("%MitreTags%".to_string(), techniques.join(" : "));
profile_converter.insert("%MitreTags%".to_string(), techniques.join(" ¦ "));
}
"%OtherTags%" => {
let tags: &Vec<String> = &tag_info
@@ -352,7 +355,7 @@ impl Detection {
})
.map(|y| y.to_owned())
.collect();
profile_converter.insert("%OtherTags%".to_string(), tags.join(" : "));
profile_converter.insert("%OtherTags%".to_string(), tags.join(" ¦ "));
}
_ => {}
@@ -455,7 +458,7 @@ impl Detection {
.filter(|x| TAGS_CONFIG.values().contains(x))
.map(|y| y.to_owned())
.collect();
profile_converter.insert("%MitreTactics%".to_string(), tactics.join(" : "));
profile_converter.insert("%MitreTactics%".to_string(), tactics.join(" ¦ "));
}
"%MitreTags%" => {
let techniques: &Vec<String> = &tag_info
@@ -471,7 +474,7 @@ impl Detection {
make_ascii_titlecase(&mut replaced_tag)
})
.collect();
profile_converter.insert("%MitreTags%".to_string(), techniques.join(" : "));
profile_converter.insert("%MitreTags%".to_string(), techniques.join(" ¦ "));
}
"%OtherTags%" => {
let tags: &Vec<String> = &tag_info
@@ -484,7 +487,7 @@ impl Detection {
})
.map(|y| y.to_owned())
.collect();
profile_converter.insert("%OtherTags%".to_string(), tags.join(" : "));
profile_converter.insert("%OtherTags%".to_string(), tags.join(" ¦ "));
}
_ => {}
}

View File

@@ -105,10 +105,10 @@ pub fn create_output_filter_config(path: &str) -> HashMap<String, String> {
return;
}
let tag_full_str = line[0].trim();
let tag_full_str = line[0].trim().to_ascii_lowercase();
let tag_replace_str = line[1].trim();
ret.insert(tag_full_str.to_owned(), tag_replace_str.to_owned());
ret.insert(tag_full_str, tag_replace_str.to_owned());
});
ret
}
@@ -599,7 +599,7 @@ mod tests {
let actual = create_output_filter_config("test_files/config/channel_abbreviations.txt");
let actual2 = create_output_filter_config("test_files/config/channel_abbreviations.txt");
let expected: HashMap<String, String> = HashMap::from([
("Security".to_string(), "Sec".to_string()),
("Security".to_ascii_lowercase(), "Sec".to_string()),
("xxx".to_string(), "yyy".to_string()),
]);
_check_hashmap_element(&expected, actual);

View File

@@ -297,15 +297,10 @@ fn create_recordinfos(record: &Value) -> String {
let summary: Vec<String> = output
.iter()
.map(|(key, value)| format!("{}:{}", key, value))
.map(|(key, value)| format!("{}: {}", key, value))
.collect();
// 標準出力する時はセルがハイプ区切りになるので、パイプ区切りにしない
if configs::CONFIG.read().unwrap().args.output.is_some() {
summary.join(" | ")
} else {
summary.join(" ")
}
summary.join(" ¦ ")
}
/**
@@ -510,7 +505,7 @@ mod tests {
Ok(record) => {
let ret = utils::create_recordinfos(&record);
// Systemは除外される/属性(_attributesも除外される)/key順に並ぶ
let expected = "AccessMask:%%1369 Process:lsass.exe User:u1".to_string();
let expected = "AccessMask: %%1369 ¦ Process: lsass.exe ¦ User: u1".to_string();
assert_eq!(ret, expected);
}
Err(_) => {
@@ -544,7 +539,7 @@ mod tests {
Ok(record) => {
let ret = utils::create_recordinfos(&record);
// Systemは除外される/属性(_attributesも除外される)/key順に並ぶ
let expected = "Binary:hogehoge Data: Data:Data1 Data:DataData2 Data:DataDataData3"
let expected = "Binary: hogehoge ¦ Data: ¦ Data: Data1 ¦ Data: DataData2 ¦ Data: DataDataData3"
.to_string();
assert_eq!(ret, expected);
}