Feature/#140 document (#144)

* update

* fix regexes and whitelist

* underconstructing

* fix

* update

* add pic

* update

* update

* update

* fix
This commit is contained in:
James
2021-10-22 00:43:40 +09:00
committed by GitHub
parent 23c60fa8ff
commit 4a1e46e47e
38 changed files with 502 additions and 241 deletions

84
Cargo.lock generated
View File

@@ -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"

View File

@@ -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"

View File

@@ -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
View 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}で指定した条件式を満たす場合に、条件に一致したものとして処理されます。
![](count1.png)
* `count() by {eventkey_2} {operator} {number}`: conditionのパイプ以前の条件に一致したログを{eventkey_2}毎にグルーピングし、グルーピング毎に一致したログ数が{operator}と{number}で指定した条件式を満たす場合に、条件に一致したものとして処理されます。
![](count2.png)
* `count({eventkey}) {operator} {number}`: conditionのパイプ以前の条件に一致したログについて、{eventkey}の値が何種類存在するか数えます。その数が{operator}と{number}で指定した条件式を満たす場合に、条件に一致したものとして処理されます。
![](count3.png)
* `count({eventkey_1}) by {eventkey_2} {operator} {number}`: conditionのパイプ以前の条件に一致したログを{eventkey_2}毎にグルーピングし、そのグループ毎に{eventkey_1}の値が何種類存在するか数えます。そのグルーピング毎に数えた値が{operator}と{number}で指定した条件式を満たす場合に、条件に一致したものとして処理されます。
![](count4.png)
また、上記のoperatorには下記を指定できます。
* `==`: 指定された値と等しい場合、条件に一致したものと処理されます。
* `>=`: 指定された値以上である場合、条件に一致したものと処理されます。
* `>`: 指定された値より大きい場合、条件に一致したものと処理されます。
* `<=`: 指定された値以下である場合、条件に一致したものと処理されます。
* `<`: 指定された値より小さい場合、条件に一致したものと処理されます。
# outputの記法
detectionの条件に一致した場合に、出力されるメッセージを指定できます。固定の文字列が出力できる他、eventkeyを%で囲むことにより、検知したログの値を表示することも可能です。下記の例では検知した際のメッセージにScriptBlockTextというeventkeyの値を使用しています。
``````
output: 'command=%ScriptBlockText%'
``````

BIN
doc/count1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
doc/count2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
doc/count3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
doc/count4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

@@ -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}$

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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()
}

View File

@@ -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()
);
}

View File

@@ -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, &regexes);
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", &regexes);
assert!(regextext == true);
let regextext = utils::check_regex("\\hogehoge.exe", 0, &regexes);
assert!(regextext == "");
let regextext = utils::check_regex("\\hogehoge.exe", &regexes);
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\"";

View File

@@ -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;

View File

@@ -1,3 +1,2 @@
regex
^"C:\\Program Files\\Google\\Chrome\\Application\\chrome\.exe"
^"C:\\Program Files\\Google\\Update\\GoogleUpdate\.exe"