Pivot Keyword List機能の追加 (#412)
* add get_pivot_keyword() func * change function name and call it's function * [WIP] support config file * compilete output * cargo fmt * [WIP] add test * add test * support -o option in pivot * add pivot mod * fix miss * pass test in pivot.rs * add comment * pass all test * add fast return * fix output * add test config file * review * rebase * cargo fmt * test pass * fix clippy in my commit * cargo fmt * little refactor * change file input logic and config format * [WIP] change output * [wip] change deta structure * change output & change data structure * pass test * add config * cargo fmt & clippy & rebase * fix cllipy * delete /rules/ in .gitignore * clean comment * clean * clean * fix rebase miss * fix rebase miss * fix clippy * file name output on -o to stdout * add pivot_keywords.txt to ./config * updated english * Documentation update * cargo fmt and clean * updated translate japanese * readme update * readme update Co-authored-by: DustInDark <nextsasasa@gmail.com> Co-authored-by: Tanaka Zakku <71482215+YamatoSecurity@users.noreply.github.com>
This commit is contained in:
@@ -35,7 +35,7 @@ Hayabusaは、日本の[Yamato Security](https://yamatosecurity.connpass.com/)
|
||||
- [Timeline Explorerでの解析:](#timeline-explorerでの解析)
|
||||
- [Criticalアラートのフィルタリングとコンピュータごとのグルーピング:](#criticalアラートのフィルタリングとコンピュータごとのグルーピング)
|
||||
- [タイムラインのサンプル結果](#タイムラインのサンプル結果)
|
||||
- [特徴](#特徴)
|
||||
- [特徴&機能](#特徴機能)
|
||||
- [予定されている機能](#予定されている機能)
|
||||
- [ダウンロード](#ダウンロード)
|
||||
- [ソースコードからのコンパイル(任意)](#ソースコードからのコンパイル任意)
|
||||
@@ -52,6 +52,7 @@ Hayabusaは、日本の[Yamato Security](https://yamatosecurity.connpass.com/)
|
||||
- [使用方法](#使用方法)
|
||||
- [コマンドラインオプション](#コマンドラインオプション)
|
||||
- [使用例](#使用例)
|
||||
- [ピボットキーワードの作成](#ピボットキーワードの作成)
|
||||
- [サンプルevtxファイルでHayabusaをテストする](#サンプルevtxファイルでhayabusaをテストする)
|
||||
- [Hayabusaの出力](#hayabusaの出力)
|
||||
- [プログレスバー](#プログレスバー)
|
||||
@@ -61,6 +62,8 @@ Hayabusaは、日本の[Yamato Security](https://yamatosecurity.connpass.com/)
|
||||
- [検知ルールのチューニング](#検知ルールのチューニング)
|
||||
- [イベントIDフィルタリング](#イベントidフィルタリング)
|
||||
- [その他のWindowsイベントログ解析ツールおよび関連プロジェクト](#その他のwindowsイベントログ解析ツールおよび関連プロジェクト)
|
||||
- [Windowsイベントログ設定のススメ](#windowsイベントログ設定のススメ)
|
||||
- [Sysmon関係のプロジェクト](#sysmon関係のプロジェクト)
|
||||
- [Sigmaをサポートする他の類似ツールとの比較](#sigmaをサポートする他の類似ツールとの比較)
|
||||
- [コミュニティによるドキュメンテーション](#コミュニティによるドキュメンテーション)
|
||||
- [英語](#英語)
|
||||
@@ -115,7 +118,7 @@ CSVのタイムライン結果のサンプルは[こちら](https://github.com/Y
|
||||
|
||||
CSVのタイムラインをExcelやTimeline Explorerで分析する方法は[こちら](doc/CSV-AnalysisWithExcelAndTimelineExplorer-Japanese.pdf)で紹介しています。
|
||||
|
||||
# 特徴
|
||||
# 特徴&機能
|
||||
|
||||
* クロスプラットフォーム対応: Windows, Linux, macOS。
|
||||
* Rustで開発され、メモリセーフでハヤブサよりも高速です!
|
||||
@@ -127,6 +130,7 @@ CSVのタイムラインをExcelやTimeline Explorerで分析する方法は[こ
|
||||
* イベントログの統計。(どのような種類のイベントがあるのかを把握し、ログ設定のチューニングに有効です。)
|
||||
* 不良ルールやノイズの多いルールを除外するルールチューニング設定が可能です。
|
||||
* MITRE ATT&CKとのマッピング (CSVの出力ファイルのみ)。
|
||||
* イベントログから不審なユーザやファイルを素早く特定するのに有用な、ピボットキーワードの一覧を作成することが可能です。
|
||||
|
||||
# 予定されている機能
|
||||
|
||||
@@ -311,6 +315,7 @@ USAGE:
|
||||
-s --statistics 'イベント ID の統計情報を表示する。'
|
||||
-q --quiet 'Quietモード。起動バナーを表示しない。'
|
||||
-Q --quiet-errors 'Quiet errorsモード。エラーログを保存しない。'
|
||||
-p --pivot-keywords-list 'ピボットキーワードの一覧作成。'
|
||||
--contributors 'コントリビュータの一覧表示。'
|
||||
```
|
||||
|
||||
@@ -376,6 +381,12 @@ hayabusa.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa\default\events\Securi
|
||||
hayabusa.exe -l -m low
|
||||
```
|
||||
|
||||
* criticalレベルのアラートからピボットキーワードの一覧を作成します(結果は結果毎に`keywords-Ip Address.txt`や`keyworss-Users.txt`等に出力されます):
|
||||
|
||||
```bash
|
||||
hayabusa.exe -l -m critical -p -o keywords
|
||||
```
|
||||
|
||||
* イベントIDの統計情報を取得します:
|
||||
|
||||
```bash
|
||||
@@ -403,10 +414,28 @@ Checking target evtx FilePath: "./hayabusa-sample-evtx/YamatoSecurity/T1218.004_
|
||||
5 / 509 [=>------------------------------------------------------------------------------------------------------------------------------------------] 0.98 % 1s
|
||||
```
|
||||
|
||||
* Quiet error mode:
|
||||
* エラーログの出力をさせないようにする:
|
||||
デフォルトでは、Hayabusaはエラーメッセージをエラーログに保存します。
|
||||
エラーメッセージを保存したくない場合は、`-Q`を追加してください。
|
||||
|
||||
## ピボットキーワードの作成
|
||||
|
||||
`-p`もしくは`--pivot-keywords-list`オプションを使うことで不審なユーザやホスト名、プロセスなどを一覧で出力することができ、イベントログから素早く特定することができます。
|
||||
ピボットキーワードのカスタマイズは`config/pivot_keywords.txt`を変更することで行うことができます。以下はデフォルトの設定になります。:
|
||||
|
||||
```
|
||||
Users.SubjectUserName
|
||||
Users.TargetUserName
|
||||
Users.User
|
||||
Logon IDs.SubjectLogonId
|
||||
Logon IDs.TargetLogonId
|
||||
Workstation Names.WorkstationName
|
||||
Ip Addresses.IpAddress
|
||||
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`等のコマンドで不審なアクティビティに絞ったタイムラインを作成することができます。
|
||||
|
||||
# サンプルevtxファイルでHayabusaをテストする
|
||||
|
||||
Hayabusaをテストしたり、新しいルールを作成したりするためのサンプルevtxファイルをいくつか提供しています: [https://github.com/Yamato-Security/Hayabusa-sample-evtx](https://github.com/Yamato-Security/Hayabusa-sample-evtx)
|
||||
@@ -532,6 +561,20 @@ Sigmaルールは、最初にHayabusaルール形式に変換する必要があ
|
||||
* [WELA (Windows Event Log Analyzer)](https://github.com/Yamato-Security/WELA/) - [Yamato Security](https://github.com/Yamato-Security/)によるWindowsイベントログ解析のマルチツール。
|
||||
* [Zircolite](https://github.com/wagga40/Zircolite) - Pythonで書かれたSigmaベースの攻撃検知ツール。
|
||||
|
||||
# Windowsイベントログ設定のススメ
|
||||
|
||||
Windows機での悪性な活動を検知する為には、デフォルトのログ設定を改善することが必要です。
|
||||
以下のサイトを閲覧することをおすすめします。:
|
||||
* [JSCU-NL (Joint Sigint Cyber Unit Netherlands) Logging Essentials](https://github.com/JSCU-NL/logging-essentials)
|
||||
* [ACSC (Australian Cyber Security Centre) Logging and Fowarding Guide](https://www.cyber.gov.au/acsc/view-all-content/publications/windows-event-logging-and-forwarding)
|
||||
* [Malware Archaeology Cheat Sheets](https://www.malwarearchaeology.com/cheat-sheets)
|
||||
|
||||
# Sysmon関係のプロジェクト
|
||||
|
||||
フォレンジックに有用な証拠を作り、高い精度で検知をさせるためには、sysmonをインストールする必要があります。以下のサイトを参考に設定することをおすすめします。:
|
||||
* [Sysmon Modular](https://github.com/olafhartong/sysmon-modular)
|
||||
* [TrustedSec Sysmon Community Guide](https://github.com/trustedsec/SysmonCommunityGuide)
|
||||
|
||||
## Sigmaをサポートする他の類似ツールとの比較
|
||||
|
||||
対象となるサンプルデータ、コマンドラインオプション、ルールのチューニング等によって結果が異なるため、完全な比較はできませんが、ご了承ください。
|
||||
|
||||
44
README.md
44
README.md
@@ -52,6 +52,7 @@ Hayabusa is a **Windows event log fast forensics timeline generator** and **thre
|
||||
- [Usage](#usage)
|
||||
- [Command Line Options](#command-line-options)
|
||||
- [Usage Examples](#usage-examples)
|
||||
- [Pivot Keyword Generator](#pivot-keyword-generator)
|
||||
- [Testing Hayabusa on Sample Evtx Files](#testing-hayabusa-on-sample-evtx-files)
|
||||
- [Hayabusa Output](#hayabusa-output)
|
||||
- [Progress Bar](#progress-bar)
|
||||
@@ -61,6 +62,8 @@ Hayabusa is a **Windows event log fast forensics timeline generator** and **thre
|
||||
- [Detection Rule Tuning](#detection-rule-tuning)
|
||||
- [Event ID Filtering](#event-id-filtering)
|
||||
- [Other Windows Event Log Analyzers and Related Projects](#other-windows-event-log-analyzers-and-related-projects)
|
||||
- [Windows Logging Recommendations](#windows-logging-recommendations)
|
||||
- [Sysmon Related Projects](#sysmon-related-projects)
|
||||
- [Comparison To Other Similar Tools](#comparison-to-other-similar-tools)
|
||||
- [Community Documentation](#community-documentation)
|
||||
- [English](#english)
|
||||
@@ -125,6 +128,7 @@ You can learn how to analyze CSV timelines in Excel and Timeline Explorer [here]
|
||||
* 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).
|
||||
* Create a list of unique pivot keywords to quickly identify abnormal users, hostnames, processes, etc... as well as correlate events.
|
||||
|
||||
# Planned Features
|
||||
|
||||
@@ -305,6 +309,7 @@ USAGE:
|
||||
-s --statistics 'Prints statistics of event IDs.'
|
||||
-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.'
|
||||
```
|
||||
|
||||
@@ -370,7 +375,13 @@ hayabusa.exe -d .\hayabusa-sample-evtx -r .\rules\hayabusa\default\events\Securi
|
||||
hayabusa.exe -l -m low
|
||||
```
|
||||
|
||||
* Get event ID statistics:
|
||||
* 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.exe -l -m critical -p -o keywords
|
||||
```
|
||||
|
||||
* Print Event ID statistics:
|
||||
|
||||
```bash
|
||||
hayabusa.exe -f Security.evtx -s
|
||||
@@ -401,6 +412,24 @@ Checking target evtx FilePath: "./hayabusa-sample-evtx/YamatoSecurity/T1218.004_
|
||||
By default, hayabusa will save error messages to error log files.
|
||||
If you do not want to save error messages, please add `-Q`.
|
||||
|
||||
## Pivot Keyword Generator
|
||||
|
||||
You can use the `-p` or `--pivot-keywords-list` option to create a list of unique pivot keywords to quickly identify abnormal users, hostnames, processes, etc... as well as correlate events. You can customize what keywords you want to search for by editing `config/pivot_keywords.txt`.
|
||||
This is the default setting:
|
||||
|
||||
```
|
||||
Users.SubjectUserName
|
||||
Users.TargetUserName
|
||||
Users.User
|
||||
Logon IDs.SubjectLogonId
|
||||
Logon IDs.TargetLogonId
|
||||
Workstation Names.WorkstationName
|
||||
Ip Addresses.IpAddress
|
||||
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`.
|
||||
|
||||
# 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)
|
||||
@@ -524,6 +553,19 @@ There is no "one tool to rule them all" and we have found that each has its own
|
||||
* [WELA (Windows Event Log Analyzer)](https://github.com/Yamato-Security/WELA) - The swiff-army knife for Windows event logs by [Yamato Security](https://github.com/Yamato-Security/)
|
||||
* [Zircolite](https://github.com/wagga40/Zircolite) - Sigma-based attack detection tool written in Python.
|
||||
|
||||
# Windows Logging Recommendations
|
||||
|
||||
In order to properly detect malicious activity on Windows machines, you will need to improve the default log settings. We recommend the following sites for guidance:
|
||||
* [JSCU-NL (Joint Sigint Cyber Unit Netherlands) Logging Essentials](https://github.com/JSCU-NL/logging-essentials)
|
||||
* [ACSC (Australian Cyber Security Centre) Logging and Fowarding Guide](https://www.cyber.gov.au/acsc/view-all-content/publications/windows-event-logging-and-forwarding)
|
||||
* [Malware Archaeology Cheat Sheets](https://www.malwarearchaeology.com/cheat-sheets)
|
||||
|
||||
# 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)
|
||||
* [TrustedSec Sysmon Community Guide](https://github.com/trustedsec/SysmonCommunityGuide)
|
||||
|
||||
## Comparison To Other Similar Tools
|
||||
|
||||
Please understand that it is not possible to do a perfect comparison as results will differ based on the target sample data, command-line options, rule tuning, etc...
|
||||
|
||||
8
config/pivot_keywords.txt
Normal file
8
config/pivot_keywords.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Users.SubjectUserName
|
||||
Users.TargetUserName
|
||||
Users.User
|
||||
Logon IDs.SubjectLogonId
|
||||
Logon IDs.TargetLogonId
|
||||
Workstation Names.WorkstationName
|
||||
Ip Addresses.IpAddress
|
||||
Processes.Image
|
||||
@@ -1,3 +1,5 @@
|
||||
use crate::detections::pivot::PivotKeyword;
|
||||
use crate::detections::pivot::PIVOT_KEYWORD;
|
||||
use crate::detections::print::AlertMessage;
|
||||
use crate::detections::utils;
|
||||
use chrono::{DateTime, Utc};
|
||||
@@ -67,7 +69,7 @@ fn build_app<'a>() -> ArgMatches<'a> {
|
||||
|
||||
let usages = "-d --directory=[DIRECTORY] 'Directory of multiple .evtx files.'
|
||||
-f --filepath=[FILEPATH] 'File path to one .evtx file.'
|
||||
-r --rules=[RULEFILE/RULEDIRECTORY] 'Rule file or directory. (Default: ./rules)'
|
||||
-r --rules=[RULEDIRECTORY/RULEFILE] 'Rule file or directory (default: ./rules)'
|
||||
-c --color 'Output with color. (Terminal needs to support True Color.)'
|
||||
-C --config=[RULECONFIGDIRECTORY] 'Rule config folder. (Default: ./rules/config)'
|
||||
-o --output=[CSV_TIMELINE] 'Save the timeline in CSV format. (Example: results.csv)'
|
||||
@@ -86,6 +88,7 @@ fn build_app<'a>() -> ArgMatches<'a> {
|
||||
-s --statistics 'Prints statistics of event IDs.'
|
||||
-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!")
|
||||
@@ -268,6 +271,7 @@ impl Default for EventKeyAliasConfig {
|
||||
fn load_eventkey_alias(path: &str) -> EventKeyAliasConfig {
|
||||
let mut config = EventKeyAliasConfig::new();
|
||||
|
||||
// eventkey_aliasが読み込めなかったらエラーで終了とする。
|
||||
let read_result = utils::read_csv(path);
|
||||
if read_result.is_err() {
|
||||
AlertMessage::alert(
|
||||
@@ -277,7 +281,7 @@ fn load_eventkey_alias(path: &str) -> EventKeyAliasConfig {
|
||||
.ok();
|
||||
return config;
|
||||
}
|
||||
// eventkey_aliasが読み込めなかったらエラーで終了とする。
|
||||
|
||||
read_result.unwrap().into_iter().for_each(|line| {
|
||||
if line.len() != 2 {
|
||||
return;
|
||||
@@ -302,6 +306,40 @@ fn load_eventkey_alias(path: &str) -> EventKeyAliasConfig {
|
||||
config
|
||||
}
|
||||
|
||||
///設定ファイルを読み込み、keyとfieldsのマップをPIVOT_KEYWORD大域変数にロードする。
|
||||
pub fn load_pivot_keywords(path: &str) {
|
||||
let read_result = utils::read_txt(path);
|
||||
if read_result.is_err() {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
read_result.as_ref().unwrap_err(),
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
|
||||
read_result.unwrap().into_iter().for_each(|line| {
|
||||
let map: Vec<&str> = line.split('.').collect();
|
||||
if map.len() != 2 {
|
||||
return;
|
||||
}
|
||||
|
||||
//存在しなければ、keyを作成
|
||||
PIVOT_KEYWORD
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry(map[0].to_string())
|
||||
.or_insert(PivotKeyword::new());
|
||||
|
||||
PIVOT_KEYWORD
|
||||
.write()
|
||||
.unwrap()
|
||||
.get_mut(&map[0].to_string())
|
||||
.unwrap()
|
||||
.fields
|
||||
.insert(map[1].to_string());
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EventInfo {
|
||||
pub evttitle: String,
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
extern crate csv;
|
||||
|
||||
use crate::detections::configs;
|
||||
use crate::detections::pivot::insert_pivot_keyword;
|
||||
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::rule;
|
||||
@@ -177,6 +179,12 @@ impl Detection {
|
||||
if !result {
|
||||
continue;
|
||||
}
|
||||
|
||||
if *PIVOT_KEYWORD_LIST_FLAG {
|
||||
insert_pivot_keyword(&record_info.record);
|
||||
continue;
|
||||
}
|
||||
|
||||
// aggregation conditionが存在しない場合はそのまま出力対応を行う
|
||||
if !agg_condition {
|
||||
Detection::insert_message(&rule, record_info);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod configs;
|
||||
pub mod detection;
|
||||
pub mod pivot;
|
||||
pub mod print;
|
||||
pub mod rule;
|
||||
pub mod utils;
|
||||
|
||||
270
src/detections/pivot.rs
Normal file
270
src/detections/pivot.rs
Normal file
@@ -0,0 +1,270 @@
|
||||
use hashbrown::HashMap;
|
||||
use hashbrown::HashSet;
|
||||
use lazy_static::lazy_static;
|
||||
use serde_json::Value;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use crate::detections::configs;
|
||||
use crate::detections::utils::get_serde_number_to_string;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PivotKeyword {
|
||||
pub keywords: HashSet<String>,
|
||||
pub fields: HashSet<String>,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref PIVOT_KEYWORD: RwLock<HashMap<String, PivotKeyword>> =
|
||||
RwLock::new(HashMap::new());
|
||||
}
|
||||
|
||||
impl Default for PivotKeyword {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl PivotKeyword {
|
||||
pub fn new() -> PivotKeyword {
|
||||
PivotKeyword {
|
||||
keywords: HashSet::new(),
|
||||
fields: HashSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///levelがlowより大きいレコードの場合、keywordがrecord内にみつかれば、
|
||||
///それをPIVOT_KEYWORD.keywordsに入れる。
|
||||
pub fn insert_pivot_keyword(event_record: &Value) {
|
||||
//levelがlow異常なら続ける
|
||||
let mut is_exist_event_key = false;
|
||||
let mut tmp_event_record: &Value = event_record;
|
||||
for s in ["Event", "System", "Level"] {
|
||||
if let Some(record) = tmp_event_record.get(s) {
|
||||
is_exist_event_key = true;
|
||||
tmp_event_record = record;
|
||||
}
|
||||
}
|
||||
if is_exist_event_key {
|
||||
let hash_value = get_serde_number_to_string(tmp_event_record);
|
||||
|
||||
if hash_value.is_some() && hash_value.as_ref().unwrap() == "infomational"
|
||||
|| hash_value.as_ref().unwrap() == "undefined"
|
||||
|| hash_value.as_ref().unwrap() == "-"
|
||||
{
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
for (_, pivot) in PIVOT_KEYWORD.write().unwrap().iter_mut() {
|
||||
for field in &pivot.fields {
|
||||
if let Some(array_str) = configs::EVENTKEY_ALIAS.get_event_key(&String::from(field)) {
|
||||
let split: Vec<&str> = array_str.split('.').collect();
|
||||
let mut is_exist_event_key = false;
|
||||
let mut tmp_event_record: &Value = event_record;
|
||||
for s in split {
|
||||
if let Some(record) = tmp_event_record.get(s) {
|
||||
is_exist_event_key = true;
|
||||
tmp_event_record = record;
|
||||
}
|
||||
}
|
||||
if is_exist_event_key {
|
||||
let hash_value = get_serde_number_to_string(tmp_event_record);
|
||||
|
||||
if let Some(value) = hash_value {
|
||||
if value == "-" || value == "127.0.0.1" || value == "::1" {
|
||||
continue;
|
||||
}
|
||||
pivot.keywords.insert(value);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::detections::configs::load_pivot_keywords;
|
||||
use crate::detections::pivot::insert_pivot_keyword;
|
||||
use crate::detections::pivot::PIVOT_KEYWORD;
|
||||
use serde_json;
|
||||
|
||||
//PIVOT_KEYWORDはグローバルなので、他の関数の影響も考慮する必要がある。
|
||||
#[test]
|
||||
fn insert_pivot_keyword_local_ip4() {
|
||||
load_pivot_keywords("test_files/config/pivot_keywords.txt");
|
||||
let record_json_str = r#"
|
||||
{
|
||||
"Event": {
|
||||
"System": {
|
||||
"Level": "high"
|
||||
},
|
||||
"EventData": {
|
||||
"IpAddress": "127.0.0.1"
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
insert_pivot_keyword(&serde_json::from_str(record_json_str).unwrap());
|
||||
|
||||
assert!(!PIVOT_KEYWORD
|
||||
.write()
|
||||
.unwrap()
|
||||
.get_mut("Ip Addresses")
|
||||
.unwrap()
|
||||
.keywords
|
||||
.contains("127.0.0.1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_pivot_keyword_ip4() {
|
||||
load_pivot_keywords("test_files/config/pivot_keywords.txt");
|
||||
let record_json_str = r#"
|
||||
{
|
||||
"Event": {
|
||||
"System": {
|
||||
"Level": "high"
|
||||
},
|
||||
"EventData": {
|
||||
"IpAddress": "10.0.0.1"
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
insert_pivot_keyword(&serde_json::from_str(record_json_str).unwrap());
|
||||
|
||||
assert!(PIVOT_KEYWORD
|
||||
.write()
|
||||
.unwrap()
|
||||
.get_mut("Ip Addresses")
|
||||
.unwrap()
|
||||
.keywords
|
||||
.contains("10.0.0.1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_pivot_keyword_ip_empty() {
|
||||
load_pivot_keywords("test_files/config/pivot_keywords.txt");
|
||||
let record_json_str = r#"
|
||||
{
|
||||
"Event": {
|
||||
"System": {
|
||||
"Level": "high"
|
||||
},
|
||||
"EventData": {
|
||||
"IpAddress": "-"
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
insert_pivot_keyword(&serde_json::from_str(record_json_str).unwrap());
|
||||
|
||||
assert!(!PIVOT_KEYWORD
|
||||
.write()
|
||||
.unwrap()
|
||||
.get_mut("Ip Addresses")
|
||||
.unwrap()
|
||||
.keywords
|
||||
.contains("-"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_pivot_keyword_local_ip6() {
|
||||
load_pivot_keywords("test_files/config/pivot_keywords.txt");
|
||||
let record_json_str = r#"
|
||||
{
|
||||
"Event": {
|
||||
"System": {
|
||||
"Level": "high"
|
||||
},
|
||||
"EventData": {
|
||||
"IpAddress": "::1"
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
insert_pivot_keyword(&serde_json::from_str(record_json_str).unwrap());
|
||||
|
||||
assert!(!PIVOT_KEYWORD
|
||||
.write()
|
||||
.unwrap()
|
||||
.get_mut("Ip Addresses")
|
||||
.unwrap()
|
||||
.keywords
|
||||
.contains("::1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_pivot_keyword_level_infomational() {
|
||||
load_pivot_keywords("test_files/config/pivot_keywords.txt");
|
||||
let record_json_str = r#"
|
||||
{
|
||||
"Event": {
|
||||
"System": {
|
||||
"Level": "infomational"
|
||||
},
|
||||
"EventData": {
|
||||
"IpAddress": "10.0.0.2"
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
insert_pivot_keyword(&serde_json::from_str(record_json_str).unwrap());
|
||||
|
||||
assert!(!PIVOT_KEYWORD
|
||||
.write()
|
||||
.unwrap()
|
||||
.get_mut("Ip Addresses")
|
||||
.unwrap()
|
||||
.keywords
|
||||
.contains("10.0.0.2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_pivot_keyword_level_low() {
|
||||
load_pivot_keywords("test_files/config/pivot_keywords.txt");
|
||||
let record_json_str = r#"
|
||||
{
|
||||
"Event": {
|
||||
"System": {
|
||||
"Level": "low"
|
||||
},
|
||||
"EventData": {
|
||||
"IpAddress": "10.0.0.1"
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
insert_pivot_keyword(&serde_json::from_str(record_json_str).unwrap());
|
||||
|
||||
assert!(PIVOT_KEYWORD
|
||||
.write()
|
||||
.unwrap()
|
||||
.get_mut("Ip Addresses")
|
||||
.unwrap()
|
||||
.keywords
|
||||
.contains("10.0.0.1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_pivot_keyword_level_none() {
|
||||
load_pivot_keywords("test_files/config/pivot_keywords.txt");
|
||||
let record_json_str = r#"
|
||||
{
|
||||
"Event": {
|
||||
"System": {
|
||||
"Level": "-"
|
||||
},
|
||||
"EventData": {
|
||||
"IpAddress": "10.0.0.3"
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
insert_pivot_keyword(&serde_json::from_str(record_json_str).unwrap());
|
||||
|
||||
assert!(!PIVOT_KEYWORD
|
||||
.write()
|
||||
.unwrap()
|
||||
.get_mut("Ip Addresses")
|
||||
.unwrap()
|
||||
.keywords
|
||||
.contains("10.0.0.3"));
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,11 @@ lazy_static! {
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("statistics");
|
||||
pub static ref PIVOT_KEYWORD_LIST_FLAG: bool = configs::CONFIG
|
||||
.read()
|
||||
.unwrap()
|
||||
.args
|
||||
.is_present("pivot-keywords-list");
|
||||
}
|
||||
|
||||
impl Default for Message {
|
||||
|
||||
@@ -87,7 +87,7 @@ pub fn read_csv(filename: &str) -> Result<Vec<Vec<String>>, String> {
|
||||
return Result::Err(e.to_string());
|
||||
}
|
||||
|
||||
let mut rdr = csv::Reader::from_reader(contents.as_bytes());
|
||||
let mut rdr = csv::ReaderBuilder::new().from_reader(contents.as_bytes());
|
||||
rdr.records().for_each(|r| {
|
||||
if r.is_err() {
|
||||
return;
|
||||
|
||||
@@ -65,7 +65,7 @@ impl RuleExclude {
|
||||
ERROR_LOG_STACK
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(format!("[WARN] {} does not exist", filename));
|
||||
.push(format!("{} does not exist", filename));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
82
src/main.rs
82
src/main.rs
@@ -9,9 +9,12 @@ use chrono::{DateTime, Datelike, Local, TimeZone};
|
||||
use evtx::{EvtxParser, ParserSettings};
|
||||
use git2::Repository;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
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, QUIET_ERRORS_FLAG, STATISTICS_FLAG,
|
||||
AlertMessage, ERROR_LOG_PATH, ERROR_LOG_STACK, PIVOT_KEYWORD_LIST_FLAG, QUIET_ERRORS_FLAG,
|
||||
STATISTICS_FLAG,
|
||||
};
|
||||
use hayabusa::detections::rule::{get_detection_keys, RuleNode};
|
||||
use hayabusa::filter;
|
||||
@@ -26,7 +29,7 @@ use std::cmp::Ordering;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::Display;
|
||||
use std::fs::create_dir;
|
||||
use std::io::BufWriter;
|
||||
use std::io::{BufWriter, Write};
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::time::SystemTime;
|
||||
@@ -71,6 +74,10 @@ impl App {
|
||||
}
|
||||
|
||||
fn exec(&mut self) {
|
||||
if *PIVOT_KEYWORD_LIST_FLAG {
|
||||
load_pivot_keywords("config/pivot_keywords.txt");
|
||||
}
|
||||
|
||||
let analysis_start_time: DateTime<Local> = Local::now();
|
||||
if !configs::CONFIG.read().unwrap().args.is_present("quiet") {
|
||||
self.output_logo();
|
||||
@@ -118,6 +125,20 @@ impl App {
|
||||
return;
|
||||
}
|
||||
if let Some(csv_path) = configs::CONFIG.read().unwrap().args.value_of("output") {
|
||||
for (key, _) in PIVOT_KEYWORD.read().unwrap().iter() {
|
||||
let keywords_file_name = csv_path.to_owned() + "-" + key + ".txt";
|
||||
if Path::new(&keywords_file_name).exists() {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
&format!(
|
||||
" The file {} already exists. Please specify a different filename.",
|
||||
&keywords_file_name
|
||||
),
|
||||
)
|
||||
.ok();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if Path::new(csv_path).exists() {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
@@ -130,6 +151,7 @@ impl App {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if *STATISTICS_FLAG {
|
||||
println!("Generating Event ID Statistics");
|
||||
println!();
|
||||
@@ -193,6 +215,60 @@ impl App {
|
||||
if ERROR_LOG_STACK.lock().unwrap().len() > 0 {
|
||||
AlertMessage::create_error_log(ERROR_LOG_PATH.to_string());
|
||||
}
|
||||
|
||||
if *PIVOT_KEYWORD_LIST_FLAG {
|
||||
//ファイル出力の場合
|
||||
if let Some(pivot_file) = configs::CONFIG.read().unwrap().args.value_of("output") {
|
||||
for (key, pivot_keyword) in PIVOT_KEYWORD.read().unwrap().iter() {
|
||||
let mut f = BufWriter::new(
|
||||
fs::File::create(pivot_file.to_owned() + "-" + key + ".txt").unwrap(),
|
||||
);
|
||||
let mut output = "".to_string();
|
||||
output += &format!("{}: ", key).to_string();
|
||||
|
||||
output += "( ";
|
||||
for i in pivot_keyword.fields.iter() {
|
||||
output += &format!("%{}% ", i).to_string();
|
||||
}
|
||||
output += "):";
|
||||
output += "\n";
|
||||
|
||||
for i in pivot_keyword.keywords.iter() {
|
||||
output += &format!("{}\n", i).to_string();
|
||||
}
|
||||
|
||||
f.write_all(output.as_bytes()).unwrap();
|
||||
}
|
||||
|
||||
//output to stdout
|
||||
let mut output =
|
||||
"Pivot keyword results saved to the following files:\n".to_string();
|
||||
for (key, _) in PIVOT_KEYWORD.read().unwrap().iter() {
|
||||
output += &(pivot_file.to_owned() + "-" + key + ".txt" + "\n");
|
||||
}
|
||||
println!("{}", output);
|
||||
} else {
|
||||
//標準出力の場合
|
||||
let mut output = "The following pivot keywords were found:\n".to_string();
|
||||
for (key, pivot_keyword) in PIVOT_KEYWORD.read().unwrap().iter() {
|
||||
output += &format!("{}: ", key).to_string();
|
||||
|
||||
output += "( ";
|
||||
for i in pivot_keyword.fields.iter() {
|
||||
output += &format!("%{}% ", i).to_string();
|
||||
}
|
||||
output += "):";
|
||||
output += "\n";
|
||||
|
||||
for i in pivot_keyword.keywords.iter() {
|
||||
output += &format!("{}\n", i).to_string();
|
||||
}
|
||||
|
||||
output += "\n";
|
||||
}
|
||||
print!("{}", output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
@@ -327,7 +403,7 @@ impl App {
|
||||
pb.inc();
|
||||
}
|
||||
detection.add_aggcondition_msges(&self.rt);
|
||||
if !*STATISTICS_FLAG {
|
||||
if !*STATISTICS_FLAG && !*PIVOT_KEYWORD_LIST_FLAG {
|
||||
after_fact();
|
||||
}
|
||||
}
|
||||
|
||||
8
test_files/config/pivot_keywords.txt
Normal file
8
test_files/config/pivot_keywords.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Users.SubjectUserName
|
||||
Users.TargetUserName
|
||||
Users.User
|
||||
Logon IDs.SubjectLogonId
|
||||
Logon IDs.TargetLogonId
|
||||
Workstation Names.WorkstationName
|
||||
Ip Addresses.IpAddress
|
||||
Processes.Image
|
||||
Reference in New Issue
Block a user