Feature/#140 document (#144)
* update * fix regexes and whitelist * underconstructing * fix * update * add pic * update * update * update * fix
This commit is contained in:
84
Cargo.lock
generated
84
Cargo.lock
generated
@@ -19,9 +19,9 @@ checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.13"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86"
|
||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -87,12 +87,6 @@ dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
@@ -902,6 +896,31 @@ dependencies = [
|
||||
"winapi-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lagotto"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"chrono",
|
||||
"clap",
|
||||
"csv",
|
||||
"dotenv",
|
||||
"evtx",
|
||||
"flate2",
|
||||
"lazy_static",
|
||||
"linked-hash-map",
|
||||
"mopa",
|
||||
"num_cpus",
|
||||
"quick-xml 0.17.2",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"slack-hook",
|
||||
"tokio 1.3.0",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@@ -961,9 +980,9 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
@@ -1543,14 +1562,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.3.9"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
"thread_local",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1564,9 +1582,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.18"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
@@ -1583,7 +1601,7 @@ version = "0.9.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f88643aea3c1343c804950d7bf983bd2067f5ab59db6d613a08e05572f2714ab"
|
||||
dependencies = [
|
||||
"base64 0.10.1",
|
||||
"base64",
|
||||
"bytes 0.4.12",
|
||||
"cookie",
|
||||
"cookie_store",
|
||||
@@ -1962,15 +1980,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
@@ -2392,31 +2401,6 @@ dependencies = [
|
||||
"winapi-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yamato_event_analyzer"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64 0.12.3",
|
||||
"chrono",
|
||||
"clap",
|
||||
"csv",
|
||||
"dotenv",
|
||||
"evtx",
|
||||
"flate2",
|
||||
"lazy_static",
|
||||
"linked-hash-map",
|
||||
"mopa",
|
||||
"num_cpus",
|
||||
"quick-xml 0.17.2",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"slack-hook",
|
||||
"tokio 1.3.0",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.4"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "yamato_event_analyzer"
|
||||
version = "0.1.0"
|
||||
name = "lagotto"
|
||||
version = "1.0.0"
|
||||
authors = ["akiranishikawa <nishikawa@kagosec.net>"]
|
||||
edition = "2018"
|
||||
|
||||
@@ -13,7 +13,7 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0"}
|
||||
serde_derive = "1.0"
|
||||
clap = "*"
|
||||
regex = "1"
|
||||
regex = "1.5.4"
|
||||
csv = "1.1"
|
||||
base64 = "*"
|
||||
flate2 = "1.0"
|
||||
|
||||
69
README.md
69
README.md
@@ -1,4 +1,69 @@
|
||||
# YamatoEventAnalyzer
|
||||
Yea! (Yamato Event Analyzer). Aiming to be the world's greatest Windows event log analysis tool!
|
||||
# Lagotto
|
||||
Aiming to be the world's greatest Windows event log analysis tool!
|
||||
|
||||
世界一のWindowsイベントログ解析ツールを目指しています!
|
||||
|
||||
|
||||
# Platforms
|
||||
Lagottoは下記のプラットフォーム上で実行できます。
|
||||
* Windows
|
||||
* Linux
|
||||
* macOS
|
||||
|
||||
# Downloads
|
||||
[Releases](https://github.com/Yamato-Security/YamatoEventAnalyzer/releases)からコンパイル済みの実行ファイルをダウンロードできます。
|
||||
|
||||
# Usage
|
||||
## Commnad line option
|
||||
````
|
||||
USAGE:
|
||||
lagotto.exe [FLAGS] [OPTIONS]
|
||||
|
||||
FLAGS:
|
||||
--credits Prints credits
|
||||
-h, --help Prints help information
|
||||
--rfc-2822 Output date and time in RFC 2822 format. Example: Mon, 07 Aug 2006 12:34:56 -0600
|
||||
--slack Slack notification
|
||||
-s, --statistics Prints statistics for event logs
|
||||
-u, --utc Output time in UTC format(default: local time)
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
--csv-timeline <CSV_TIMELINE> Csv output timeline
|
||||
-d, --directory <DIRECTORY> Event log files directory
|
||||
-f, --filepath <FILEPATH> Event file path
|
||||
--human-readable-timeline <HUMAN_READABLE_TIMELINE> Human readable timeline
|
||||
-l, --lang <LANG> Output language
|
||||
-t, --threadnum <NUM> Thread number
|
||||
````
|
||||
|
||||
## Usage examples
|
||||
* Windowsイベントログを一つ指定する
|
||||
````
|
||||
lagotto.exe --filepath=eventlog.evtx
|
||||
````
|
||||
|
||||
* Windowsイベントログが格納されているフォルダを指定する
|
||||
````
|
||||
lagotto.exe --directory=.\evtx
|
||||
````
|
||||
|
||||
* 結果をCSVファイルに出力する。
|
||||
````
|
||||
lagotto.exe --directory=.\evtx --csv-timeline lagotto.csv
|
||||
````
|
||||
|
||||
# Rule files
|
||||
LagottoではWindowsEventログを検知するルールをYAML形式で定義します。
|
||||
|
||||
ルールの記載方法については[RULEFILE.md](./doc/RULEFILE.md)を参照してください。
|
||||
|
||||
ルールファイルはrulesフォルダ内に設置します。
|
||||
rulesフォルダには組み込みルールファイルも設置されていますので、参考にしてください。
|
||||
|
||||
# How to compile from source files
|
||||
下記のコマンドでビルドできます。
|
||||
|
||||
````
|
||||
cargo build
|
||||
````
|
||||
315
doc/RULEFILE.md
Normal file
315
doc/RULEFILE.md
Normal file
@@ -0,0 +1,315 @@
|
||||
# ルールファイル
|
||||
LagottoはWindowsEventログの検知ルールをYAML形式のファイルに記載します。
|
||||
単なる文字列一致だけでなく、正規表現やANDやOR等の条件を組み合わせることができ、複雑な検知ルールも表現できるようになっています。
|
||||
ここではその検知ルールの書き方について説明します。
|
||||
|
||||
# ルールファイルのフォーマット
|
||||
ルールファイルのフォーマットは下記の通りです。
|
||||
|
||||
``````
|
||||
title: PowerShell Execution Pipeline
|
||||
description: This rule detect powershell execution pipeline.
|
||||
author: Zach Mathis
|
||||
detection:
|
||||
selection:
|
||||
Event.System.EventID: 7040
|
||||
Event.System.Channel: System
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: medium
|
||||
output: 'command=%CommandLine%'
|
||||
creation_date: 2020/11/8
|
||||
updated_date: 2020/11/8
|
||||
``````
|
||||
|
||||
* title [required]: ルールファイルのタイトルを入力します。
|
||||
* description [optional]: ルールファイルの説明を入力します。
|
||||
* author [optional]: ルールファイルの作者を入力します。
|
||||
* detection [required]: 検知ルールを入力します。
|
||||
* falsepositives [optional]: 誤検知に関する情報を入力します。
|
||||
* level [optional]: リスクレベルを入力します。指定する値は`informational`,`low`,`medium`,`high`,`critical`のいづれかです。
|
||||
* output [required]: イベントログが検知した場合に表示されるメッセージを入力します。
|
||||
* creation_date [optional]: ルールファイルの作成日を入力します。
|
||||
* updated_date [optional]: ルールファイルの更新日を入力します。
|
||||
|
||||
# detectionの記法について
|
||||
## detectionの基本
|
||||
まず、detectionの基本的な書き方について説明します。
|
||||
|
||||
### AND条件とOR条件の書き方
|
||||
AND条件を記載する場合はYAMLのハッシュを用いて記載します。
|
||||
下記のようにdetectionを記載すると、以下`両方の条件を満たす`イベントログを検知します。
|
||||
* EventIDが`7040`に完全一致する
|
||||
* Channelが`System`に完全一致する
|
||||
|
||||
``````
|
||||
detection:
|
||||
selection:
|
||||
Event.System.EventID: 7040
|
||||
Event.System.Channel: System
|
||||
``````
|
||||
|
||||
OR条件を記載する場合は配列を利用します。
|
||||
下記のようにdetectionを記載すると、以下`いずれの条件を満たす`イベントログを検知します。
|
||||
* EventIDが`7040`に完全一致する
|
||||
* Channelが`System`に完全一致する
|
||||
|
||||
``````
|
||||
detection:
|
||||
selection:
|
||||
- Event.System.EventID: 7040
|
||||
- Event.System.Channel: System
|
||||
``````
|
||||
|
||||
また、下記のように記載することもできます。
|
||||
この場合、以下`両方の条件を満たす`イベントログを検知します。
|
||||
* EventIDが`7040`又は`7041`に完全一致する
|
||||
* Channelが`System`に完全一致する
|
||||
|
||||
``````
|
||||
detection:
|
||||
selection:
|
||||
Event.System.EventID:
|
||||
- 7040
|
||||
- 7041
|
||||
Event.System.Channel: System
|
||||
``````
|
||||
|
||||
### eventkey
|
||||
WindowsイベントログをXML形式で一部抜粋で出力すると、下記のようになります。ルールファイルの例に含まれる`Event.System.Channel`は、XMLの`<Event><System><Channel>System<Channel><System></Event>`を指しています。今回の例のように、XML形式のWindowsイベントログについて、入れ子になったXMLのタグに含まれる値をルールの条件に指定する場合、`.`でつなげて指定します。ルールファイルでは、この`.`でつなげた文字列をeventkeyと呼んでいます。
|
||||
|
||||
``````
|
||||
<Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'>
|
||||
<System>
|
||||
<EventID>7040</EventID>
|
||||
<Channel>System</Channel>
|
||||
</System>
|
||||
<EventData>
|
||||
<Data Name='param1'>Background Intelligent Transfer Service</Data>
|
||||
<Data Name='param2'>自動的な開始</Data>
|
||||
</EventData>
|
||||
</Event>
|
||||
``````
|
||||
|
||||
### eventkeyのalias
|
||||
`.`でつなげたeventkeyは長い文字列になってしまうことがあるため、Lagottoではeventkeyに対するエイリアスを使用できます。エイリアスは`config\eventkey_alias.txt`というファイルに定義されています。ファイルはCSV形式であり、aliasとevent_keyという列から構成されています。aliasにはエイリアスを定義し、event_keyには`.`でつなげたeventkeyを指定します。このエイリアスを用いると、最初に例に挙げたdetectionは以下のように書き換えることができます。
|
||||
|
||||
``````
|
||||
detection:
|
||||
selection:
|
||||
EventID: 7040
|
||||
Channel: System
|
||||
``````
|
||||
### XMLの属性(attribute)をルールの条件にする方法
|
||||
WindowsEventログをXML形式で出力すると、XMLの属性に値が設定されている場合もあります。下記の例だと、ProviderタグのNameがXMLの属性です。
|
||||
|
||||
````````````
|
||||
<Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'>
|
||||
<System>
|
||||
<Provider Name='Microsoft-Windows-Security-Auditing' Guid='{54849625-5478-4994-a5ba-3e3b0328c30d}'/>
|
||||
<EventID>4672</EventID>
|
||||
<EventRecordID>607469</EventRecordID>
|
||||
<Channel>Security</Channel>
|
||||
<Security />
|
||||
</System>
|
||||
</Event>
|
||||
````````````
|
||||
|
||||
XMLの属性をeventkeyで指定するには、{eventkey}_attributes.{attribute_name}という形式で指定します。例えば、ProviderタグのName属性をルールファイルに指定する場合、下記のようになります。
|
||||
|
||||
``````
|
||||
detection:
|
||||
selection:
|
||||
Channel: Security
|
||||
EventID: 4672
|
||||
Event.System.Provider_attributes.Name: 'Microsoft-Windows-Security-Auditing'
|
||||
``````
|
||||
|
||||
### EventData
|
||||
WindowsEventログをXML形式で出力すると、EventDataというタグが使用されている場合があります。(EventDataタグは様々なEventIDのログで頻繁に利用されます。)このEventDataにネストされたタグの名前は全て`Data`となっており、ここまで説明してきたeventkeyではSubjectUserSidやSubjectUserNameを区別することができません。
|
||||
````````````
|
||||
<Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'>
|
||||
<System>
|
||||
<EventID>5379</EventID>
|
||||
<TimeCreated SystemTime='2021-10-20T10:16:18.7782563Z' />
|
||||
<EventRecordID>607469</EventRecordID>
|
||||
<Channel>Security</Channel>
|
||||
<Security />
|
||||
</System>
|
||||
<EventData>
|
||||
<Data Name='SubjectUserSid'>S-1-1-11-1111111111-111111111-1111111111-1111</Data>
|
||||
<Data Name='SubjectUserName'>lagotto</Data>
|
||||
<Data Name='SubjectDomainName'>DESKTOP-LAGOTTO</Data>
|
||||
<Data Name='SubjectLogonId'>0x11111111</Data>
|
||||
</EventData>
|
||||
</Event>
|
||||
````````````
|
||||
|
||||
この問題に対応するため、eventkeyで`Data`の代わりに`Name`に指定されている値をeventkeyに指定できるようになっています。例えば、EventData内のSubjectUserNameとSubjectDomainNameをルールの条件とする場合、下記のように記載します。
|
||||
|
||||
``````
|
||||
detection:
|
||||
selection:
|
||||
EventID: 7040
|
||||
Channel: System
|
||||
Event.EventData.SubjectUserName: lagotto
|
||||
Event.EventData.SubjectDomainName: DESKTOP-LAGOTTO
|
||||
``````
|
||||
|
||||
### EventDataの特殊なパターン
|
||||
EventDataというタグにネストされたタグの中には、Name属性が存在しないタグもあります。
|
||||
``````
|
||||
<Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'>
|
||||
<System>
|
||||
<EventID>5379</EventID>
|
||||
<Channel>Security</Channel>
|
||||
<Security />
|
||||
</System>
|
||||
<EventData>
|
||||
<Data>Available</Data>
|
||||
<Data>None</Data>
|
||||
<Data>NewEngineState=Available PreviousEngineState=None SequenceNumber=9 HostName=ConsoleHost HostVersion=2.0 HostId=5cbb33bf-acf7-47cc-9242-141cd0ba9f0c EngineVersion=2.0 RunspaceId=c6e94dca-0daf-418c-860a-f751a9f2cbe1 PipelineId= CommandName= CommandType= ScriptName= CommandPath= CommandLine=</Data>
|
||||
</EventData>
|
||||
</Event>
|
||||
``````
|
||||
|
||||
上記のようなイベントログを検知する場合、`EventData`というeventkeyを指定します。この場合、EventDataタグにネストされているName属性の指定されてないタグのうち、いずれか一つでも一致するタグがあれば、条件に一致したものとして処理されます。
|
||||
``````
|
||||
detection:
|
||||
selection:
|
||||
Channel: Security
|
||||
EventID: 5379
|
||||
EventData: None
|
||||
``````
|
||||
|
||||
## パイプ
|
||||
eventkeyにはパイプを指定することができます。ここまで説明した書き方では完全一致しか表現できませんでしたが、パイプを使うことでより柔軟な検知ルールを記載できるようになります。下記の例ではCommandLineの値が`yamato.*lagotto`という正規表現にマッチする場合、条件に一致したものとして処理されます。
|
||||
|
||||
``````
|
||||
detection:
|
||||
selection:
|
||||
EventID: 7040
|
||||
Channel: System
|
||||
CommandLine|re: yamato.*lagotto
|
||||
``````
|
||||
|
||||
使用できるパイプの一覧です。なお、v1.0.0時点では、複数のパイプをつなげて使用することはできません。
|
||||
* startswith: 先頭一致
|
||||
* endswith: 後方一致
|
||||
* contains: 部分一致
|
||||
* re: 正規表現(正規表現の処理にはregexクレートを使用しています。正規表現の詳細記法についてはhttps://docs.rs/regex/1.5.4/regex/を参照してください。)
|
||||
|
||||
## ワイルドカード
|
||||
eventkeyに対応する値にはワイルドカードを指定することができます。下記の例ではCommandLineがlagottoという文字列で始まっていれば、条件に一致したものとして処理されます。基本的にはsigmaルールのwildcardと同じ仕様になっています。
|
||||
|
||||
``````
|
||||
detection:
|
||||
selection:
|
||||
EventID: 7040
|
||||
Channel: System
|
||||
CommandLine: lagotto*
|
||||
``````
|
||||
|
||||
使用できるワイルドカードの一覧です。
|
||||
* `*`: 任意の0文字以上の文字列にマッチします。(内部的には`.*`という正規表現に変換されます。)
|
||||
* `?`: 任意の1文字にマッチします。(内部的には`.`という正規表現に変換されます。)
|
||||
|
||||
ワイルドカードを使用する場合、下記のルールに則って解釈されます。
|
||||
* ワイルドカード(`*`と`?`)をエスケープするにはバックスラッシュ(`/`)を使用します。
|
||||
* ワイルドカードの直前に文字としてのバックスラッシュ(`/`)を使用する場合、`\\*`又は`\\?`と記載してください。
|
||||
* バックスラッシュを単体で使う分にはエスケープ不要です。
|
||||
|
||||
## eventkeyにネストできるキーワード
|
||||
eventkeyには特定のキーワードをネストさせることができます。下記の例ではCommandLineの値が`aa*bb`というワイルドカードにマッチした上で文字列長が10以上である場合、条件に一致したものと処理されます。
|
||||
|
||||
``````
|
||||
detection:
|
||||
selection:
|
||||
EventID: 7040
|
||||
Channel: System
|
||||
CommandLine:
|
||||
value: aa*bb
|
||||
min_length: 10
|
||||
``````
|
||||
|
||||
現状では下記のキーワードを指定できます。
|
||||
* value: 文字列による一致(ワイルドカードやパイプを指定することもできます)
|
||||
* min_length: 指定した文字数以上である場合、条件に一致したものとして処理されます。
|
||||
* regexes: 指定したファイルに記載された正規表現のリストにひとつでも一致すれば、`条件に一致した`ものとして処理されます。
|
||||
* whitelist: 指定したファイルに記載された正規表現のリストにひとつでも一致すれば、`条件に一致していない`ものとして処理されます。
|
||||
|
||||
### regexes.txtとwhitelist.txt
|
||||
lagottoではregexesやwhitelistを使用した組み込みのルールを用意しており、それらのルールはregexes.txtとwhitelist.txtを参照しています。regexes.txtとwhitelist.txtを書き換えることで、参照する全てのルールの挙動を一度に変更することが可能です。
|
||||
|
||||
また、regexesやwhitelistに指定するファイルは、ユーザーが独自に作成することも可能です。作成する場合、regexes.txtとwhitelist.txtを参考にしてください。
|
||||
|
||||
## condition
|
||||
これまでの記法を用いると、AND条件やOR条件を表現することができますが、ANDやOR等が複雑に入り組んだ条件を定義することは難しい場合があります。その場合、conditionというキーワードを使用することで、複雑な条件式を定義することができます。
|
||||
|
||||
``````
|
||||
detection:
|
||||
selection_1:
|
||||
EventID: 7040
|
||||
selection_2:
|
||||
EventID: 7041
|
||||
selection_3:
|
||||
Channel: System
|
||||
selection_4:
|
||||
CommandLine|contains: lsass.exe
|
||||
selection_5:
|
||||
CommandLine|contains: services.exe
|
||||
selection_6:
|
||||
ParentProcessName|contains: wininit.exe
|
||||
|
||||
condition: ( selection_1 or selection_2 ) and selection_3 and ( selection_4 or selection_5 ) and ( not selection_6 )
|
||||
``````
|
||||
|
||||
conditionには以下のキーワードを使用することができます。
|
||||
* {expression1} and {expression2}: {expression1}と{expression2}のAND条件を表します。
|
||||
* {expression1} or {expression2}: {expression1}と{expression2}のOR条件を表します。
|
||||
* not {expression}: {expression}の条件式の真偽を逆転させます。
|
||||
* ( {expression} ) : {expression}の条件式を優先して評価します。数学等で現れる括弧と同じです。
|
||||
|
||||
なお、上記の例では、条件式をグルーピングするためにselection_1やselection_2といった名前を使用していますが、selectionというprefixを付ける必要はなく、ユーザーが任意の名前を定義できます。ただし、使用可能な文字は`\w`という正規表現にマッチする文字のみです。
|
||||
|
||||
## aggregation condition
|
||||
上記で説明したconditionには、andやor条件だけでなく、検知したイベントログを集計するような機能も実装されています。この機能をaggregation conditionと呼んでおり、conditionの後にパイプでつなげて指定します。下記の例では、DestinationIpの値が3種類以上あるかどうかをSubjectUserName毎に判定する条件式になります。
|
||||
|
||||
``````
|
||||
detection:
|
||||
selection:
|
||||
EventID: 7040
|
||||
Channel: System
|
||||
condition: selection | count(DestinationIp) by SubjectUserName >= 3
|
||||
``````
|
||||
|
||||
aggregation conditionは下記の形式で定義できます。なお、{number}には数値を指定します。
|
||||
* `count() {operator} {number}`: conditionのパイプ以前の条件に一致したログについて、一致したログ数が{operator}と{number}で指定した条件式を満たす場合に、条件に一致したものとして処理されます。
|
||||
|
||||

|
||||
|
||||
* `count() by {eventkey_2} {operator} {number}`: conditionのパイプ以前の条件に一致したログを{eventkey_2}毎にグルーピングし、グルーピング毎に一致したログ数が{operator}と{number}で指定した条件式を満たす場合に、条件に一致したものとして処理されます。
|
||||
|
||||

|
||||
|
||||
* `count({eventkey}) {operator} {number}`: conditionのパイプ以前の条件に一致したログについて、{eventkey}の値が何種類存在するか数えます。その数が{operator}と{number}で指定した条件式を満たす場合に、条件に一致したものとして処理されます。
|
||||
|
||||

|
||||
|
||||
* `count({eventkey_1}) by {eventkey_2} {operator} {number}`: conditionのパイプ以前の条件に一致したログを{eventkey_2}毎にグルーピングし、そのグループ毎に{eventkey_1}の値が何種類存在するか数えます。そのグルーピング毎に数えた値が{operator}と{number}で指定した条件式を満たす場合に、条件に一致したものとして処理されます。
|
||||
|
||||

|
||||
|
||||
また、上記のoperatorには下記を指定できます。
|
||||
* `==`: 指定された値と等しい場合、条件に一致したものと処理されます。
|
||||
* `>=`: 指定された値以上である場合、条件に一致したものと処理されます。
|
||||
* `>`: 指定された値より大きい場合、条件に一致したものと処理されます。
|
||||
* `<=`: 指定された値以下である場合、条件に一致したものと処理されます。
|
||||
* `<`: 指定された値より小さい場合、条件に一致したものと処理されます。
|
||||
|
||||
# outputの記法
|
||||
detectionの条件に一致した場合に、出力されるメッセージを指定できます。固定の文字列が出力できる他、eventkeyを%で囲むことにより、検知したログの値を表示することも可能です。下記の例では検知した際のメッセージにScriptBlockTextというeventkeyの値を使用しています。
|
||||
|
||||
``````
|
||||
output: 'command=%ScriptBlockText%'
|
||||
``````
|
||||
BIN
doc/count1.png
Normal file
BIN
doc/count1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
BIN
doc/count2.png
Normal file
BIN
doc/count2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
BIN
doc/count3.png
Normal file
BIN
doc/count3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
BIN
doc/count4.png
Normal file
BIN
doc/count4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
36
regexes.txt
36
regexes.txt
@@ -1,19 +1,17 @@
|
||||
Type,regex,string
|
||||
0,^cmd.exe /c echo [a-z]{6} > \\\\.\\pipe\\[a-z]{6}$,Metasploit-style cmd with pipe (possible use of Meterpreter 'getsystem')
|
||||
0,^%SYSTEMROOT%\\[a-zA-Z]{8}\.exe$,Metasploit-style %SYSTEMROOT% image path (possible use of Metasploit 'Native upload' exploit payload)
|
||||
0,powershell.*FromBase64String.*IO.Compression.GzipStream,Metasploit-style base64 encoded/compressed PowerShell function (possible use of Metasploit PowerShell exploit payload)
|
||||
0,DownloadString\(.http,Download via Net.WebClient DownloadString
|
||||
0,mimikatz,Command referencing Mimikatz
|
||||
0,Invoke-Mimikatz.ps,PowerSploit Invoke-Mimikatz.ps1
|
||||
0,PowerSploit.*ps1,Use of PowerSploit
|
||||
0,User-Agent,User-Agent set via command line
|
||||
0,[a-zA-Z0-9/+=]{500},500+ consecutive Base64 characters
|
||||
0,powershell.exe.*Hidden.*Enc,Base64 encoded and hidden PowerShell command
|
||||
# Generic csc.exe alert, comment out if experiencing false positives
|
||||
0,\\csc\.exe,Use of C Sharp compiler csc.exe
|
||||
0,\\csc\.exe.*\\Appdata\\Local\\Temp\\[a-z0-9]{8}\.cmdline,PSAttack-style command via csc.exe
|
||||
# Generic cvtres.exe alert, comment out if experiencing false positives
|
||||
0,\\cvtres\.exe.*,Resource File To COFF Object Conversion Utility cvtres.exe
|
||||
0,\\cvtres\.exe.*\\AppData\\Local\\Temp\\[A-Z0-9]{7}\.tmp,PSAttack-style command via cvtres.exe
|
||||
1,^[a-zA-Z]{22}$,Metasploit-style service name: 22 characters, [A-Za-z]
|
||||
1,^[a-zA-Z]{16}$,Metasploit-style service name: 16 characters, [A-Za-z]
|
||||
^cmd.exe /c echo [a-z]{6} > \\\\.\\pipe\\[a-z]{6}$
|
||||
^%SYSTEMROOT%\\[a-zA-Z]{8}\.exe$
|
||||
powershell.*FromBase64String.*IO.Compression.GzipStream
|
||||
DownloadString\(.http
|
||||
mimikatz
|
||||
Invoke-Mimikatz.ps
|
||||
PowerSploit.*ps1
|
||||
User-Agent
|
||||
[a-zA-Z0-9/+=]{500}
|
||||
powershell.exe.*Hidden.*Enc
|
||||
\\csc\.exe
|
||||
\\csc\.exe.*\\Appdata\\Local\\Temp\\[a-z0-9]{8}\.cmdline
|
||||
# Generic cvtres.exe alert
|
||||
\\cvtres\.exe.*
|
||||
\\cvtres\.exe.*\\AppData\\Local\\Temp\\[A-Z0-9]{7}\.tmp
|
||||
^[a-zA-Z]{22}$
|
||||
^[a-zA-Z]{16}$
|
||||
@@ -1,15 +1,12 @@
|
||||
title: BitsJob
|
||||
description: Adversaries may abuse BITS jobs to persistently execute or clean up after malicious payloads.
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Microsoft-Windows-Bits-Client/Operational
|
||||
EventID: 59
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: medium
|
||||
output: 'Started bits job created. JobTitle:%JobTitle% URL:%Url%'
|
||||
creation_date: 2021/7/15
|
||||
updated_date: 2021/7/15
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: PowerShell Execution Pipeline
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Microsoft-Windows-PowerShell/Operational
|
||||
@@ -13,7 +11,6 @@ detection:
|
||||
# condition: selection
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: medium
|
||||
output: 'command=%CommandLine%'
|
||||
creation_date: 2020/11/8
|
||||
updated_date: 2020/11/8
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: PowerShell Execution Remote Command
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Microsoft-Windows-PowerShell/Operational
|
||||
@@ -12,7 +10,6 @@ detection:
|
||||
# condition: selection
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: medium
|
||||
output: 'command=%ScriptBlockText%'
|
||||
creation_date: 2020/11/8
|
||||
updated_date: 2020/11/8
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: The Audit log file was cleared
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Security
|
||||
@@ -10,7 +8,6 @@ detection:
|
||||
# condition: selection
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: medium
|
||||
output: |
|
||||
Audit Log Clear
|
||||
The Audit log was cleared.
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: Sensitive Privilede Use (Mimikatz)
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Security
|
||||
@@ -10,7 +8,6 @@ detection:
|
||||
# condition: selection | count(EventID) > 4
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: medium
|
||||
output: |
|
||||
Sensitive Privilege Use Exceeds Threshold
|
||||
Potentially indicative of Mimikatz, multiple sensitive priviledge calls have been made.
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: An Operation was attempted on a privileged object
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Security
|
||||
@@ -12,7 +10,6 @@ detection:
|
||||
# condition: selection
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: medium
|
||||
output: |
|
||||
Possible Hidden Service Attempt
|
||||
User requested to modify the Dynamic Access Control (DAC) permissions of a service, possibly to hide it from view.
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: Command Line Logging
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Security
|
||||
@@ -11,7 +9,6 @@ detection:
|
||||
# condition: selection
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: medium
|
||||
output: 'CommandLine:%CommandLine% ParentProcessName:%ParentProcessName%'
|
||||
creation_date: 2020/11/8
|
||||
updated_date: 2020/11/8
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: A user account was created.
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Security
|
||||
@@ -10,7 +8,6 @@ detection:
|
||||
# condition: selection
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: low
|
||||
output: 'New User Created UserName:%TargetUserName% SID:%TargetSid%'
|
||||
creation_date: 2020/11/8
|
||||
updated_date: 2020/11/8
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: A member was added to a security-enabled global group.
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Security
|
||||
@@ -11,7 +9,6 @@ detection:
|
||||
# condition: selection
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: low
|
||||
output: 'user added to global Administrators UserName: %MemberName% SID: %MemberSid%'
|
||||
creation_date: 2020/11/8
|
||||
updated_date: 2020/11/8
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: A member was added to a security-enabled local group.
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Security
|
||||
@@ -11,7 +9,6 @@ detection:
|
||||
# condition: selection
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: low
|
||||
output: 'user added to local Administrators UserName: %MemberName% SID: %MemberSid%'
|
||||
creation_date: 2020/11/8
|
||||
updated_date: 2020/11/8
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: A member was added to a security-enabled universal group.
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Security
|
||||
@@ -11,7 +9,6 @@ detection:
|
||||
# condition: selection
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: low
|
||||
output: 'user added to universal Administrators UserName: %MemberName% SID: %MemberSid%'
|
||||
creation_date: 2020/11/8
|
||||
updated_date: 2020/11/8
|
||||
|
||||
@@ -2,8 +2,6 @@ title: An account failed to log on
|
||||
description: hogehoge
|
||||
ignore: true
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Security
|
||||
@@ -11,7 +9,6 @@ detection:
|
||||
# condition: selection | count(TargetUserName) > 3
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: medium
|
||||
output: 'High number of logon failures for one account UserName:%event_data.SubjectUserName% Total logon faiures:%count%'
|
||||
creation_date: 2020/11/8
|
||||
updated_date: 2020/11/8
|
||||
|
||||
@@ -2,8 +2,6 @@ title: An account failed to log on
|
||||
description: hogehoge
|
||||
ignore: true
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Security
|
||||
@@ -11,7 +9,6 @@ detection:
|
||||
# condition: selection | count(TargetUserName) > 3
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: High
|
||||
output: 'Distributed Account Explicit Credential Use (Password Spray Attack)¥n The use of multiple user account access attempts with explicit credentials is ¥nan indicator of a password spray attack.¥nTarget Usernames:%TargetUserName$¥nAccessing Username: %SubjectUserName%¥nAccessing Host Name: %SubjectDomainName%'
|
||||
creation_date: 2020/11/8
|
||||
updated_date: 2020/11/8
|
||||
|
||||
@@ -2,8 +2,6 @@ title: Command Line Logging
|
||||
description: hogehoge
|
||||
ignore: true
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Security
|
||||
@@ -12,7 +10,6 @@ detection:
|
||||
# condition: selection
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: medium
|
||||
output: 'CommandLine:%CommandLine% ParentProcessName:%ParentProcessName%'
|
||||
creation_date: 2020/11/8
|
||||
updated_date: 2020/11/8
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: Sysmon Check command lines
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Sysmon
|
||||
@@ -11,7 +9,6 @@ detection:
|
||||
# condition: selection
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: medium
|
||||
output: 'CommandLine=%CommandLine%¥nParentImage=%ParentImage%'
|
||||
creation_date: 2020/11/8
|
||||
uodated_date: 2020/11/8
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: Check for unsigned EXEs/DLLs
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Sysmon
|
||||
@@ -11,7 +9,6 @@ detection:
|
||||
# condition: selection
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: low
|
||||
output: 'Message: Unsigned Image(DLL)¥n Result : Loaded by: %event_data.Image%¥nCommand : %event_data.ImageLoaded%'
|
||||
creation_date: 2020/11/8
|
||||
uodated_date: 2020/11/8
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: The System log file was cleared
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: System
|
||||
@@ -10,7 +8,6 @@ detection:
|
||||
# condition: selection
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: medium
|
||||
output: 'System Log Clear¥nThe System log was cleared.'
|
||||
creation_date: 2020/11/8
|
||||
uodated_date: 2020/11/8
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: This service may not function properly
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: System
|
||||
@@ -12,7 +10,6 @@ detection:
|
||||
# condition: selection
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: low
|
||||
output: 'Interactive service warning¥nService name: %ServiceName%¥nMalware (and some third party software) trigger this warning'
|
||||
creation_date: 2020/11/8
|
||||
uodated_date: 2020/11/8
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: The ... service entered the stopped|running state
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: System
|
||||
@@ -12,7 +10,6 @@ detection:
|
||||
condition: selection
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: low
|
||||
output: 'Suspicious Service Name¥nService name: %ServiceName%'
|
||||
creation_date: 2020/11/8
|
||||
uodated_date: 2020/11/8
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: The start type of the Windows Event Log service was changed from auto start to disabled
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: System
|
||||
@@ -14,7 +12,6 @@ detection:
|
||||
condition: selection
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: low
|
||||
output: 'Service name : %param1%¥nMessage : Event Log Service Stopped¥nResults: Selective event log manipulation may follow this event.'
|
||||
creation_date: 2020/11/8
|
||||
uodated_date: 2020/11/8
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: A service was installed in the system
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: System
|
||||
@@ -15,7 +13,6 @@ detection:
|
||||
condition: selection
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: low
|
||||
output: 'New Service Created¥n%ImagePath¥nService name: %ServiceName%'
|
||||
creation_date: 2020/11/8
|
||||
uodated_date: 2020/11/8
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: AS-REP Roasting
|
||||
description: For each account found without preauthentication, an adversary may send an AS-REQ message without the encrypted timestamp and receive an AS-REP message with TGT data which may be encrypted with an insecure algorithm such as RC4.
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Security
|
||||
@@ -11,7 +9,6 @@ detection:
|
||||
PreAuthType: 0
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: medium
|
||||
output: 'Detected AS-REP Roasting Risk Actvity.'
|
||||
creation_date: 2021/4/31
|
||||
updated_date: 2021/4/31
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: Kerberoasting
|
||||
description: Adversaries may abuse a valid Kerberos ticket-granting ticket (TGT) or sniff network traffic to obtain a ticket-granting service (TGS) ticket that may be vulnerable to Brute Force.
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Security
|
||||
@@ -11,7 +9,6 @@ detection:
|
||||
PreAuthType: 2
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: medium
|
||||
output: 'Detected Kerberoasting Risk Activity.'
|
||||
creation_date: 2021/4/31
|
||||
updated_date: 2021/4/31
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
title: PowerShell DownGradeAttack
|
||||
description: hogehoge
|
||||
author: Yea
|
||||
logsource:
|
||||
product: windows
|
||||
detection:
|
||||
selection:
|
||||
Channel: Windows PowerShell
|
||||
@@ -10,7 +8,6 @@ detection:
|
||||
EventData|re: '[\s\S]*EngineVersion=2\.0[\s\S]*'
|
||||
falsepositives:
|
||||
- unknown
|
||||
level: medium
|
||||
output: 'Powershell DownGrade Attack Detected!!'
|
||||
creation_date: 2020/11/8
|
||||
updated_date: 2020/11/8
|
||||
|
||||
@@ -39,22 +39,20 @@ fn build_app<'a>() -> ArgMatches<'a> {
|
||||
}
|
||||
|
||||
App::new(&program)
|
||||
.about("Yea! (Yamato Event Analyzer). Aiming to be the world's greatest Windows event log analysis tool!")
|
||||
.version("0.0.1")
|
||||
.author("Author name <author@example.com>")
|
||||
.about("Lagotto. Aiming to be the world's greatest Windows event log analysis tool!")
|
||||
.version("1.0.0")
|
||||
.author("Author name Yamato-Security(https://github.com/Yamato-Security/YamatoEventAnalyzer)")
|
||||
.setting(AppSettings::VersionlessSubcommands)
|
||||
.arg(Arg::from_usage("-f --filepath=[FILEPATH] 'event file path'"))
|
||||
.arg(Arg::from_usage("--attackhunt=[ATTACK_HUNT] 'Attack Hunt'"))
|
||||
.arg(Arg::from_usage("--csv-timeline=[CSV_TIMELINE] 'csv output timeline'"))
|
||||
.arg(Arg::from_usage("--human-readable-timeline=[HUMAN_READABLE_TIMELINE] 'human readable timeline'"))
|
||||
.arg(Arg::from_usage("--rfc-2822 'output date and time in RFC 2822 format. Example: Mon, 07 Aug 2006 12:34:56 -0600'"))
|
||||
.arg(Arg::from_usage("-l --lang=[LANG] 'output language'"))
|
||||
.arg(Arg::from_usage("-u --utc 'output time in UTC format(default: local time)'"))
|
||||
.arg(Arg::from_usage("-d --directory=[DIRECTORY] 'event log files directory'"))
|
||||
.arg(Arg::from_usage("-s --statistics 'event statistics'"))
|
||||
.arg(Arg::from_usage("-t --threadnum=[NUM] 'thread number'"))
|
||||
.arg(Arg::from_usage("--slack 'slack notification'"))
|
||||
.arg(Arg::from_usage("--credits 'Zachary Mathis, Akira Nishikawa'"))
|
||||
.arg(Arg::from_usage("-f --filepath=[FILEPATH] 'Event file path'"))
|
||||
.arg(Arg::from_usage("--csv-timeline=[CSV_TIMELINE] 'Csv output timeline'"))
|
||||
.arg(Arg::from_usage("--rfc-2822 'Output date and time in RFC 2822 format. Example: Mon, 07 Aug 2006 12:34:56 -0600'"))
|
||||
.arg(Arg::from_usage("-l --lang=[LANG] 'Output language'"))
|
||||
.arg(Arg::from_usage("-u --utc 'Output time in UTC format(default: local time)'"))
|
||||
.arg(Arg::from_usage("-d --directory=[DIRECTORY] 'Event log files directory'"))
|
||||
.arg(Arg::from_usage("-s --statistics 'Prints statistics for event logs'"))
|
||||
.arg(Arg::from_usage("-t --threadnum=[NUM] 'Thread number'"))
|
||||
.arg(Arg::from_usage("--slack 'Slack notification'"))
|
||||
.arg(Arg::from_usage("--credits 'Prints credits'"))
|
||||
.get_matches()
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ impl LeafMatcher for MinlengthMatcher {
|
||||
/// 正規表現のリストが記載されたファイルを読み取って、比較するロジックを表すクラス
|
||||
/// DeepBlueCLIのcheck_cmdメソッドの一部に同様の処理が実装されていた。
|
||||
pub struct RegexesFileMatcher {
|
||||
regexes_csv_content: Vec<Vec<String>>,
|
||||
regexes_csv_content: Vec<String>,
|
||||
}
|
||||
|
||||
impl RegexesFileMatcher {
|
||||
@@ -107,15 +107,11 @@ impl LeafMatcher for RegexesFileMatcher {
|
||||
return Result::Err(vec![errmsg]);
|
||||
}
|
||||
|
||||
let csv_content = utils::read_csv(&value.unwrap());
|
||||
if csv_content.is_err() {
|
||||
let errmsg = format!(
|
||||
"cannot read regexes file. [key:{}]",
|
||||
utils::concat_selection_key(key_list)
|
||||
);
|
||||
return Result::Err(vec![errmsg]);
|
||||
let regexes_csv_content = utils::read_txt(&value.unwrap());
|
||||
if regexes_csv_content.is_err() {
|
||||
return Result::Err(vec![regexes_csv_content.unwrap_err()]);
|
||||
}
|
||||
self.regexes_csv_content = csv_content.unwrap();
|
||||
self.regexes_csv_content = regexes_csv_content.unwrap();
|
||||
|
||||
return Result::Ok(());
|
||||
}
|
||||
@@ -123,10 +119,8 @@ impl LeafMatcher for RegexesFileMatcher {
|
||||
fn is_match(&self, event_value: Option<&Value>) -> bool {
|
||||
//TODO Wildcardの場合、CaseInsensitiveなので、ToLowerする。
|
||||
return match event_value.unwrap_or(&Value::Null) {
|
||||
Value::String(s) => !utils::check_regex(s, 0, &self.regexes_csv_content).is_empty(),
|
||||
Value::Number(n) => {
|
||||
!utils::check_regex(&n.to_string(), 0, &self.regexes_csv_content).is_empty()
|
||||
}
|
||||
Value::String(s) => !utils::check_regex(s, &self.regexes_csv_content),
|
||||
Value::Number(n) => !utils::check_regex(&n.to_string(), &self.regexes_csv_content),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
@@ -135,7 +129,7 @@ impl LeafMatcher for RegexesFileMatcher {
|
||||
/// ファイルに列挙された文字列に一致する場合に検知するロジックを表す
|
||||
/// DeepBlueCLIのcheck_cmdメソッドの一部に同様の処理が実装されていた。
|
||||
pub struct WhitelistFileMatcher {
|
||||
whitelist_csv_content: Vec<Vec<String>>,
|
||||
whitelist_csv_content: Vec<String>,
|
||||
}
|
||||
|
||||
impl WhitelistFileMatcher {
|
||||
@@ -170,15 +164,11 @@ impl LeafMatcher for WhitelistFileMatcher {
|
||||
return Result::Err(vec![errmsg]);
|
||||
}
|
||||
|
||||
let csv_content = utils::read_csv(&value.unwrap());
|
||||
if csv_content.is_err() {
|
||||
let errmsg = format!(
|
||||
"cannot read whitelist file. [key:{}]",
|
||||
utils::concat_selection_key(key_list)
|
||||
);
|
||||
return Result::Err(vec![errmsg]);
|
||||
let whitelist_content = utils::read_txt(&value.unwrap());
|
||||
if whitelist_content.is_err() {
|
||||
return Result::Err(vec![whitelist_content.unwrap_err()]);
|
||||
}
|
||||
self.whitelist_csv_content = csv_content.unwrap();
|
||||
self.whitelist_csv_content = whitelist_content.unwrap();
|
||||
|
||||
return Result::Ok(());
|
||||
}
|
||||
@@ -662,28 +652,16 @@ mod tests {
|
||||
|
||||
// regexes.txtの中身と一致していることを確認
|
||||
let csvcontent = &ancestor_matcher.regexes_csv_content;
|
||||
assert_eq!(csvcontent.len(), 14);
|
||||
|
||||
let firstcontent = &csvcontent[0];
|
||||
assert_eq!(firstcontent.len(), 3);
|
||||
assert_eq!(firstcontent[0], "0");
|
||||
assert_eq!(csvcontent.len(), 17);
|
||||
assert_eq!(
|
||||
firstcontent[1],
|
||||
csvcontent[0],
|
||||
r"^cmd.exe /c echo [a-z]{6} > \\\\.\\pipe\\[a-z]{6}$"
|
||||
);
|
||||
assert_eq!(
|
||||
firstcontent[2],
|
||||
r"Metasploit-style cmd with pipe (possible use of Meterpreter 'getsystem')"
|
||||
);
|
||||
|
||||
let lastcontent = &csvcontent[13];
|
||||
assert_eq!(lastcontent.len(), 3);
|
||||
assert_eq!(lastcontent[0], "0");
|
||||
assert_eq!(
|
||||
lastcontent[1],
|
||||
csvcontent[14],
|
||||
r"\\cvtres\.exe.*\\AppData\\Local\\Temp\\[A-Z0-9]{7}\.tmp"
|
||||
);
|
||||
assert_eq!(lastcontent[2], r"PSAttack-style command via cvtres.exe");
|
||||
}
|
||||
|
||||
// whitelist.txtが読み込めることを確認
|
||||
@@ -704,11 +682,11 @@ mod tests {
|
||||
assert_eq!(csvcontent.len(), 2);
|
||||
|
||||
assert_eq!(
|
||||
csvcontent[0][0],
|
||||
csvcontent[0],
|
||||
r#"^"C:\\Program Files\\Google\\Chrome\\Application\\chrome\.exe""#.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
csvcontent[1][0],
|
||||
csvcontent[1],
|
||||
r#"^"C:\\Program Files\\Google\\Update\\GoogleUpdate\.exe""#.to_string()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use regex::Regex;
|
||||
use serde_json::Value;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::str;
|
||||
use std::string::String;
|
||||
|
||||
@@ -23,50 +24,30 @@ pub fn concat_selection_key(key_list: &Vec<String>) -> String {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn check_regex(
|
||||
string: &str,
|
||||
r#type: usize,
|
||||
regex_list: &Vec<Vec<String>>,
|
||||
) -> std::string::String {
|
||||
let empty = "".to_string();
|
||||
let mut regextext = "".to_string();
|
||||
pub fn check_regex(string: &str, regex_list: &Vec<String>) -> bool {
|
||||
for line in regex_list {
|
||||
let type_str = line.get(0).unwrap_or(&empty);
|
||||
if type_str != &r#type.to_string() {
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let regex_str = line.get(1).unwrap_or(&empty);
|
||||
if regex_str.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let re = Regex::new(regex_str);
|
||||
let re = Regex::new(line);
|
||||
if re.is_err() || re.unwrap().is_match(string) == false {
|
||||
continue;
|
||||
}
|
||||
|
||||
let text = line.get(2).unwrap_or(&empty);
|
||||
if text.is_empty() {
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
|
||||
regextext.push_str(text);
|
||||
regextext.push_str("\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return regextext;
|
||||
}
|
||||
|
||||
pub fn check_whitelist(target: &str, whitelist: &Vec<Vec<String>>) -> bool {
|
||||
let empty = "".to_string();
|
||||
pub fn check_whitelist(target: &str, whitelist: &Vec<String>) -> bool {
|
||||
for line in whitelist {
|
||||
let r_str = line.get(0).unwrap_or(&empty);
|
||||
if r_str.is_empty() {
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let r = Regex::new(r_str);
|
||||
let r = Regex::new(line);
|
||||
if r.is_ok() && r.unwrap().is_match(target) {
|
||||
return true;
|
||||
}
|
||||
@@ -75,6 +56,21 @@ pub fn check_whitelist(target: &str, whitelist: &Vec<Vec<String>>) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn read_txt(filename: &str) -> Result<Vec<String>, String> {
|
||||
let f = File::open(filename);
|
||||
if f.is_err() {
|
||||
let errmsg = format!("cannot open file. [file:{}]", filename);
|
||||
return Result::Err(errmsg);
|
||||
}
|
||||
let reader = BufReader::new(f.unwrap());
|
||||
return Result::Ok(
|
||||
reader
|
||||
.lines()
|
||||
.map(|line| line.unwrap_or(String::default()))
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn read_csv(filename: &str) -> Result<Vec<Vec<String>>, String> {
|
||||
let mut f = File::open(filename).expect("file not found!!!");
|
||||
let mut contents: String = String::new();
|
||||
@@ -165,18 +161,18 @@ mod tests {
|
||||
use crate::detections::utils;
|
||||
#[test]
|
||||
fn test_check_regex() {
|
||||
let regexes = utils::read_csv("regexes.txt").unwrap();
|
||||
let regextext = utils::check_regex("\\cvtres.exe", 0, ®exes);
|
||||
assert!(regextext == "Resource File To COFF Object Conversion Utility cvtres.exe\n");
|
||||
let regexes = utils::read_txt("regexes.txt").unwrap();
|
||||
let regextext = utils::check_regex("\\cvtres.exe", ®exes);
|
||||
assert!(regextext == true);
|
||||
|
||||
let regextext = utils::check_regex("\\hogehoge.exe", 0, ®exes);
|
||||
assert!(regextext == "");
|
||||
let regextext = utils::check_regex("\\hogehoge.exe", ®exes);
|
||||
assert!(regextext == false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_whitelist() {
|
||||
let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate.exe\"";
|
||||
let whitelist = utils::read_csv("whitelist.txt").unwrap();
|
||||
let whitelist = utils::read_txt("whitelist.txt").unwrap();
|
||||
assert!(true == utils::check_whitelist(commandline, &whitelist));
|
||||
|
||||
let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate2.exe\"";
|
||||
|
||||
12
src/main.rs
12
src/main.rs
@@ -2,17 +2,17 @@ extern crate serde;
|
||||
extern crate serde_derive;
|
||||
|
||||
use evtx::{EvtxParser, ParserSettings};
|
||||
use lagotto::detections::detection;
|
||||
use lagotto::detections::detection::EvtxRecordInfo;
|
||||
use lagotto::detections::print::AlertMessage;
|
||||
use lagotto::omikuji::Omikuji;
|
||||
use lagotto::{afterfact::after_fact, detections::utils};
|
||||
use lagotto::{detections::configs, timeline::timeline::Timeline};
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
path::PathBuf,
|
||||
vec,
|
||||
};
|
||||
use yamato_event_analyzer::detections::detection;
|
||||
use yamato_event_analyzer::detections::detection::EvtxRecordInfo;
|
||||
use yamato_event_analyzer::detections::print::AlertMessage;
|
||||
use yamato_event_analyzer::omikuji::Omikuji;
|
||||
use yamato_event_analyzer::{afterfact::after_fact, detections::utils};
|
||||
use yamato_event_analyzer::{detections::configs, timeline::timeline::Timeline};
|
||||
|
||||
// 一度にtimelineやdetectionを実行する行数
|
||||
const MAX_DETECT_RECORDS: usize = 40000;
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
regex
|
||||
^"C:\\Program Files\\Google\\Chrome\\Application\\chrome\.exe"
|
||||
^"C:\\Program Files\\Google\\Update\\GoogleUpdate\.exe"
|
||||
|
||||
Reference in New Issue
Block a user