Merge pull request #588 from Yamato-Security/clap_update_v3
Clap update v3
This commit is contained in:
@@ -1,5 +1,20 @@
|
||||
# 変更点
|
||||
|
||||
## v1.4 [2022/XX/XX]
|
||||
|
||||
**新機能:**
|
||||
|
||||
- XXX
|
||||
|
||||
**改善:**
|
||||
|
||||
- Clap Crateパッケージの更新 (#413) (@hitenkoku)
|
||||
- オプションの指定がないときに、`--help`と同じ画面出力を行うように変更した。(#387) (@hitenkoku)
|
||||
|
||||
**バグ修正:**
|
||||
|
||||
- XXX
|
||||
|
||||
## v1.3.2 [2022/06/13]
|
||||
|
||||
- evtxクレートを0.7.2から0.7.3に更新し、パッケージを全部更新した。 (@YamatoSecurity)
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# Changes
|
||||
|
||||
## v1.4 [2022/XX/XX]
|
||||
|
||||
**New Features:**
|
||||
|
||||
- XXX
|
||||
|
||||
**Enhancements:**
|
||||
|
||||
- Updated clap crate package to version 3. (#413) (@hitnekoku)
|
||||
- Updated the default usage and help menu. (#387) (@hitenkoku)
|
||||
|
||||
**Bug Fixes:**
|
||||
|
||||
- XXX
|
||||
|
||||
## v1.3.2 [2022/06/13]
|
||||
|
||||
**Enhancements:**
|
||||
|
||||
Generated
+93
-54
@@ -14,7 +14,7 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom 0.2.6",
|
||||
"getrandom 0.2.7",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
@@ -39,9 +39,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.57"
|
||||
version = "1.0.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
|
||||
checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
@@ -220,24 +220,39 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.1.18"
|
||||
version = "3.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
|
||||
checksum = "d53da17d37dba964b9b3ecb5c5a1f193a2762c700e6829201e645b9381c99dc7"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"once_cell",
|
||||
"strsim 0.10.0",
|
||||
"termcolor",
|
||||
"textwrap 0.15.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.0"
|
||||
name = "clap_derive"
|
||||
version = "3.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
|
||||
checksum = "c11d40217d16aee8508cc8e5fde8b4ff24639758608e5374e731b53f85749fb9"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
@@ -280,9 +295,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.4"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
|
||||
checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
@@ -301,26 +316,26 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.8"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
|
||||
checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"memoffset",
|
||||
"once_cell",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.8"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
|
||||
checksum = "8ff1f980957787286a554052d03c7aee98d99cc32e09f6d45f0a814133c87978"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"lazy_static",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -617,20 +632,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.6"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.13.25"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f29229cc1b24c0e6062f6e742aa3e256492a5323365e5ed3413599f8a5eff7d6"
|
||||
checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -647,12 +662,6 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.1"
|
||||
@@ -664,19 +673,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hayabusa"
|
||||
version = "1.3.2"
|
||||
version = "1.4.0-dev"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytesize",
|
||||
"chrono",
|
||||
"clap 2.34.0",
|
||||
"clap 3.2.5",
|
||||
"crossbeam-utils",
|
||||
"csv",
|
||||
"downcast-rs",
|
||||
"evtx",
|
||||
"flate2",
|
||||
"git2",
|
||||
"hashbrown 0.12.1",
|
||||
"hashbrown",
|
||||
"hex",
|
||||
"hhmmss",
|
||||
"hyper",
|
||||
@@ -701,6 +710,12 @@ dependencies = [
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
@@ -804,12 +819,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.8.2"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a"
|
||||
checksum = "6c6392766afd7964e2531940894cffe4bd8d7d17dbc3c1c4857040fd4b33bdb3"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown 0.11.2",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -899,7 +914,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"atty",
|
||||
"chrono",
|
||||
"clap 3.1.18",
|
||||
"clap 3.2.5",
|
||||
"file-chunker",
|
||||
"indicatif",
|
||||
"memmap2",
|
||||
@@ -925,9 +940,9 @@ checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
version = "0.12.26+1.3.0"
|
||||
version = "0.13.4+1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19e1c899248e606fbfe68dcb31d8b0176ebab833b103824af31bddf4b7457494"
|
||||
checksum = "d0fa6563431ede25f5cc7f6d803c6afbc1c5d3ad3d4925d12c882bf2b526f5d1"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -1225,6 +1240,30 @@ dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.19"
|
||||
@@ -1275,9 +1314,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.18"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
||||
checksum = "f53dc8cf16a769a6f677e09e7ff2cd4be1ea0f48754aac39520536962011de0d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -1625,9 +1664,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.96"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
|
||||
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1810,9 +1849,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
|
||||
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
@@ -1857,9 +1896,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
||||
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
@@ -1947,9 +1986,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.80"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad"
|
||||
checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -1957,9 +1996,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.80"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4"
|
||||
checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
@@ -1972,9 +2011,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.80"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5"
|
||||
checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -1982,9 +2021,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.80"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b"
|
||||
checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1995,9 +2034,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.80"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744"
|
||||
checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
|
||||
+18
-18
@@ -1,46 +1,46 @@
|
||||
[package]
|
||||
name = "hayabusa"
|
||||
version = "1.3.2"
|
||||
version = "1.4.0-dev"
|
||||
authors = ["Yamato Security @SecurityYamato"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.*", features = ["derive", "cargo"]}
|
||||
evtx = { git = "https://github.com/Yamato-Security/hayabusa-evtx.git" , rev = "158d496" , features = ["fast-alloc"]}
|
||||
quick-xml = {version = "0.23.0", features = ["serialize"] }
|
||||
serde = { version = "1.0.*", features = ["derive"] }
|
||||
quick-xml = {version = "0.*", features = ["serialize"] }
|
||||
serde = { version = "1.*", features = ["derive"] }
|
||||
serde_json = { version = "1.0"}
|
||||
serde_derive = "1.0.*"
|
||||
clap = "2.*"
|
||||
serde_derive = "1.*"
|
||||
regex = "1.5.*"
|
||||
csv = "1.1.*"
|
||||
base64 = "*"
|
||||
flate2 = "1.0.*"
|
||||
lazy_static = "1.4.0"
|
||||
chrono = "0.4.19"
|
||||
flate2 = "1.*"
|
||||
lazy_static = "1.4.*"
|
||||
chrono = "0.4.*"
|
||||
yaml-rust = "0.4.*"
|
||||
linked-hash-map = "0.5.*"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
num_cpus = "1.13.*"
|
||||
downcast-rs = "1.2.0"
|
||||
num_cpus = "1.*"
|
||||
downcast-rs = "1.*"
|
||||
hhmmss = "*"
|
||||
pbr = "*"
|
||||
hashbrown = "0.12.*"
|
||||
hex = "0.4.*"
|
||||
git2 = "0.13"
|
||||
git2 = "0.*"
|
||||
termcolor = "*"
|
||||
prettytable-rs = "0.8"
|
||||
prettytable-rs = "0.*"
|
||||
krapslog = "*"
|
||||
terminal_size = "*"
|
||||
bytesize = "1.1"
|
||||
hyper = "0.14.19"
|
||||
lock_api = "0.4.7"
|
||||
crossbeam-utils = "0.8.8"
|
||||
bytesize = "1.*"
|
||||
hyper = "0.14.*"
|
||||
lock_api = "0.4.*"
|
||||
crossbeam-utils = "0.8.*"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
is_elevated = "0.1.2"
|
||||
static_vcruntime = "2.0"
|
||||
is_elevated = "0.1.*"
|
||||
static_vcruntime = "2.*"
|
||||
|
||||
[target.'cfg(unix)'.dependencies] #Mac and Linux
|
||||
openssl = { version = "*", features = ["vendored"] } #vendored is needed to compile statically.
|
||||
|
||||
+38
-33
@@ -321,40 +321,45 @@ macOSの環境設定から「セキュリティとプライバシー」を開き
|
||||
|
||||
## コマンドラインオプション
|
||||
|
||||
```bash
|
||||
```
|
||||
USAGE:
|
||||
-d, --directory [DIRECTORY] '.evtxファイルを持つディレクトリのパス。'
|
||||
-f, --filepath [FILE_PATH] '1つの.evtxファイルのパス。'
|
||||
-F, --full-data '全てのフィールド情報を出力する。'
|
||||
-r, --rules [RULE_DIRECTORY/RULE_FILE] 'ルールファイルまたはルールファイルを持つディレクトリ。(デフォルト: .\rules)'
|
||||
-C, --config [RULE_CONFIG_DIRECTORY] 'ルールフォルダのコンフィグディレクトリ(デフォルト: .\rules\config)'
|
||||
-o, --output [CSV_TIMELINE] 'タイムラインをCSV形式で保存する。(例: results.csv)'
|
||||
--all-tags '出力したCSVファイルにルール内のタグ情報を全て出力する。'
|
||||
-R, --hide-record-id 'イベントレコードIDを表示しない。'
|
||||
-v, --verbose '詳細な情報を出力する。'
|
||||
-V, --visualize-timeline 'イベント頻度タイムラインを出力する。'
|
||||
-D, --enable-deprecated-rules 'Deprecatedルールを有効にする。'
|
||||
-n, --enable-noisy-rules 'Noisyルールを有効にする。'
|
||||
-u, --update-rules 'rulesフォルダをhayabusa-rulesのgithubリポジトリの最新版に更新する。'
|
||||
-m, --min-level [LEVEL] '結果出力をするルールの最低レベル。(デフォルト: informational)'
|
||||
-l, --live-analysis 'ローカル端末のC:\Windows\System32\winevt\Logsフォルダを解析する。(Windowsのみ。管理者権限が必要。)'
|
||||
--start-timeline [START_TIMELINE] '解析対象とするイベントログの開始時刻。(例: "2020-02-22 00:00:00 +09:00")'
|
||||
--end-timeline [END_TIMELINE] '解析対象とするイベントログの終了時刻。(例: "2022-02-22 23:59:59 +09:00")'
|
||||
--rfc-2822 'RFC 2822形式で日付と時刻を出力する。(例: Fri, 22 Feb 2022 22:00:00 -0600)'
|
||||
--rfc-3339 'RFC 3339形式で日付と時刻を出力する。 (例: 2022-02-22 22:00:00.123456-06:00)'
|
||||
--US-time 'アメリカ形式で日付と時刻を出力する。 (例: 02-22-2022 10:00:00.123 PM -06:00)'
|
||||
--US-military-time '24時間制(ミリタリータイム)のアメリカ形式で日付と時刻を出力する。 (例: 02-22-2022 22:00:00.123 -06:00)'
|
||||
--European-time 'ヨーロッパ形式で日付と時刻を出力する。 (例: 22-02-2022 22:00:00.123 +02:00)'
|
||||
-U, --utc 'UTC形式で日付と時刻を出力する。(デフォルト: 現地時間)'
|
||||
--no-color 'カラー出力を無効にする。'
|
||||
-t, --thread-number [NUMBER] 'スレッド数。(デフォルト: パフォーマンスに最適な数値)'
|
||||
-s, --statistics 'イベントIDの統計情報を表示する。'
|
||||
-L, --logon-summary '成功と失敗したログオン情報の要約を出力する。'
|
||||
-q, --quiet 'Quietモード。起動バナーを表示しない。'
|
||||
-Q, --quiet-errors 'Quiet errorsモード。エラーログを保存しない。'
|
||||
--level-tuning [LEVEL_TUNING_FILE] 'ルールlevelのチューニング (デフォルト: .\rules\config\level_tuning.txt)'
|
||||
-p, --pivot-keywords-list 'ピボットキーワードの一覧作成。'
|
||||
--contributors 'コントリビュータの一覧表示。'
|
||||
hayabusa.exe -f file.evtx [OPTIONS] / hayabusa.exe -d evtx-directory [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
--European-time ヨーロッパ形式で日付と時刻を出力する (例: 22-02-2022 22:00:00.123 +02:00)
|
||||
--RFC-2822 RFC 2822形式で日付と時刻を出力する (例: Fri, 22 Feb 2022 22:00:00 -0600)
|
||||
--RFC-3339 RFC 3339形式で日付と時刻を出力する (例: 2022-02-22 22:00:00.123456-06:00)
|
||||
--US-military-time 24時間制(ミリタリータイム)のアメリカ形式で日付と時刻を出力する (例: 02-22-2022 22:00:00.123 -06:00)
|
||||
--US-time アメリカ形式で日付と時刻を出力する (例: 02-22-2022 10:00:00.123 PM -06:00)
|
||||
--all-tags 出力したCSVファイルにルール内のタグ情報を全て出力する
|
||||
-c, --config <RULE_CONFIG_DIRECTORY> ルールフォルダのコンフィグディレクトリ (デフォルト: ./rules/config)
|
||||
--contributors コントリビュータの一覧表示
|
||||
-d, --directory <DIRECTORY> .evtxファイルを持つディレクトリのパス
|
||||
-D, --enable-deprecated-rules Deprecatedルールを有効にする
|
||||
--end-timeline <END_TIMELINE> 解析対象とするイベントログの終了時刻 (例: "2022-02-22 23:59:59 +09:00")
|
||||
-f, --filepath <FILE_PATH> 1つの.evtxファイルに対して解析を行う
|
||||
-F, --full-data 全てのフィールド情報を出力する
|
||||
-h, --help ヘルプ情報を表示する
|
||||
-l, --live-analysis ローカル端末のC:\Windows\System32\winevt\Logsフォルダを解析する
|
||||
-L, --logon-summary 成功と失敗したログオン情報の要約を出力する
|
||||
--level-tuning <LEVEL_TUNING_FILE> ルールlevelのチューニング (デフォルト: ./rules/config/level_tuning.txt)
|
||||
-m, --min-level <LEVEL> 結果出力をするルールの最低レベル (デフォルト: informational)
|
||||
-n, --enable-noisy-rules Noisyルールを有効にする
|
||||
--no_color カラー出力を無効にする
|
||||
-o, --output <CSV_TIMELINE> タイムラインをCSV形式で保存する (例: results.csv)
|
||||
-p, --pivot-keywords-list ピボットキーワードの一覧作成
|
||||
-q, --quiet Quietモード: 起動バナーを表示しない
|
||||
-Q, --quiet-errors Quiet errorsモード: エラーログを保存しない
|
||||
-r, --rules <RULE_DIRECTORY/RULE_FILE> ルールファイルまたはルールファイルを持つディレクトリ (デフォルト: ./rules)
|
||||
-R, --hide-record-id イベントレコードIDを表示しない
|
||||
-s, --statistics イベントIDの統計情報を表示する
|
||||
--start-timeline <START_TIMELINE> 解析対象とするイベントログの開始時刻 (例: "2020-02-22 00:00:00 +09:00")
|
||||
-t, --thread-number <NUMBER> スレッド数 (デフォルト: パフォーマンスに最適な数値)
|
||||
-u, --update-rules rulesフォルダをhayabusa-rulesのgithubリポジトリの最新版に更新する
|
||||
-U, --UTC UTC形式で日付と時刻を出力する (デフォルト: 現地時間)
|
||||
-v, --verbose 詳細な情報を出力する
|
||||
-V, --visualize-timeline イベント頻度タイムラインを出力する
|
||||
--version バージョン情報を表示する
|
||||
```
|
||||
|
||||
## 使用例
|
||||
|
||||
@@ -319,40 +319,45 @@ You should now be able to run hayabusa.
|
||||
|
||||
## Command Line Options
|
||||
|
||||
```bash
|
||||
```
|
||||
USAGE:
|
||||
-d, --directory [DIRECTORY] 'Directory of multiple .evtx files.'
|
||||
-f, --filepath [FILE_PATH] 'File path to one .evtx file.'
|
||||
-F, --full-data 'Print all field information.'
|
||||
-r, --rules [RULE_DIRECTORY/RULE_FILE] 'Rule file or directory (Default: .\rules)'
|
||||
-C, --config [RULE_CONFIG_DIRECTORY] 'Rule config folder. (Default: .\rules\config)'
|
||||
-o, --output [CSV_TIMELINE] 'Save the timeline in CSV format. (Ex: results.csv)'
|
||||
--all-tags 'Output all tags when saving to a CSV file.'
|
||||
-R, --hide-record-id 'Do not display the EventRecordID number.'
|
||||
-v, --verbose 'Output verbose information.'
|
||||
-V, --visualize-timeline 'Output event frequency timeline.'
|
||||
-D, --enable-deprecated-rules 'Enable rules marked as deprecated.'
|
||||
-n, --enable-noisy-rules 'Enable rules marked as noisy.'
|
||||
-u, --update-rules 'Update to the latest rules in the hayabusa-rules github repository.'
|
||||
-m, --min-level [LEVEL] 'Minimum level for rules. (Default: informational)'
|
||||
-l, --live-analysis 'Analyze the local C:\Windows\System32\winevt\Logs folder (Windows Only. Administrator privileges required.)'
|
||||
--start-timeline [START_TIMELINE] 'Start time of the event logs to load. (Ex: "2020-02-22 00:00:00 +09:00")'
|
||||
--end-timeline [END_TIMELINE] 'End time of the event logs to load. (Ex: "2022-02-22 23:59:59 +09:00")'
|
||||
--rfc-2822 'Output timestamp in RFC 2822 format. (Ex: Fri, 22 Feb 2022 22:00:00 -0600)'
|
||||
--rfc-3339 'Output timestamp in RFC 3339 format. (Ex: 2022-02-22 22:00:00.123456-06:00)'
|
||||
--US-time 'Output timestamp in US time format. (Ex: 02-22-2022 10:00:00.123 PM -06:00)'
|
||||
--US-military-time 'Output timestamp in US military time format. (Ex: 02-22-2022 22:00:00.123 -06:00)'
|
||||
--European-time 'Output timestamp in European time format. (Ex: 22-02-2022 22:00:00.123 +02:00)'
|
||||
-U, --utc 'Output time in UTC format. (Default: local time)'
|
||||
--no-color 'Disable color output.'
|
||||
-t, --thread-number [NUMBER] 'Thread number. (Default: Optimal number for performance.)'
|
||||
-s, --statistics 'Prints statistics of event IDs.'
|
||||
-L, --logon-summary 'Successful and failed logons summary.'
|
||||
-q, --quiet 'Quiet mode. Do not display the launch banner.'
|
||||
-Q, --quiet-errors 'Quiet errors mode. Do not save error logs.'
|
||||
--level-tuning [LEVEL_TUNING_FILE] 'Tune alert levels. (Default: .\rules\config\level_tuning.txt)'
|
||||
-p, --pivot-keywords-list 'Create a list of pivot keywords.'
|
||||
--contributors 'Prints the list of contributors.'
|
||||
hayabusa.exe -f file.evtx [OPTIONS] / hayabusa.exe -d evtx-directory [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
--European-time Output timestamp in European time format (ex: 22-02-2022 22:00:00.123 +02:00)
|
||||
--RFC-2822 Output timestamp in RFC 2822 format (ex: Fri, 22 Feb 2022 22:00:00 -0600)
|
||||
--RFC-3339 Output timestamp in RFC 3339 format (ex: 2022-02-22 22:00:00.123456-06:00)
|
||||
--US-military-time Output timestamp in US military time format (ex: 02-22-2022 22:00:00.123 -06:00)
|
||||
--US-time Output timestamp in US time format (ex: 02-22-2022 10:00:00.123 PM -06:00)
|
||||
--all-tags Output all tags when saving to a CSV file
|
||||
-c, --config <RULE_CONFIG_DIRECTORY> Specify custom rule config folder (default: ./rules/config)
|
||||
--contributors Print the list of contributors
|
||||
-d, --directory <DIRECTORY> Directory of multiple .evtx files
|
||||
-D, --enable-deprecated-rules Enable rules marked as deprecated
|
||||
--end-timeline <END_TIMELINE> End time of the event logs to load (ex: "2022-02-22 23:59:59 +09:00")
|
||||
-f, --filepath <FILE_PATH> File path to one .evtx file
|
||||
-F, --full-data Print all field information
|
||||
-h, --help Print help information
|
||||
-l, --live-analysis Analyze the local C:\Windows\System32\winevt\Logs folder
|
||||
-L, --logon-summary Print a summary of successful and failed logons
|
||||
--level-tuning <LEVEL_TUNING_FILE> Tune alert levels (default: ./rules/config/level_tuning.txt)
|
||||
-m, --min-level <LEVEL> Minimum level for rules (default: informational)
|
||||
-n, --enable-noisy-rules Enable rules marked as noisy
|
||||
--no-color Disable color output
|
||||
-o, --output <CSV_TIMELINE> Save the timeline in CSV format (ex: results.csv)
|
||||
-p, --pivot-keywords-list Create a list of pivot keywords
|
||||
-q, --quiet Quiet mode: do not display the launch banner
|
||||
-Q, --quiet-errors Quiet errors mode: do not save error logs
|
||||
-r, --rules <RULE_DIRECTORY/RULE_FILE> Specify a rule directory or file (default: ./rules)
|
||||
-R, --hide-record-ID Do not display EventRecordID numbers
|
||||
-s, --statistics Print statistics of event IDs
|
||||
--start-timeline <START_TIMELINE> Start time of the event logs to load (ex: "2020-02-22 00:00:00 +09:00")
|
||||
-t, --thread-number <NUMBER> Thread number (default: optimal number for performance)
|
||||
-u, --update-rules Update to the latest rules in the hayabusa-rules github repository
|
||||
-U, --UTC Output time in UTC format (default: local time)
|
||||
-v, --verbose Output verbose information
|
||||
-V, --visualize-timeline Output event frequency timeline
|
||||
--version Print version information
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
@@ -627,7 +632,7 @@ Please check out the current rules to use as a template in creating new ones or
|
||||
## Hayabusa v.s. Converted Sigma Rules
|
||||
|
||||
Sigma rules need to first be converted to hayabusa rule format explained [here](https://github.com/Yamato-Security/hayabusa-rules/blob/main/tools/sigmac/README.md).
|
||||
Most rules are compatible with the sigma format so you can use them just like sigma rules to convert to other SIEM formats.
|
||||
Almost all hayabusa rules are compatible with the sigma format so you can use them just like sigma rules to convert to other SIEM formats.
|
||||
Hayabusa rules are designed solely for Windows event log analysis and have the following benefits:
|
||||
|
||||
1. An extra `details` field to display additional information taken from only the useful fields in the log.
|
||||
@@ -739,6 +744,10 @@ At the least, if you like our tool then please give us a star on Github and show
|
||||
Please submit any bugs you find [here.](https://github.com/Yamato-Security/hayabusa/issues/new?assignees=&labels=bug&template=bug_report.md&title=%5Bbug%5D)
|
||||
This project is currently actively maintained and we are happy to fix any bugs reported.
|
||||
|
||||
If you find any issues (false positives, bugs, etc...) with Hayabusa rules, please report them to the hayabusa-rules github issues page [here](https://github.com/Yamato-Security/hayabusa-rules/issues/new).
|
||||
|
||||
If you find any issues (false positives, bugs, etc...) with Sigma rules, please report them to the upstream SigmaHQ github issues page [here](https://github.com/SigmaHQ/sigma/issues).
|
||||
|
||||
# License
|
||||
|
||||
Hayabusa is released under [GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html) and all rules are released under the [Detection Rule License (DRL) 1.1](https://github.com/SigmaHQ/sigma/blob/master/LICENSE.Detection.Rules.md).
|
||||
|
||||
+1
-1
Submodule rules updated: deb6026fcf...4d5b76a37d
+14
-19
@@ -1,4 +1,5 @@
|
||||
use crate::detections::configs;
|
||||
use crate::detections::configs::TERM_SIZE;
|
||||
use crate::detections::print;
|
||||
use crate::detections::print::{AlertMessage, IS_HIDE_RECORD_ID};
|
||||
use crate::detections::utils;
|
||||
@@ -18,7 +19,7 @@ use std::io::BufWriter;
|
||||
use std::io::Write;
|
||||
use std::process;
|
||||
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
|
||||
use terminal_size::{terminal_size, Width};
|
||||
use terminal_size::Width;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
@@ -63,7 +64,7 @@ lazy_static! {
|
||||
pub fn set_output_color() -> HashMap<String, Color> {
|
||||
let read_result = utils::read_csv("config/level_color.txt");
|
||||
let mut color_map: HashMap<String, Color> = HashMap::new();
|
||||
if configs::CONFIG.read().unwrap().args.is_present("no-color") {
|
||||
if configs::CONFIG.read().unwrap().args.no_color {
|
||||
return color_map;
|
||||
}
|
||||
if read_result.is_err() {
|
||||
@@ -166,7 +167,7 @@ pub fn after_fact(all_record_cnt: usize) {
|
||||
|
||||
let mut displayflag = false;
|
||||
let mut target: Box<dyn io::Write> =
|
||||
if let Some(csv_path) = configs::CONFIG.read().unwrap().args.value_of("output") {
|
||||
if let Some(csv_path) = &configs::CONFIG.read().unwrap().args.output {
|
||||
// output to file
|
||||
match File::create(csv_path) {
|
||||
Ok(file) => Box::new(BufWriter::new(file)),
|
||||
@@ -362,19 +363,13 @@ fn emit_csv<W: std::io::Write>(
|
||||
writeln!(disp_wtr_buf, "Results Summary:").ok();
|
||||
disp_wtr.print(&disp_wtr_buf).ok();
|
||||
|
||||
let size = terminal_size();
|
||||
let terminal_width = match size {
|
||||
let terminal_width = match *TERM_SIZE {
|
||||
Some((Width(w), _)) => w as usize,
|
||||
None => 100,
|
||||
};
|
||||
println!();
|
||||
|
||||
if configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("visualize-timeline")
|
||||
{
|
||||
if configs::CONFIG.read().unwrap().args.visualize_timeline {
|
||||
_print_timeline_hist(timestamps, terminal_width, 3);
|
||||
println!();
|
||||
}
|
||||
@@ -439,7 +434,7 @@ fn _get_serialized_disp_output(dispformat: Option<DisplayFormat>) -> String {
|
||||
if !*IS_HIDE_RECORD_ID {
|
||||
titles.insert(5, "RecordID");
|
||||
}
|
||||
if configs::CONFIG.read().unwrap().args.is_present("full-data") {
|
||||
if configs::CONFIG.read().unwrap().args.full_data {
|
||||
titles.push("RecordInformation");
|
||||
}
|
||||
return format!("{}\n", titles.join("|"));
|
||||
@@ -598,7 +593,7 @@ fn _print_detection_summary_by_computer(
|
||||
}
|
||||
|
||||
fn format_time(time: &DateTime<Utc>, date_only: bool) -> String {
|
||||
if configs::CONFIG.read().unwrap().args.is_present("utc") {
|
||||
if configs::CONFIG.read().unwrap().args.utc {
|
||||
format_rfc(time, date_only)
|
||||
} else {
|
||||
format_rfc(&time.with_timezone(&Local), date_only)
|
||||
@@ -607,7 +602,7 @@ fn format_time(time: &DateTime<Utc>, date_only: bool) -> String {
|
||||
|
||||
/// get timestamp to input datetime.
|
||||
fn _get_timestamp(time: &DateTime<Utc>) -> i64 {
|
||||
if configs::CONFIG.read().unwrap().args.is_present("utc") {
|
||||
if configs::CONFIG.read().unwrap().args.utc {
|
||||
time.timestamp()
|
||||
} else {
|
||||
let offset_sec = Local.timestamp(0, 0).offset().local_minus_utc();
|
||||
@@ -621,31 +616,31 @@ where
|
||||
Tz::Offset: std::fmt::Display,
|
||||
{
|
||||
let time_args = &configs::CONFIG.read().unwrap().args;
|
||||
if time_args.is_present("rfc-2822") {
|
||||
if time_args.rfc_2822 {
|
||||
if date_only {
|
||||
time.format("%a, %e %b %Y").to_string()
|
||||
} else {
|
||||
time.format("%a, %e %b %Y %H:%M:%S %:z").to_string()
|
||||
}
|
||||
} else if time_args.is_present("rfc-3339") {
|
||||
} else if time_args.rfc_3339 {
|
||||
if date_only {
|
||||
time.format("%Y-%m-%d").to_string()
|
||||
} else {
|
||||
time.format("%Y-%m-%d %H:%M:%S%.6f%:z").to_string()
|
||||
}
|
||||
} else if time_args.is_present("US-time") {
|
||||
} else if time_args.us_time {
|
||||
if date_only {
|
||||
time.format("%m-%d-%Y").to_string()
|
||||
} else {
|
||||
time.format("%m-%d-%Y %I:%M:%S%.3f %p %:z").to_string()
|
||||
}
|
||||
} else if time_args.is_present("US-military-time") {
|
||||
} else if time_args.us_military_time {
|
||||
if date_only {
|
||||
time.format("%m-%d-%Y").to_string()
|
||||
} else {
|
||||
time.format("%m-%d-%Y %H:%M:%S%.3f %:z").to_string()
|
||||
}
|
||||
} else if time_args.is_present("European-time") {
|
||||
} else if time_args.european_time {
|
||||
if date_only {
|
||||
time.format("%d-%m-%Y").to_string()
|
||||
} else {
|
||||
|
||||
+192
-90
@@ -3,14 +3,17 @@ use crate::detections::pivot::PIVOT_KEYWORD;
|
||||
use crate::detections::print::AlertMessage;
|
||||
use crate::detections::utils;
|
||||
use chrono::{DateTime, Utc};
|
||||
use clap::{App, AppSettings, Arg, ArgMatches};
|
||||
use clap::{App, CommandFactory, Parser};
|
||||
use hashbrown::HashMap;
|
||||
use hashbrown::HashSet;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::RwLock;
|
||||
use terminal_size::{terminal_size, Height, Width};
|
||||
|
||||
lazy_static! {
|
||||
pub static ref CONFIG: RwLock<ConfigReader> = RwLock::new(ConfigReader::new());
|
||||
pub static ref CONFIG: RwLock<ConfigReader<'static>> = RwLock::new(ConfigReader::new());
|
||||
pub static ref LEVELMAP: HashMap<String, u128> = {
|
||||
let mut levelmap = HashMap::new();
|
||||
levelmap.insert("INFORMATIONAL".to_owned(), 1);
|
||||
@@ -22,109 +25,209 @@ lazy_static! {
|
||||
};
|
||||
pub static ref EVENTKEY_ALIAS: EventKeyAliasConfig = load_eventkey_alias(&format!(
|
||||
"{}/eventkey_alias.txt",
|
||||
CONFIG.read().unwrap().folder_path
|
||||
CONFIG.read().unwrap().args.config.as_path().display()
|
||||
));
|
||||
pub static ref IDS_REGEX: Regex =
|
||||
Regex::new(r"^[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}$").unwrap();
|
||||
pub static ref TERM_SIZE: Option<(Width, Height)> = terminal_size();
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConfigReader {
|
||||
pub args: ArgMatches<'static>,
|
||||
pub folder_path: String,
|
||||
pub struct ConfigReader<'a> {
|
||||
pub app: App<'a>,
|
||||
pub args: Config,
|
||||
pub headless_help: String,
|
||||
pub event_timeline_config: EventInfoConfig,
|
||||
pub target_eventids: TargetEventIds,
|
||||
}
|
||||
|
||||
impl Default for ConfigReader {
|
||||
impl Default for ConfigReader<'_> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigReader {
|
||||
#[derive(Parser)]
|
||||
#[clap(
|
||||
name = "Hayabusa",
|
||||
usage = "hayabusa.exe -f file.evtx [OPTIONS] / hayabusa.exe -d evtx-directory [OPTIONS]",
|
||||
author = "Yamato Security (https://github.com/Yamato-Security/hayabusa) @SecurityYamato)",
|
||||
version,
|
||||
term_width = 400
|
||||
)]
|
||||
pub struct Config {
|
||||
/// Directory of multiple .evtx files
|
||||
#[clap(short = 'd', long, value_name = "DIRECTORY")]
|
||||
pub directory: Option<PathBuf>,
|
||||
|
||||
/// File path to one .evtx file
|
||||
#[clap(short = 'f', long, value_name = "FILE_PATH")]
|
||||
pub filepath: Option<PathBuf>,
|
||||
|
||||
/// Print all field information
|
||||
#[clap(short = 'F', long = "full-data")]
|
||||
pub full_data: bool,
|
||||
|
||||
/// Specify a rule directory or file (default: ./rules)
|
||||
#[clap(
|
||||
short = 'r',
|
||||
long,
|
||||
default_value = "./rules",
|
||||
hide_default_value = true,
|
||||
value_name = "RULE_DIRECTORY/RULE_FILE"
|
||||
)]
|
||||
pub rules: PathBuf,
|
||||
|
||||
/// Specify custom rule config folder (default: ./rules/config)
|
||||
#[clap(
|
||||
short = 'c',
|
||||
long,
|
||||
default_value = "./rules/config",
|
||||
hide_default_value = true,
|
||||
value_name = "RULE_CONFIG_DIRECTORY"
|
||||
)]
|
||||
pub config: PathBuf,
|
||||
|
||||
/// Save the timeline in CSV format (ex: results.csv)
|
||||
#[clap(short = 'o', long, value_name = "CSV_TIMELINE")]
|
||||
pub output: Option<PathBuf>,
|
||||
|
||||
/// Output all tags when saving to a CSV file
|
||||
#[clap(long = "all-tags")]
|
||||
pub all_tags: bool,
|
||||
|
||||
/// Do not display EventRecordID numbers
|
||||
#[clap(short = 'R', long = "hide-record-id")]
|
||||
pub hide_record_id: bool,
|
||||
|
||||
/// Output verbose information
|
||||
#[clap(short = 'v', long)]
|
||||
pub verbose: bool,
|
||||
|
||||
/// Output event frequency timeline
|
||||
#[clap(short = 'V', long = "visualize-timeline")]
|
||||
pub visualize_timeline: bool,
|
||||
|
||||
/// Enable rules marked as deprecated
|
||||
#[clap(short = 'D', long = "enable-deprecated-rules")]
|
||||
pub enable_deprecated_rules: bool,
|
||||
|
||||
/// Enable rules marked as noisy
|
||||
#[clap(short = 'n', long = "enable-noisy-rules")]
|
||||
pub enable_noisy_rules: bool,
|
||||
|
||||
/// Update to the latest rules in the hayabusa-rules github repository
|
||||
#[clap(short = 'u', long = "update-rules")]
|
||||
pub update_rules: bool,
|
||||
|
||||
/// Minimum level for rules (default: informational)
|
||||
#[clap(
|
||||
short = 'm',
|
||||
long = "min-level",
|
||||
default_value = "informational",
|
||||
hide_default_value = true,
|
||||
value_name = "LEVEL"
|
||||
)]
|
||||
pub min_level: String,
|
||||
|
||||
/// Analyze the local C:\Windows\System32\winevt\Logs folder
|
||||
#[clap(short = 'l', long = "live-analysis")]
|
||||
pub live_analysis: bool,
|
||||
|
||||
/// Start time of the event logs to load (ex: "2020-02-22 00:00:00 +09:00")
|
||||
#[clap(long = "start-timeline", value_name = "START_TIMELINE")]
|
||||
pub start_timeline: Option<String>,
|
||||
|
||||
/// End time of the event logs to load (ex: "2022-02-22 23:59:59 +09:00")
|
||||
#[clap(long = "end-timeline", value_name = "END_TIMELINE")]
|
||||
pub end_timeline: Option<String>,
|
||||
|
||||
/// Output timestamp in RFC 2822 format (ex: Fri, 22 Feb 2022 22:00:00 -0600)
|
||||
#[clap(long = "RFC-2822")]
|
||||
pub rfc_2822: bool,
|
||||
|
||||
/// Output timestamp in RFC 3339 format (ex: 2022-02-22 22:00:00.123456-06:00)
|
||||
#[clap(long = "RFC-3339")]
|
||||
pub rfc_3339: bool,
|
||||
|
||||
/// Output timestamp in US time format (ex: 02-22-2022 10:00:00.123 PM -06:00)
|
||||
#[clap(long = "US-time")]
|
||||
pub us_time: bool,
|
||||
|
||||
/// Output timestamp in US military time format (ex: 02-22-2022 22:00:00.123 -06:00)
|
||||
#[clap(long = "US-military-time")]
|
||||
pub us_military_time: bool,
|
||||
|
||||
/// Output timestamp in European time format (ex: 22-02-2022 22:00:00.123 +02:00)
|
||||
#[clap(long = "European-time")]
|
||||
pub european_time: bool,
|
||||
|
||||
/// Output time in UTC format (default: local time)
|
||||
#[clap(short = 'U', long = "UTC")]
|
||||
pub utc: bool,
|
||||
|
||||
/// Disable color output
|
||||
#[clap(long = "no-color")]
|
||||
pub no_color: bool,
|
||||
|
||||
/// Thread number (default: optimal number for performance)
|
||||
#[clap(short, long = "thread-number", value_name = "NUMBER")]
|
||||
pub thread_number: Option<usize>,
|
||||
|
||||
/// Print statistics of event IDs
|
||||
#[clap(short, long)]
|
||||
pub statistics: bool,
|
||||
|
||||
/// Print a summary of successful and failed logons
|
||||
#[clap(short = 'L', long = "logon-summary")]
|
||||
pub logon_summary: bool,
|
||||
|
||||
/// Tune alert levels (default: ./rules/config/level_tuning.txt)
|
||||
#[clap(
|
||||
long = "level-tuning",
|
||||
default_value = "./rules/config/level_tuning.txt",
|
||||
hide_default_value = true,
|
||||
value_name = "LEVEL_TUNING_FILE"
|
||||
)]
|
||||
pub level_tuning: PathBuf,
|
||||
|
||||
/// Quiet mode: do not display the launch banner
|
||||
#[clap(short, long)]
|
||||
pub quiet: bool,
|
||||
|
||||
/// Quiet errors mode: do not save error logs
|
||||
#[clap(short = 'Q', long = "quiet-errors")]
|
||||
pub quiet_errors: bool,
|
||||
|
||||
/// Create a list of pivot keywords
|
||||
#[clap(short = 'p', long = "pivot-keywords-list")]
|
||||
pub pivot_keywords_list: bool,
|
||||
|
||||
/// Print the list of contributors
|
||||
#[clap(long)]
|
||||
pub contributors: bool,
|
||||
}
|
||||
|
||||
impl ConfigReader<'_> {
|
||||
pub fn new() -> Self {
|
||||
let arg = build_app();
|
||||
let folder_path_str = arg.value_of("config").unwrap_or("rules/config").to_string();
|
||||
let parse = Config::parse();
|
||||
let help_term_width = if let Some((Width(w), _)) = *TERM_SIZE {
|
||||
w as usize
|
||||
} else {
|
||||
400
|
||||
};
|
||||
let build_cmd = Config::command()
|
||||
.term_width(help_term_width)
|
||||
.help_template("\n\nUSAGE:\n {usage}\n\nOPTIONS:\n{options}");
|
||||
ConfigReader {
|
||||
args: arg,
|
||||
folder_path: folder_path_str,
|
||||
app: build_cmd,
|
||||
args: parse,
|
||||
headless_help: String::default(),
|
||||
event_timeline_config: load_eventcode_info("config/statistics_event_info.txt"),
|
||||
target_eventids: load_target_ids("config/target_eventids.txt"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_app<'a>() -> ArgMatches<'a> {
|
||||
let program = std::env::args()
|
||||
.next()
|
||||
.and_then(|s| {
|
||||
std::path::PathBuf::from(s)
|
||||
.file_stem()
|
||||
.map(|s| s.to_string_lossy().into_owned())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
if is_test_mode() {
|
||||
return ArgMatches::default();
|
||||
}
|
||||
|
||||
let usages = "-d, --directory [DIRECTORY] 'Directory of multiple .evtx files.'
|
||||
-f, --filepath [FILE_PATH] 'File path to one .evtx file.'
|
||||
-F, --full-data 'Print all field information.'
|
||||
-r, --rules [RULE_DIRECTORY/RULE_FILE] 'Rule directory or file (Default: .\\rules)'
|
||||
-C, --config [RULE_CONFIG_DIRECTORY] 'Rule config folder. (Default: .\\rules\\config)'
|
||||
-o, --output [CSV_TIMELINE] 'Save the timeline in CSV format. (Ex: results.csv)'
|
||||
--all-tags 'Output all tags when saving to a CSV file.'
|
||||
-R, --hide-record-id 'Do not display EventRecordID number.'
|
||||
-v, --verbose 'Output verbose information.'
|
||||
-V, --visualize-timeline 'Output event frequency timeline.'
|
||||
-D, --enable-deprecated-rules 'Enable rules marked as deprecated.'
|
||||
-n, --enable-noisy-rules 'Enable rules marked as noisy.'
|
||||
-u, --update-rules 'Update to the latest rules in the hayabusa-rules github repository.'
|
||||
-m, --min-level [LEVEL] 'Minimum level for rules. (Default: informational)'
|
||||
-l, --live-analysis 'Analyze the local C:\\Windows\\System32\\winevt\\Logs folder (Windows Only. Administrator privileges required.)'
|
||||
--start-timeline [START_TIMELINE] 'Start time of the event logs to load. (Ex: \"2020-02-22 00:00:00 +09:00\")'
|
||||
--end-timeline [END_TIMELINE] 'End time of the event logs to load. (Ex: \"2022-02-22 23:59:59 +09:00\")'
|
||||
--rfc-2822 'Output timestamp in RFC 2822 format. (Ex: Fri, 22 Feb 2022 22:00:00 -0600)'
|
||||
--rfc-3339 'Output timestamp in RFC 3339 format. (Ex: 2022-02-22 22:00:00.123456-06:00)'
|
||||
--US-time 'Output timestamp in US time format. (Ex: 02-22-2022 10:00:00.123 PM -06:00)'
|
||||
--US-military-time 'Output timestamp in US military time format. (Ex: 02-22-2022 22:00:00.123 -06:00)'
|
||||
--European-time 'Output timestamp in European time format. (Ex: 22-02-2022 22:00:00.123 +02:00)'
|
||||
-U, --utc 'Output time in UTC format. (Default: local time)'
|
||||
--no-color 'Disable color output.'
|
||||
-t, --thread-number [NUMBER] 'Thread number. (Default: Optimal number for performance.)'
|
||||
-s, --statistics 'Prints statistics of event IDs.'
|
||||
-L, --logon-summary 'Successful and failed logons summary.'
|
||||
-q, --quiet 'Quiet mode. Do not display the launch banner.'
|
||||
-Q, --quiet-errors 'Quiet errors mode. Do not save error logs.'
|
||||
-p, --pivot-keywords-list 'Create a list of pivot keywords.'
|
||||
--contributors 'Prints the list of contributors.'";
|
||||
App::new(&program)
|
||||
.about("Hayabusa: Aiming to be the world's greatest Windows event log analysis tool!")
|
||||
.version("1.3.2")
|
||||
.author("Yamato Security (https://github.com/Yamato-Security/hayabusa) @SecurityYamato")
|
||||
.setting(AppSettings::VersionlessSubcommands)
|
||||
.arg(
|
||||
// TODO: When update claps to 3.x, these can write in usage texts...
|
||||
Arg::from_usage("--level-tuning [LEVEL_TUNING_FILE] 'Tune alert levels. (Default: .\\rules\\config\\level_tuning.txt)'")
|
||||
.default_value("./rules/config/level_tuning.txt"),
|
||||
)
|
||||
.usage(usages)
|
||||
.args_from_usage(usages)
|
||||
.get_matches()
|
||||
}
|
||||
|
||||
fn is_test_mode() -> bool {
|
||||
for i in std::env::args() {
|
||||
if i == "--test" {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TargetEventIds {
|
||||
ids: HashSet<String>,
|
||||
@@ -186,9 +289,8 @@ impl Default for TargetEventTime {
|
||||
impl TargetEventTime {
|
||||
pub fn new() -> Self {
|
||||
let mut parse_success_flag = true;
|
||||
let start_time =
|
||||
if let Some(s_time) = CONFIG.read().unwrap().args.value_of("start-timeline") {
|
||||
match DateTime::parse_from_str(s_time, "%Y-%m-%d %H:%M:%S %z") // 2014-11-28 21:00:09 +09:00
|
||||
let start_time = if let Some(s_time) = &CONFIG.read().unwrap().args.start_timeline {
|
||||
match DateTime::parse_from_str(s_time, "%Y-%m-%d %H:%M:%S %z") // 2014-11-28 21:00:09 +09:00
|
||||
.or_else(|_| DateTime::parse_from_str(s_time, "%Y/%m/%d %H:%M:%S %z")) // 2014/11/28 21:00:09 +09:00
|
||||
{
|
||||
Ok(dt) => Some(dt.with_timezone(&Utc)),
|
||||
@@ -201,10 +303,10 @@ impl TargetEventTime {
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let end_time = if let Some(e_time) = CONFIG.read().unwrap().args.value_of("end-timeline") {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let end_time = if let Some(e_time) = &CONFIG.read().unwrap().args.end_timeline {
|
||||
match DateTime::parse_from_str(e_time, "%Y-%m-%d %H:%M:%S %z") // 2014-11-28 21:00:09 +09:00
|
||||
.or_else(|_| DateTime::parse_from_str(e_time, "%Y/%m/%d %H:%M:%S %z")) // 2014/11/28 21:00:09 +09:00
|
||||
{
|
||||
|
||||
+12
-18
@@ -6,10 +6,10 @@ use crate::detections::print::AlertMessage;
|
||||
use crate::detections::print::DetectInfo;
|
||||
use crate::detections::print::ERROR_LOG_STACK;
|
||||
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, IS_HIDE_RECORD_ID, TAGS_CONFIG};
|
||||
use crate::detections::print::{
|
||||
LOGONSUMMARY_FLAG, PIVOT_KEYWORD_LIST_FLAG, QUIET_ERRORS_FLAG, STATISTICS_FLAG,
|
||||
};
|
||||
use crate::detections::rule;
|
||||
use crate::detections::rule::AggResult;
|
||||
use crate::detections::rule::RuleNode;
|
||||
@@ -20,11 +20,10 @@ use hashbrown;
|
||||
use hashbrown::HashMap;
|
||||
use serde_json::Value;
|
||||
use std::fmt::Write;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use tokio::{runtime::Runtime, spawn, task::JoinHandle};
|
||||
|
||||
const DIRPATH_RULES: &str = "rules";
|
||||
|
||||
// イベントファイルの1レコード分の情報を保持する構造体
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EvtxRecordInfo {
|
||||
@@ -58,16 +57,15 @@ impl Detection {
|
||||
// ルールファイルをパースします。
|
||||
pub fn parse_rule_files(
|
||||
level: String,
|
||||
rulespath: Option<&str>,
|
||||
rulespath: &Path,
|
||||
exclude_ids: &filter::RuleExclude,
|
||||
) -> Vec<RuleNode> {
|
||||
// ルールファイルのパースを実行
|
||||
let mut rulefile_loader = ParseYaml::new();
|
||||
let result_readdir =
|
||||
rulefile_loader.read_dir(rulespath.unwrap_or(DIRPATH_RULES), &level, exclude_ids);
|
||||
let result_readdir = rulefile_loader.read_dir(rulespath, &level, exclude_ids);
|
||||
if result_readdir.is_err() {
|
||||
let errmsg = format!("{}", result_readdir.unwrap_err());
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
if configs::CONFIG.read().unwrap().args.verbose {
|
||||
AlertMessage::alert(&errmsg).ok();
|
||||
}
|
||||
if !*QUIET_ERRORS_FLAG {
|
||||
@@ -89,7 +87,7 @@ impl Detection {
|
||||
err_msgs_result.err().iter().for_each(|err_msgs| {
|
||||
let errmsg_body =
|
||||
format!("Failed to parse rule file. (FilePath : {})", rule.rulepath);
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
if configs::CONFIG.read().unwrap().args.verbose {
|
||||
AlertMessage::warn(&errmsg_body).ok();
|
||||
|
||||
err_msgs.iter().for_each(|err_msg| {
|
||||
@@ -120,12 +118,7 @@ impl Detection {
|
||||
.map(|rule_file_tuple| rule::create_rule(rule_file_tuple.0, rule_file_tuple.1))
|
||||
.filter_map(return_if_success)
|
||||
.collect();
|
||||
if !configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("logon-summary")
|
||||
{
|
||||
if !*LOGONSUMMARY_FLAG {
|
||||
let _ = &rulefile_loader
|
||||
.rule_load_cnt
|
||||
.insert(String::from("rule parsing error"), parseerror_count);
|
||||
@@ -276,7 +269,7 @@ impl Detection {
|
||||
.map(|str| str.to_owned())
|
||||
.collect();
|
||||
let output = Detection::create_count_output(rule, &agg_result);
|
||||
let rec_info = if configs::CONFIG.read().unwrap().args.is_present("full-data") {
|
||||
let rec_info = if configs::CONFIG.read().unwrap().args.full_data {
|
||||
Option::Some(String::default())
|
||||
} else {
|
||||
Option::None
|
||||
@@ -411,12 +404,13 @@ mod tests {
|
||||
use crate::detections::rule::AggResult;
|
||||
use crate::filter;
|
||||
use chrono::{TimeZone, Utc};
|
||||
use std::path::Path;
|
||||
use yaml_rust::YamlLoader;
|
||||
|
||||
#[test]
|
||||
fn test_parse_rule_files() {
|
||||
let level = "informational";
|
||||
let opt_rule_path = Some("./test_files/rules/level_yaml");
|
||||
let opt_rule_path = Path::new("./test_files/rules/level_yaml");
|
||||
let cole =
|
||||
Detection::parse_rule_files(level.to_owned(), opt_rule_path, &filter::exclude_ids());
|
||||
assert_eq!(5, cole.len());
|
||||
|
||||
+8
-27
@@ -48,42 +48,23 @@ lazy_static! {
|
||||
"./logs/errorlog-{}.log",
|
||||
Local::now().format("%Y%m%d_%H%M%S")
|
||||
);
|
||||
pub static ref QUIET_ERRORS_FLAG: bool = configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("quiet-errors");
|
||||
pub static ref QUIET_ERRORS_FLAG: bool = configs::CONFIG.read().unwrap().args.quiet_errors;
|
||||
pub static ref ERROR_LOG_STACK: Mutex<Vec<String>> = Mutex::new(Vec::new());
|
||||
pub static ref STATISTICS_FLAG: bool = configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("statistics");
|
||||
pub static ref LOGONSUMMARY_FLAG: bool = configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("logon-summary");
|
||||
pub static ref STATISTICS_FLAG: bool = configs::CONFIG.read().unwrap().args.statistics;
|
||||
pub static ref LOGONSUMMARY_FLAG: bool = configs::CONFIG.read().unwrap().args.logon_summary;
|
||||
pub static ref TAGS_CONFIG: HashMap<String, String> = Message::create_output_filter_config(
|
||||
"config/output_tag.txt",
|
||||
true,
|
||||
configs::CONFIG.read().unwrap().args.is_present("all-tags")
|
||||
configs::CONFIG.read().unwrap().args.all_tags
|
||||
);
|
||||
pub static ref CH_CONFIG: HashMap<String, String> = Message::create_output_filter_config(
|
||||
"config/channel_abbreviations.txt",
|
||||
false,
|
||||
configs::CONFIG.read().unwrap().args.is_present("all-tags")
|
||||
configs::CONFIG.read().unwrap().args.all_tags
|
||||
);
|
||||
pub static ref PIVOT_KEYWORD_LIST_FLAG: bool = configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("pivot-keywords-list");
|
||||
pub static ref IS_HIDE_RECORD_ID: bool = configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("hide-record-id");
|
||||
pub static ref PIVOT_KEYWORD_LIST_FLAG: bool =
|
||||
configs::CONFIG.read().unwrap().args.pivot_keywords_list;
|
||||
pub static ref IS_HIDE_RECORD_ID: bool = configs::CONFIG.read().unwrap().args.hide_record_id;
|
||||
}
|
||||
|
||||
impl Default for Message {
|
||||
|
||||
@@ -86,7 +86,7 @@ fn get_alias_value_in_record(
|
||||
utils::get_event_value(&utils::get_event_id_key(), record).unwrap()
|
||||
),
|
||||
};
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
if configs::CONFIG.read().unwrap().args.verbose {
|
||||
AlertMessage::alert(&errmsg).ok();
|
||||
}
|
||||
if !*QUIET_ERRORS_FLAG {
|
||||
@@ -188,7 +188,7 @@ impl TimeFrameInfo {
|
||||
tnum.retain(|c| c != 'd');
|
||||
} else {
|
||||
let errmsg = format!("Timeframe is invalid. Input value:{}", value);
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
if configs::CONFIG.read().unwrap().args.verbose {
|
||||
AlertMessage::alert(&errmsg).ok();
|
||||
}
|
||||
if !*QUIET_ERRORS_FLAG {
|
||||
@@ -224,7 +224,7 @@ pub fn get_sec_timeframe(rule: &RuleNode) -> Option<i64> {
|
||||
}
|
||||
Err(err) => {
|
||||
let errmsg = format!("Timeframe number is invalid. timeframe. {}", err);
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
if configs::CONFIG.read().unwrap().args.verbose {
|
||||
AlertMessage::alert(&errmsg).ok();
|
||||
}
|
||||
if !*QUIET_ERRORS_FLAG {
|
||||
|
||||
@@ -185,13 +185,9 @@ pub fn get_event_value<'a>(key: &str, event_value: &'a Value) -> Option<&'a Valu
|
||||
}
|
||||
|
||||
pub fn get_thread_num() -> usize {
|
||||
let def_thread_num_str = num_cpus::get().to_string();
|
||||
let cpu_num = num_cpus::get();
|
||||
let conf = configs::CONFIG.read().unwrap();
|
||||
conf.args
|
||||
.value_of("thread-number")
|
||||
.unwrap_or(def_thread_num_str.as_str())
|
||||
.parse::<usize>()
|
||||
.unwrap()
|
||||
conf.args.thread_number.unwrap_or(cpu_num)
|
||||
}
|
||||
|
||||
pub fn create_tokio_runtime() -> Runtime {
|
||||
@@ -228,7 +224,7 @@ pub fn create_rec_info(data: Value, path: String, keys: &[String]) -> EvtxRecord
|
||||
|
||||
// EvtxRecordInfoを作る
|
||||
let data_str = data.to_string();
|
||||
let rec_info = if configs::CONFIG.read().unwrap().args.is_present("full-data") {
|
||||
let rec_info = if configs::CONFIG.read().unwrap().args.full_data {
|
||||
Option::Some(create_recordinfos(&data))
|
||||
} else {
|
||||
Option::None
|
||||
@@ -279,7 +275,7 @@ fn create_recordinfos(record: &Value) -> String {
|
||||
.collect();
|
||||
|
||||
// 標準出力する時はセルがハイプ区切りになるので、パイプ区切りにしない
|
||||
if configs::CONFIG.read().unwrap().args.is_present("output") {
|
||||
if configs::CONFIG.read().unwrap().args.output.is_some() {
|
||||
summary.join(" | ")
|
||||
} else {
|
||||
summary.join(" ")
|
||||
|
||||
+16
-9
@@ -29,21 +29,28 @@ impl RuleExclude {
|
||||
pub fn exclude_ids() -> RuleExclude {
|
||||
let mut exclude_ids = RuleExclude::default();
|
||||
|
||||
if !configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("enable-noisy-rules")
|
||||
{
|
||||
if !configs::CONFIG.read().unwrap().args.enable_noisy_rules {
|
||||
exclude_ids.insert_ids(&format!(
|
||||
"{}/noisy_rules.txt",
|
||||
configs::CONFIG.read().unwrap().folder_path
|
||||
configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.config
|
||||
.as_path()
|
||||
.display()
|
||||
));
|
||||
};
|
||||
|
||||
exclude_ids.insert_ids(&format!(
|
||||
"{}/exclude_rules.txt",
|
||||
configs::CONFIG.read().unwrap().folder_path
|
||||
configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.config
|
||||
.as_path()
|
||||
.display()
|
||||
));
|
||||
|
||||
exclude_ids
|
||||
@@ -53,7 +60,7 @@ impl RuleExclude {
|
||||
fn insert_ids(&mut self, filename: &str) {
|
||||
let f = File::open(filename);
|
||||
if f.is_err() {
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
if configs::CONFIG.read().unwrap().args.verbose {
|
||||
AlertMessage::warn(&format!("{} does not exist", filename)).ok();
|
||||
}
|
||||
if !*QUIET_ERRORS_FLAG {
|
||||
|
||||
+56
-62
@@ -86,22 +86,14 @@ impl App {
|
||||
}
|
||||
|
||||
let analysis_start_time: DateTime<Local> = Local::now();
|
||||
|
||||
// Show usage when no arguments.
|
||||
if std::env::args().len() == 1 {
|
||||
self.output_logo();
|
||||
println!();
|
||||
write_color_buffer(
|
||||
BufferWriter::stdout(ColorChoice::Always),
|
||||
None,
|
||||
configs::CONFIG.read().unwrap().args.usage(),
|
||||
)
|
||||
.ok();
|
||||
configs::CONFIG.write().unwrap().app.print_help().ok();
|
||||
println!();
|
||||
return;
|
||||
}
|
||||
|
||||
if !configs::CONFIG.read().unwrap().args.is_present("quiet") {
|
||||
if !configs::CONFIG.read().unwrap().args.quiet {
|
||||
self.output_logo();
|
||||
println!();
|
||||
self.output_eggs(&format!(
|
||||
@@ -120,12 +112,7 @@ impl App {
|
||||
return;
|
||||
}
|
||||
|
||||
if configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("update-rules")
|
||||
{
|
||||
if configs::CONFIG.read().unwrap().args.update_rules {
|
||||
match self.update_rules() {
|
||||
Ok(output) => {
|
||||
if output != "You currently have the latest rules." {
|
||||
@@ -153,10 +140,11 @@ impl App {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(csv_path) = configs::CONFIG.read().unwrap().args.value_of("output") {
|
||||
if let Some(csv_path) = &configs::CONFIG.read().unwrap().args.output {
|
||||
let pivot_key_unions = PIVOT_KEYWORD.read().unwrap();
|
||||
pivot_key_unions.iter().for_each(|(key, _)| {
|
||||
let keywords_file_name = csv_path.to_owned() + "-" + key + ".txt";
|
||||
let keywords_file_name =
|
||||
csv_path.as_path().display().to_string() + "-" + key + ".txt";
|
||||
if Path::new(&keywords_file_name).exists() {
|
||||
AlertMessage::alert(&format!(
|
||||
" The file {} already exists. Please specify a different filename.",
|
||||
@@ -165,10 +153,10 @@ impl App {
|
||||
.ok();
|
||||
}
|
||||
});
|
||||
if Path::new(csv_path).exists() {
|
||||
if csv_path.exists() {
|
||||
AlertMessage::alert(&format!(
|
||||
" The file {} already exists. Please specify a different filename.",
|
||||
csv_path
|
||||
csv_path.as_os_str().to_str().unwrap()
|
||||
))
|
||||
.ok();
|
||||
return;
|
||||
@@ -198,20 +186,16 @@ impl App {
|
||||
.ok();
|
||||
println!();
|
||||
}
|
||||
if configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("live-analysis")
|
||||
{
|
||||
if configs::CONFIG.read().unwrap().args.live_analysis {
|
||||
let live_analysis_list = self.collect_liveanalysis_files();
|
||||
if live_analysis_list.is_none() {
|
||||
return;
|
||||
}
|
||||
self.analysis_files(live_analysis_list.unwrap(), &time_filter);
|
||||
} else if let Some(filepath) = configs::CONFIG.read().unwrap().args.value_of("filepath") {
|
||||
if !filepath.ends_with(".evtx")
|
||||
|| Path::new(filepath)
|
||||
} else if let Some(filepath) = &configs::CONFIG.read().unwrap().args.filepath {
|
||||
if filepath.extension().unwrap_or_else(|| OsStr::new(".")) != "evtx"
|
||||
|| filepath
|
||||
.as_path()
|
||||
.file_stem()
|
||||
.unwrap_or_else(|| OsStr::new("."))
|
||||
.to_str()
|
||||
@@ -226,36 +210,27 @@ impl App {
|
||||
return;
|
||||
}
|
||||
self.analysis_files(vec![PathBuf::from(filepath)], &time_filter);
|
||||
} else if let Some(directory) = configs::CONFIG.read().unwrap().args.value_of("directory") {
|
||||
let evtx_files = self.collect_evtxfiles(directory);
|
||||
} else if let Some(directory) = &configs::CONFIG.read().unwrap().args.directory {
|
||||
let evtx_files = self.collect_evtxfiles(directory.as_os_str().to_str().unwrap());
|
||||
if evtx_files.is_empty() {
|
||||
AlertMessage::alert("No .evtx files were found.").ok();
|
||||
return;
|
||||
}
|
||||
self.analysis_files(evtx_files, &time_filter);
|
||||
} else if configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("contributors")
|
||||
{
|
||||
} else if configs::CONFIG.read().unwrap().args.contributors {
|
||||
self.print_contributors();
|
||||
return;
|
||||
} else if configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("level-tuning")
|
||||
&& std::env::args()
|
||||
.into_iter()
|
||||
.any(|arg| arg.contains("level-tuning"))
|
||||
} else if std::env::args()
|
||||
.into_iter()
|
||||
.any(|arg| arg.contains("level-tuning"))
|
||||
{
|
||||
let level_tuning_config_path = configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.value_of("level-tuning")
|
||||
.unwrap_or("./rules/config/level_tuning.txt")
|
||||
.level_tuning
|
||||
.as_path()
|
||||
.display()
|
||||
.to_string();
|
||||
|
||||
if Path::new(&level_tuning_config_path).exists() {
|
||||
@@ -265,8 +240,10 @@ impl App {
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.value_of("rules")
|
||||
.unwrap_or("rules"),
|
||||
.rules
|
||||
.as_os_str()
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
) {
|
||||
AlertMessage::alert(&err).ok();
|
||||
}
|
||||
@@ -277,6 +254,14 @@ impl App {
|
||||
.ok();
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
write_color_buffer(
|
||||
BufferWriter::stdout(ColorChoice::Always),
|
||||
None,
|
||||
&configs::CONFIG.read().unwrap().headless_help,
|
||||
)
|
||||
.ok();
|
||||
return;
|
||||
}
|
||||
|
||||
let analysis_end_time: DateTime<Local> = Local::now();
|
||||
@@ -290,7 +275,7 @@ impl App {
|
||||
.ok();
|
||||
println!();
|
||||
|
||||
// Qオプションを付けた場合もしくはパースのエラーがない場合はerrorのstackが9となるのでエラーログファイル自体が生成されない。
|
||||
// Qオプションを付けた場合もしくはパースのエラーがない場合はerrorのstackが0となるのでエラーログファイル自体が生成されない。
|
||||
if ERROR_LOG_STACK.lock().unwrap().len() > 0 {
|
||||
AlertMessage::create_error_log(ERROR_LOG_PATH.to_string());
|
||||
}
|
||||
@@ -315,10 +300,13 @@ impl App {
|
||||
};
|
||||
|
||||
//ファイル出力の場合
|
||||
if let Some(pivot_file) = configs::CONFIG.read().unwrap().args.value_of("output") {
|
||||
if let Some(pivot_file) = &configs::CONFIG.read().unwrap().args.output {
|
||||
pivot_key_unions.iter().for_each(|(key, pivot_keyword)| {
|
||||
let mut f = BufWriter::new(
|
||||
fs::File::create(pivot_file.to_owned() + "-" + key + ".txt").unwrap(),
|
||||
fs::File::create(
|
||||
pivot_file.as_path().display().to_string() + "-" + key + ".txt",
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
f.write_all(create_output(String::default(), key, pivot_keyword).as_bytes())
|
||||
.unwrap();
|
||||
@@ -328,7 +316,12 @@ impl App {
|
||||
"Pivot keyword results saved to the following files:\n".to_string();
|
||||
|
||||
pivot_key_unions.iter().for_each(|(key, _)| {
|
||||
writeln!(output, "{}", &(pivot_file.to_owned() + "-" + key + ".txt")).ok();
|
||||
writeln!(
|
||||
output,
|
||||
"{}",
|
||||
&(pivot_file.as_path().display().to_string() + "-" + key + ".txt")
|
||||
)
|
||||
.ok();
|
||||
});
|
||||
write_color_buffer(BufferWriter::stdout(ColorChoice::Always), None, &output).ok();
|
||||
} else {
|
||||
@@ -379,7 +372,7 @@ impl App {
|
||||
let entries = fs::read_dir(dirpath);
|
||||
if entries.is_err() {
|
||||
let errmsg = format!("{}", entries.unwrap_err());
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
if configs::CONFIG.read().unwrap().args.verbose {
|
||||
AlertMessage::alert(&errmsg).ok();
|
||||
}
|
||||
if !*QUIET_ERRORS_FLAG {
|
||||
@@ -424,7 +417,9 @@ impl App {
|
||||
|
||||
fn print_contributors(&self) {
|
||||
match fs::read_to_string("./contributors.txt") {
|
||||
Ok(contents) => println!("{}", contents),
|
||||
Ok(contents) => {
|
||||
write_color_buffer(BufferWriter::stdout(ColorChoice::Always), None, &contents).ok();
|
||||
}
|
||||
Err(err) => {
|
||||
AlertMessage::alert(&format!("{}", err)).ok();
|
||||
}
|
||||
@@ -436,8 +431,7 @@ impl App {
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.value_of("min-level")
|
||||
.unwrap_or("informational")
|
||||
.min_level
|
||||
.to_uppercase();
|
||||
write_color_buffer(
|
||||
BufferWriter::stdout(ColorChoice::Always),
|
||||
@@ -458,7 +452,7 @@ impl App {
|
||||
|
||||
let rule_files = detection::Detection::parse_rule_files(
|
||||
level,
|
||||
configs::CONFIG.read().unwrap().args.value_of("rules"),
|
||||
&configs::CONFIG.read().unwrap().args.rules,
|
||||
&filter::exclude_ids(),
|
||||
);
|
||||
|
||||
@@ -476,7 +470,7 @@ impl App {
|
||||
let mut detection = detection::Detection::new(rule_files);
|
||||
let mut total_records: usize = 0;
|
||||
for evtx_file in evtx_files {
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
if configs::CONFIG.read().unwrap().args.verbose {
|
||||
println!("Checking target evtx FilePath: {:?}", &evtx_file);
|
||||
}
|
||||
let cnt_tmp: usize;
|
||||
@@ -484,7 +478,7 @@ impl App {
|
||||
total_records += cnt_tmp;
|
||||
pb.inc();
|
||||
}
|
||||
if configs::CONFIG.read().unwrap().args.is_present("output") {
|
||||
if configs::CONFIG.read().unwrap().args.output.is_some() {
|
||||
println!();
|
||||
println!();
|
||||
println!("Analysis finished. Please wait while the results are being saved.");
|
||||
@@ -530,7 +524,7 @@ impl App {
|
||||
evtx_filepath,
|
||||
record_result.unwrap_err()
|
||||
);
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
if configs::CONFIG.read().unwrap().args.verbose {
|
||||
AlertMessage::alert(&errmsg).ok();
|
||||
}
|
||||
if !*QUIET_ERRORS_FLAG {
|
||||
@@ -660,7 +654,7 @@ impl App {
|
||||
fn output_logo(&self) {
|
||||
let fp = &"art/logo.txt".to_string();
|
||||
let content = fs::read_to_string(fp).unwrap_or_default();
|
||||
let output_color = if configs::CONFIG.read().unwrap().args.is_present("no-color") {
|
||||
let output_color = if configs::CONFIG.read().unwrap().args.no_color {
|
||||
None
|
||||
} else {
|
||||
Some(Color::Green)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::detections::{configs, detection::EvtxRecordInfo, utils};
|
||||
use crate::detections::print::{LOGONSUMMARY_FLAG, STATISTICS_FLAG};
|
||||
use crate::detections::{detection::EvtxRecordInfo, utils};
|
||||
use hashbrown::HashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -34,12 +35,7 @@ impl EventStatistics {
|
||||
|
||||
pub fn evt_stats_start(&mut self, records: &[EvtxRecordInfo]) {
|
||||
// 引数でstatisticsオプションが指定されている時だけ、統計情報を出力する。
|
||||
if !configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("statistics")
|
||||
{
|
||||
if !*STATISTICS_FLAG {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -53,13 +49,8 @@ impl EventStatistics {
|
||||
}
|
||||
|
||||
pub fn logon_stats_start(&mut self, records: &[EvtxRecordInfo]) {
|
||||
// 引数でstatisticsオプションが指定されている時だけ、統計情報を出力する。
|
||||
if !configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("logon-summary")
|
||||
{
|
||||
// 引数でlogon-summaryオプションが指定されている時だけ、統計情報を出力する。
|
||||
if !*LOGONSUMMARY_FLAG {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::detections::print::{LOGONSUMMARY_FLAG, STATISTICS_FLAG};
|
||||
use crate::detections::{configs::CONFIG, detection::EvtxRecordInfo};
|
||||
use prettytable::{Cell, Row, Table};
|
||||
|
||||
@@ -19,8 +20,8 @@ impl Timeline {
|
||||
pub fn new() -> Timeline {
|
||||
let totalcnt = 0;
|
||||
let filepath = String::default();
|
||||
let starttm = "".to_string();
|
||||
let endtm = "".to_string();
|
||||
let starttm = String::default();
|
||||
let endtm = String::default();
|
||||
let statslst = HashMap::new();
|
||||
let statsloginlst = HashMap::new();
|
||||
|
||||
@@ -35,12 +36,10 @@ impl Timeline {
|
||||
}
|
||||
|
||||
pub fn tm_stats_dsp_msg(&mut self) {
|
||||
let statics_flag = CONFIG.read().unwrap().args.is_present("statistics");
|
||||
if !statics_flag {
|
||||
if !*STATISTICS_FLAG {
|
||||
return;
|
||||
}
|
||||
// 出力メッセージ作成
|
||||
//println!("map -> {:#?}", evtstat_map);
|
||||
let mut sammsges: Vec<String> = Vec::new();
|
||||
sammsges.push("---------------------------------------".to_string());
|
||||
sammsges.push(format!("Evtx File Path: {}", self.stats.filepath));
|
||||
@@ -66,8 +65,7 @@ impl Timeline {
|
||||
}
|
||||
|
||||
pub fn tm_logon_stats_dsp_msg(&mut self) {
|
||||
let logon_summary_flag = CONFIG.read().unwrap().args.is_present("logon-summary");
|
||||
if !logon_summary_flag {
|
||||
if !*LOGONSUMMARY_FLAG {
|
||||
return;
|
||||
}
|
||||
// 出力メッセージ作成
|
||||
|
||||
+7
-12
@@ -68,7 +68,7 @@ impl ParseYaml {
|
||||
"fail to read metadata of file: {}",
|
||||
path.as_ref().to_path_buf().display(),
|
||||
);
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
if configs::CONFIG.read().unwrap().args.verbose {
|
||||
AlertMessage::alert(&errmsg)?;
|
||||
}
|
||||
if !*QUIET_ERRORS_FLAG {
|
||||
@@ -100,7 +100,7 @@ impl ParseYaml {
|
||||
path.as_ref().to_path_buf().display(),
|
||||
read_content.unwrap_err()
|
||||
);
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
if configs::CONFIG.read().unwrap().args.verbose {
|
||||
AlertMessage::warn(&errmsg)?;
|
||||
}
|
||||
if !*QUIET_ERRORS_FLAG {
|
||||
@@ -121,7 +121,7 @@ impl ParseYaml {
|
||||
path.as_ref().to_path_buf().display(),
|
||||
yaml_contents.unwrap_err()
|
||||
);
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
if configs::CONFIG.read().unwrap().args.verbose {
|
||||
AlertMessage::warn(&errmsg)?;
|
||||
}
|
||||
if !*QUIET_ERRORS_FLAG {
|
||||
@@ -173,7 +173,7 @@ impl ParseYaml {
|
||||
entry.path().display(),
|
||||
read_content.unwrap_err()
|
||||
);
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
if configs::CONFIG.read().unwrap().args.verbose {
|
||||
AlertMessage::warn(&errmsg)?;
|
||||
}
|
||||
if !*QUIET_ERRORS_FLAG {
|
||||
@@ -194,7 +194,7 @@ impl ParseYaml {
|
||||
entry.path().display(),
|
||||
yaml_contents.unwrap_err()
|
||||
);
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
if configs::CONFIG.read().unwrap().args.verbose {
|
||||
AlertMessage::warn(&errmsg)?;
|
||||
}
|
||||
if !*QUIET_ERRORS_FLAG {
|
||||
@@ -256,7 +256,7 @@ impl ParseYaml {
|
||||
.or_insert(0);
|
||||
*status_cnt += 1;
|
||||
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
if configs::CONFIG.read().unwrap().args.verbose {
|
||||
println!("Loaded yml file path: {}", filepath);
|
||||
}
|
||||
|
||||
@@ -272,12 +272,7 @@ impl ParseYaml {
|
||||
return Option::None;
|
||||
}
|
||||
|
||||
if !configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("enable-deprecated-rules")
|
||||
{
|
||||
if !configs::CONFIG.read().unwrap().args.enable_deprecated_rules {
|
||||
let rule_status = &yaml_doc["status"].as_str().unwrap_or_default();
|
||||
if *rule_status == "deprecated" {
|
||||
let entry = self
|
||||
|
||||
Reference in New Issue
Block a user