logon summary (#523)
* logon summary #110 * logon summary #110 * english update * add sort #110 * add sort #110 * Formatting the output string * Fixed the check process. * added document #110 * Fixed login failure eventID. * Fixed clipy err * prevent rule load output with logon-summary option #110 * fixed bug of level-tuning execute when option is -s or -L only #110 Co-authored-by: garigariganzy <tosada31@hotmail.co.jp> Co-authored-by: Tanaka Zakku <71482215+YamatoSecurity@users.noreply.github.com>
This commit is contained in:
108
Cargo.lock
generated
108
Cargo.lock
generated
@@ -23,7 +23,7 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.2.6",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
@@ -52,6 +52,18 @@ version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
@@ -120,6 +132,17 @@ version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "blake2b_simd"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"constant_time_eq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.17"
|
||||
@@ -295,6 +318,12 @@ version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.12.0"
|
||||
@@ -484,6 +513,17 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "discard"
|
||||
version = "1.0.4"
|
||||
@@ -769,6 +809,17 @@ dependencies = [
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.6"
|
||||
@@ -863,6 +914,7 @@ dependencies = [
|
||||
"num_cpus",
|
||||
"openssl",
|
||||
"pbr",
|
||||
"prettytable-rs",
|
||||
"quick-xml",
|
||||
"regex",
|
||||
"serde",
|
||||
@@ -1528,6 +1580,20 @@ version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
||||
|
||||
[[package]]
|
||||
name = "prettytable-rs"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fd04b170004fa2daccf418a7f8253aaf033c27760b5f225889024cf66d7ac2e"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"csv",
|
||||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"term",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.19"
|
||||
@@ -1743,6 +1809,17 @@ dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
|
||||
dependencies = [
|
||||
"getrandom 0.1.16",
|
||||
"redox_syscall 0.1.57",
|
||||
"rust-argon2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.5"
|
||||
@@ -1829,6 +1906,18 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-argon2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
|
||||
dependencies = [
|
||||
"base64 0.13.0",
|
||||
"blake2b_simd",
|
||||
"constant_time_eq",
|
||||
"crossbeam-utils 0.8.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.21"
|
||||
@@ -2178,6 +2267,17 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"dirs",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
@@ -2593,6 +2693,12 @@ dependencies = [
|
||||
"try-lock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
|
||||
@@ -32,6 +32,7 @@ hashbrown = "0.12.*"
|
||||
colored = "2.*"
|
||||
hex = "0.4.*"
|
||||
git2="0.13"
|
||||
prettytable-rs = "0.8"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
is_elevated = "0.1.2"
|
||||
|
||||
@@ -56,6 +56,7 @@ Hayabusaは、日本の[Yamato Security](https://yamatosecurity.connpass.com/)
|
||||
- [コマンドラインオプション](#コマンドラインオプション)
|
||||
- [使用例](#使用例)
|
||||
- [ピボットキーワードの作成](#ピボットキーワードの作成)
|
||||
- [ログオン情報の要約](#ログオン情報の要約)
|
||||
- [サンプルevtxファイルでHayabusaをテストする](#サンプルevtxファイルでhayabusaをテストする)
|
||||
- [Hayabusaの出力](#hayabusaの出力)
|
||||
- [MITRE ATT&CK戦術の省略](#mitre-attck戦術の省略)
|
||||
@@ -327,6 +328,7 @@ USAGE:
|
||||
-U --utc 'UTC形式で日付と時刻を出力する。(デフォルト: 現地時間)'
|
||||
-t --thread-number=[NUMBER] 'スレッド数。(デフォルト: パフォーマンスに最適な数値)'
|
||||
-s --statistics 'イベント ID の統計情報を表示する。'
|
||||
-L --logon-summary 'ユーザのログオン情報の要約を出力'
|
||||
-q --quiet 'Quietモード。起動バナーを表示しない。'
|
||||
-Q --quiet-errors 'Quiet errorsモード。エラーログを保存しない。'
|
||||
--level-tuning <LEVEL_TUNING_FILE> 'ルールlevelのチューニング [default: ./rules/config/level_tuning.txt]'
|
||||
@@ -451,6 +453,10 @@ Processes.Image
|
||||
|
||||
形式は`KeywordName.FieldName`となっています。例えばデフォルトの設定では、`Users`というリストは検知したイベントから`SubjectUserName`、 `TargetUserName` 、 `User`のフィールドの値が一覧として出力されます。hayabusaのデフォルトでは検知したすべてのイベントから結果を出力するため、`--pivot-keyword-list`オプションを使うときには `-m` もしくは `--min-level` オプションを併せて使って検知するイベントのレベルを指定することをおすすめします。まず`-m critical`を指定して、最も高い`critical`レベルのアラートのみを対象として、レベルを必要に応じて下げていくとよいでしょう。結果に正常なイベントにもある共通のキーワードが入っている可能性が高いため、手動で結果を確認してから、不審なイベントにありそうなキーワードリストを1つのファイルに保存し、`grep -f keywords.txt timeline.csv`等のコマンドで不審なアクティビティに絞ったタイムラインを作成することができます。
|
||||
|
||||
## ログオン情報の要約
|
||||
|
||||
`-L` または `--logon-summary` オプションを使うことでログオン情報の要約(ユーザ名、ログイン成功数、ログイン失敗数)の画面出力ができます。`-d` オプションを合わせて使うことでevtxファイルごとのログイン情報の要約を出力できます。
|
||||
|
||||
# サンプルevtxファイルでHayabusaをテストする
|
||||
|
||||
Hayabusaをテストしたり、新しいルールを作成したりするためのサンプルevtxファイルをいくつか提供しています: [https://github.com/Yamato-Security/Hayabusa-sample-evtx](https://github.com/Yamato-Security/Hayabusa-sample-evtx)
|
||||
|
||||
@@ -55,6 +55,7 @@ Hayabusa is a **Windows event log fast forensics timeline generator** and **thre
|
||||
- [Command Line Options](#command-line-options)
|
||||
- [Usage Examples](#usage-examples)
|
||||
- [Pivot Keyword Generator](#pivot-keyword-generator)
|
||||
- [Logon Summary Generator](#logon-summary-generator)
|
||||
- [Testing Hayabusa on Sample Evtx Files](#testing-hayabusa-on-sample-evtx-files)
|
||||
- [Hayabusa Output](#hayabusa-output)
|
||||
- [MITRE ATT&CK Tactics Abbreviations](#mitre-attck-tactics-abbreviations)
|
||||
@@ -319,6 +320,7 @@ USAGE:
|
||||
-U --utc 'Output time in UTC format. (Default: local time)'
|
||||
-t --thread-number=[NUMBER] 'Thread number. (Default: Optimal number for performance.)'
|
||||
-s --statistics 'Prints statistics of event IDs.'
|
||||
-L --logon-summary 'User logon and failed logon 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 the rule level [default: ./rules/config/level_tuning.txt]'
|
||||
@@ -443,6 +445,11 @@ Processes.Image
|
||||
|
||||
The format is `KeywordName.FieldName`. For example, when creating the list of `Users`, hayabusa will list up all the values in the `SubjectUserName`, `TargetUserName` and `User` fields. By default, hayabusa will return results from all events (informational and higher) so we highly recommend combining the `--pivot-keyword-list` option with the `-m` or `--min-level` option. For example, start off with only creating keywords from `critical` alerts with `-m critical` and then continue with `-m high`, `-m medium`, etc... There will most likely be common keywords in your results that will match on many normal events, so after manually checking the results and creating a list of unique keywords in a single file, you can then create a narrowed down timeline of suspicious activity with a command like `grep -f keywords.txt timeline.csv`.
|
||||
|
||||
## Logon Summary Generator
|
||||
|
||||
You can use the `-L` or `--logon-summary` option to output logon information summary(logon username, logon success and logon failed count).
|
||||
You can get logon information each evtx file with `-d` option.
|
||||
|
||||
# Testing Hayabusa on Sample Evtx Files
|
||||
|
||||
We have provided some sample evtx files for you to test hayabusa and/or create new rules at [https://github.com/Yamato-Security/hayabusa-sample-evtx](https://github.com/Yamato-Security/hayabusa-sample-evtx)
|
||||
|
||||
2
rules
2
rules
Submodule rules updated: ea59e261dc...4144fb3ea4
@@ -90,6 +90,7 @@ fn build_app<'a>() -> ArgMatches<'a> {
|
||||
-U --utc 'Output time in UTC format. (Default: local time)'
|
||||
-t --thread-number=[NUMBER] 'Thread number. (Default: Optimal number for performance.)'
|
||||
-s --statistics 'Prints statistics of event IDs.'
|
||||
-L --logon-summary 'User logon and failed logon 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.'
|
||||
|
||||
@@ -121,11 +121,18 @@ 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")
|
||||
{
|
||||
Detection::print_rule_load_info(
|
||||
&rulefile_loader.rulecounter,
|
||||
&parseerror_count,
|
||||
&rulefile_loader.ignorerule_count,
|
||||
);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,11 @@ lazy_static! {
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("statistics");
|
||||
pub static ref LOGONSUMMARY_FLAG: bool = configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("logon-summary");
|
||||
pub static ref TAGS_CONFIG: HashMap<String, String> =
|
||||
Message::create_output_filter_config("config/output_tag.txt");
|
||||
pub static ref CH_CONFIG: HashMap<String, String> =
|
||||
|
||||
@@ -6,3 +6,5 @@ pub mod omikuji;
|
||||
pub mod options;
|
||||
pub mod timeline;
|
||||
pub mod yaml;
|
||||
#[macro_use]
|
||||
extern crate prettytable;
|
||||
|
||||
16
src/main.rs
16
src/main.rs
@@ -13,8 +13,8 @@ use hayabusa::detections::configs::load_pivot_keywords;
|
||||
use hayabusa::detections::detection::{self, EvtxRecordInfo};
|
||||
use hayabusa::detections::pivot::PIVOT_KEYWORD;
|
||||
use hayabusa::detections::print::{
|
||||
AlertMessage, ERROR_LOG_PATH, ERROR_LOG_STACK, PIVOT_KEYWORD_LIST_FLAG, QUIET_ERRORS_FLAG,
|
||||
STATISTICS_FLAG,
|
||||
AlertMessage, ERROR_LOG_PATH, ERROR_LOG_STACK, LOGONSUMMARY_FLAG, PIVOT_KEYWORD_LIST_FLAG,
|
||||
QUIET_ERRORS_FLAG, STATISTICS_FLAG,
|
||||
};
|
||||
use hayabusa::detections::rule::{get_detection_keys, RuleNode};
|
||||
use hayabusa::filter;
|
||||
@@ -176,6 +176,10 @@ impl App {
|
||||
println!("Generating Event ID Statistics");
|
||||
println!();
|
||||
}
|
||||
if *LOGONSUMMARY_FLAG {
|
||||
println!("Generating Logons Summary");
|
||||
println!();
|
||||
}
|
||||
if configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
@@ -229,6 +233,9 @@ impl App {
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("level-tuning")
|
||||
&& std::env::args()
|
||||
.into_iter()
|
||||
.any(|arg| arg.contains("level-tuning"))
|
||||
{
|
||||
let level_tuning_config_path = configs::CONFIG
|
||||
.read()
|
||||
@@ -458,7 +465,7 @@ impl App {
|
||||
pb.inc();
|
||||
}
|
||||
detection.add_aggcondition_msges(&self.rt);
|
||||
if !*STATISTICS_FLAG && !*PIVOT_KEYWORD_LIST_FLAG {
|
||||
if !(*STATISTICS_FLAG || *LOGONSUMMARY_FLAG || *PIVOT_KEYWORD_LIST_FLAG) {
|
||||
after_fact();
|
||||
}
|
||||
}
|
||||
@@ -531,13 +538,14 @@ impl App {
|
||||
// timeline機能の実行
|
||||
tl.start(&records_per_detect);
|
||||
|
||||
if !*STATISTICS_FLAG {
|
||||
if !(*STATISTICS_FLAG || *LOGONSUMMARY_FLAG) {
|
||||
// ruleファイルの検知
|
||||
detection = detection.start(&self.rt, records_per_detect);
|
||||
}
|
||||
}
|
||||
|
||||
tl.tm_stats_dsp_msg();
|
||||
tl.tm_logon_stats_dsp_msg();
|
||||
|
||||
detection
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ pub struct EventStatistics {
|
||||
pub start_time: String,
|
||||
pub end_time: String,
|
||||
pub stats_list: HashMap<String, usize>,
|
||||
pub stats_login_list: HashMap<String, [usize; 2]>,
|
||||
}
|
||||
/**
|
||||
* Windows Event Logの統計情報を出力する
|
||||
@@ -19,6 +20,7 @@ impl EventStatistics {
|
||||
start_time: String,
|
||||
end_time: String,
|
||||
stats_list: HashMap<String, usize>,
|
||||
stats_login_list: HashMap<String, [usize; 2]>,
|
||||
) -> EventStatistics {
|
||||
EventStatistics {
|
||||
total,
|
||||
@@ -26,10 +28,11 @@ impl EventStatistics {
|
||||
start_time,
|
||||
end_time,
|
||||
stats_list,
|
||||
stats_login_list,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&mut self, records: &[EvtxRecordInfo]) {
|
||||
pub fn evt_stats_start(&mut self, records: &[EvtxRecordInfo]) {
|
||||
// 引数でstatisticsオプションが指定されている時だけ、統計情報を出力する。
|
||||
if !configs::CONFIG
|
||||
.read()
|
||||
@@ -49,6 +52,22 @@ impl EventStatistics {
|
||||
self.stats_eventid(records);
|
||||
}
|
||||
|
||||
pub fn logon_stats_start(&mut self, records: &[EvtxRecordInfo]) {
|
||||
// 引数でstatisticsオプションが指定されている時だけ、統計情報を出力する。
|
||||
if !configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("logon-summary")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self.stats_time_cnt(records);
|
||||
|
||||
self.stats_login_eventid(records);
|
||||
}
|
||||
|
||||
fn stats_time_cnt(&mut self, records: &[EvtxRecordInfo]) {
|
||||
if records.is_empty() {
|
||||
return;
|
||||
@@ -93,4 +112,29 @@ impl EventStatistics {
|
||||
}
|
||||
// return evtstat_map;
|
||||
}
|
||||
// Login event
|
||||
fn stats_login_eventid(&mut self, records: &[EvtxRecordInfo]) {
|
||||
for record in records.iter() {
|
||||
let evtid = utils::get_event_value("EventID", &record.record);
|
||||
if evtid.is_none() {
|
||||
continue;
|
||||
}
|
||||
let username = utils::get_event_value("TargetUserName", &record.record);
|
||||
let idnum = evtid.unwrap();
|
||||
let countlist: [usize; 2] = [0, 0];
|
||||
if idnum == 4624 {
|
||||
let count: &mut [usize; 2] = self
|
||||
.stats_login_list
|
||||
.entry(username.unwrap().to_string())
|
||||
.or_insert(countlist);
|
||||
count[0] += 1;
|
||||
} else if idnum == 4625 {
|
||||
let count: &mut [usize; 2] = self
|
||||
.stats_login_list
|
||||
.entry(username.unwrap().to_string())
|
||||
.or_insert(countlist);
|
||||
count[1] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::detections::{configs, detection::EvtxRecordInfo};
|
||||
use prettytable::{Cell, Row, Table};
|
||||
|
||||
use super::statistics::EventStatistics;
|
||||
use hashbrown::HashMap;
|
||||
@@ -21,13 +22,16 @@ impl Timeline {
|
||||
let starttm = "".to_string();
|
||||
let endtm = "".to_string();
|
||||
let statslst = HashMap::new();
|
||||
let statsloginlst = HashMap::new();
|
||||
|
||||
let statistic = EventStatistics::new(totalcnt, filepath, starttm, endtm, statslst);
|
||||
let statistic =
|
||||
EventStatistics::new(totalcnt, filepath, starttm, endtm, statslst, statsloginlst);
|
||||
Timeline { stats: statistic }
|
||||
}
|
||||
|
||||
pub fn start(&mut self, records: &[EvtxRecordInfo]) {
|
||||
self.stats.start(records);
|
||||
self.stats.evt_stats_start(records);
|
||||
self.stats.logon_stats_start(records);
|
||||
}
|
||||
|
||||
pub fn tm_stats_dsp_msg(&mut self) {
|
||||
@@ -64,6 +68,31 @@ impl Timeline {
|
||||
println!("{}", msgprint);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tm_logon_stats_dsp_msg(&mut self) {
|
||||
if !configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("logon-summary")
|
||||
{
|
||||
return;
|
||||
}
|
||||
// 出力メッセージ作成
|
||||
let mut sammsges: Vec<String> = Vec::new();
|
||||
sammsges.push("---------------------------------------".to_string());
|
||||
sammsges.push(format!("Evtx File Path: {}", self.stats.filepath));
|
||||
sammsges.push(format!("Total Event Records: {}\n", self.stats.total));
|
||||
sammsges.push(format!("First Timestamp: {}", self.stats.start_time));
|
||||
sammsges.push(format!("Last Timestamp: {}\n", self.stats.end_time));
|
||||
sammsges.push("---------------------------------------".to_string());
|
||||
for msgprint in sammsges.iter() {
|
||||
println!("{}", msgprint);
|
||||
}
|
||||
|
||||
self.tm_loginstats_tb_set_msg();
|
||||
}
|
||||
|
||||
// イベントID毎の出力メッセージ生成
|
||||
fn tm_stats_set_msg(&self, mapsorted: Vec<(&std::string::String, &usize)>) -> Vec<String> {
|
||||
let mut msges: Vec<String> = Vec::new();
|
||||
@@ -101,4 +130,38 @@ impl Timeline {
|
||||
msges.push("---------------------------------------".to_string());
|
||||
msges
|
||||
}
|
||||
// ユーザ毎のログイン統計情報出力メッセージ生成
|
||||
fn tm_loginstats_tb_set_msg(&self) {
|
||||
println!("Logon Summary");
|
||||
if self.stats.stats_login_list.is_empty() {
|
||||
let mut loginmsges: Vec<String> = Vec::new();
|
||||
loginmsges.push("-----------------------------------------".to_string());
|
||||
loginmsges.push("| No logon events were detected. |".to_string());
|
||||
loginmsges.push("-----------------------------------------\n".to_string());
|
||||
for msgprint in loginmsges.iter() {
|
||||
println!("{}", msgprint);
|
||||
}
|
||||
} else {
|
||||
let mut logins_stats_tb = Table::new();
|
||||
logins_stats_tb.set_titles(row!["User", "Failed", "Successful"]);
|
||||
// 集計件数でソート
|
||||
let mut mapsorted: Vec<_> = self.stats.stats_login_list.iter().collect();
|
||||
mapsorted.sort_by(|x, y| x.0.cmp(y.0));
|
||||
|
||||
for (key, values) in &mapsorted {
|
||||
let mut username: String = key.to_string();
|
||||
//key.to_string().retain(|c| c != '\"');
|
||||
//key.to_string().pop();
|
||||
username.pop();
|
||||
username.remove(0);
|
||||
logins_stats_tb.add_row(Row::new(vec![
|
||||
Cell::new(&username),
|
||||
Cell::new(&values[1].to_string()),
|
||||
Cell::new(&values[0].to_string()),
|
||||
]));
|
||||
}
|
||||
logins_stats_tb.printstd();
|
||||
println!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user