Merge branch 'main' into feature/fill_no_use_rules
This commit is contained in:
53
Cargo.lock
generated
53
Cargo.lock
generated
@@ -312,10 +312,20 @@ version = "0.4.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
|
checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils 0.7.2",
|
||||||
"maybe-uninit",
|
"maybe-uninit",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"crossbeam-utils 0.8.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-deque"
|
name = "crossbeam-deque"
|
||||||
version = "0.7.3"
|
version = "0.7.3"
|
||||||
@@ -323,7 +333,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
|
checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-epoch",
|
"crossbeam-epoch",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils 0.7.2",
|
||||||
"maybe-uninit",
|
"maybe-uninit",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -335,7 +345,7 @@ checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg 1.0.1",
|
"autocfg 1.0.1",
|
||||||
"cfg-if 0.1.10",
|
"cfg-if 0.1.10",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils 0.7.2",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"maybe-uninit",
|
"maybe-uninit",
|
||||||
"memoffset",
|
"memoffset",
|
||||||
@@ -349,7 +359,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
|
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 0.1.10",
|
"cfg-if 0.1.10",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils 0.7.2",
|
||||||
"maybe-uninit",
|
"maybe-uninit",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -364,6 +374,16 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "csv"
|
name = "csv"
|
||||||
version = "1.1.3"
|
version = "1.1.3"
|
||||||
@@ -730,6 +750,7 @@ dependencies = [
|
|||||||
"linked-hash-map",
|
"linked-hash-map",
|
||||||
"mopa",
|
"mopa",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
|
"pbr",
|
||||||
"quick-xml 0.17.2",
|
"quick-xml 0.17.2",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -1288,6 +1309,18 @@ dependencies = [
|
|||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pbr"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff5751d87f7c00ae6403eb1fcbba229b9c76c9a30de8c1cf87182177b168cea2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel 0.5.1",
|
||||||
|
"libc",
|
||||||
|
"time 0.1.44",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -1564,9 +1597,9 @@ version = "1.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "91739a34c4355b5434ce54c9086c5895604a9c278586d1f1aa95e04f66b525a0"
|
checksum = "91739a34c4355b5434ce54c9086c5895604a9c278586d1f1aa95e04f66b525a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-channel",
|
"crossbeam-channel 0.4.4",
|
||||||
"crossbeam-deque",
|
"crossbeam-deque",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils 0.7.2",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
]
|
]
|
||||||
@@ -2209,7 +2242,7 @@ version = "0.1.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671"
|
checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils 0.7.2",
|
||||||
"futures",
|
"futures",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2241,7 +2274,7 @@ version = "0.1.12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351"
|
checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils 0.7.2",
|
||||||
"futures",
|
"futures",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
@@ -2286,7 +2319,7 @@ checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-deque",
|
"crossbeam-deque",
|
||||||
"crossbeam-queue",
|
"crossbeam-queue",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils 0.7.2",
|
||||||
"futures",
|
"futures",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
@@ -2301,7 +2334,7 @@ version = "0.2.13"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296"
|
checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils 0.7.2",
|
||||||
"futures",
|
"futures",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio-executor",
|
"tokio-executor",
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ mopa = "0.2.2"
|
|||||||
slack-hook = "0.8"
|
slack-hook = "0.8"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
hhmmss = "*"
|
hhmmss = "*"
|
||||||
|
pbr = "*"
|
||||||
|
|
||||||
[target.x86_64-pc-windows-gnu]
|
[target.x86_64-pc-windows-gnu]
|
||||||
linker = "x86_64-w64-mingw32-gcc"
|
linker = "x86_64-w64-mingw32-gcc"
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
4fe151c2-ecf9-4fae-95ae-b88ec9c2fca6
|
4fe151c2-ecf9-4fae-95ae-b88ec9c2fca6
|
||||||
c92f1896-d1d2-43c3-92d5-7a5b35c217bb
|
c92f1896-d1d2-43c3-92d5-7a5b35c217bb
|
||||||
|
7b449a5e-1db5-4dd0-a2dc-4e3a67282538
|
||||||
|
c265cf08-3f99-46c1-8d59-328247057d57
|
||||||
|
66b6be3d-55d0-4f47-9855-d69df21740ea
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
0f06a3a5-6a09-413f-8743-e6cf35561297 # sysmon_wmi_event_subscription.yml
|
||||||
|
b0d77106-7bb0-41fe-bd94-d1752164d066 # win_rare_schtasks_creations.yml
|
||||||
|
66bfef30-22a5-4fcd-ad44-8d81e60922ae # win_rare_service_installs.yml
|
||||||
|
e98374a6-e2d9-4076-9b5c-11bdb2569995 # win_susp_failed_logons_single_source.yml
|
||||||
|
6309ffc4-8fa2-47cf-96b8-a2f72e58e538 # win_susp_failed_logons_single_source2.yml
|
||||||
@@ -9,7 +9,7 @@ output_jp: 'ユーザ: %TargetUserName% : タイプ: %LogonType% : 端末: %
|
|||||||
description: Prints logon information.
|
description: Prints logon information.
|
||||||
description_jp: Prints logon information.
|
description_jp: Prints logon information.
|
||||||
|
|
||||||
id: a85096da-be85-48d7-8ad5-2f957cd74daa
|
id: e87bd730-df45-4ae9-85de-6c75369c5d29
|
||||||
level: low
|
level: low
|
||||||
status: stable
|
status: stable
|
||||||
detection:
|
detection:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ output_jp: 'ユーザ: %TargetUserName% : タイプ: %LogonType% : 端末: %
|
|||||||
description: Prints logon information.
|
description: Prints logon information.
|
||||||
description_jp: Prints logon information.
|
description_jp: Prints logon information.
|
||||||
|
|
||||||
id: a85096da-be85-48d7-8ad5-2f957cd74daa
|
id: 8afa97ce-a217-4f7c-aced-3e320a57756d
|
||||||
level: informational
|
level: informational
|
||||||
status: stable
|
status: stable
|
||||||
detection:
|
detection:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ output_jp: 'ユーザ: %MemberName% : SID: %MemberSid% : グループ名: %T
|
|||||||
description: A user was added to a security-enabled local group.
|
description: A user was added to a security-enabled local group.
|
||||||
description_jp: ユーザがローカルセキュリティグループに追加された。
|
description_jp: ユーザがローカルセキュリティグループに追加された。
|
||||||
|
|
||||||
id: 611e2e76-a28f-4255-812c-eb8836b2f5bb
|
id: 2f04e44e-1c79-4343-b4ab-ba670ee10aa0
|
||||||
level: low
|
level: low
|
||||||
status: stable
|
status: stable
|
||||||
detection:
|
detection:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ output_jp: "ユーザ名: %LogFileClearedSubjectUserName%"
|
|||||||
description: Somebody has cleared the System event log.
|
description: Somebody has cleared the System event log.
|
||||||
description_jp: 誰かがシステムログをクリアした。
|
description_jp: 誰かがシステムログをクリアした。
|
||||||
|
|
||||||
id: c2f690ac-53f8-4745-8cfe-7127dda28c74
|
id: f481a1f3-969e-4187-b3a5-b47c272bfebd
|
||||||
level: high
|
level: high
|
||||||
status: stable
|
status: stable
|
||||||
detection:
|
detection:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ output_jp: 'Job名: %JobTitle% : URL: %Url%'
|
|||||||
description: Adversaries may abuse BITS jobs to persistently execute or clean up after malicious payloads.
|
description: Adversaries may abuse BITS jobs to persistently execute or clean up after malicious payloads.
|
||||||
description_jp: Adversaries may abuse BITS jobs to persistently execute or clean up after malicious payloads.
|
description_jp: Adversaries may abuse BITS jobs to persistently execute or clean up after malicious payloads.
|
||||||
|
|
||||||
id: d3fb8f7b-88b0-4ff4-bf9b-ca286ce19031
|
id: 18e6fa4a-353d-42b6-975c-bb05dbf4a004
|
||||||
level: informational
|
level: informational
|
||||||
status: stable
|
status: stable
|
||||||
detection:
|
detection:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ output_jp: 'ユーザ: %TargetUserName% : 端末: %WorkstationName% : IPア
|
|||||||
description: Prints logon information.
|
description: Prints logon information.
|
||||||
description_jp: Prints logon information.
|
description_jp: Prints logon information.
|
||||||
|
|
||||||
id: e50e3952-06d9-44a8-ab07-7a41c9801d78
|
id: fbbe9d3f-ed1f-49a9-9446-726e349f5fba
|
||||||
level: informational
|
level: informational
|
||||||
status: stable
|
status: stable
|
||||||
detection:
|
detection:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ output_jp: 'ユーザ: %TargetUserName% : 端末: %WorkstationName% : IPア
|
|||||||
description: Prints logon information.
|
description: Prints logon information.
|
||||||
description_jp: Prints logon information.
|
description_jp: Prints logon information.
|
||||||
|
|
||||||
id: e50e3952-06d9-44a8-ab07-7a41c9801d78
|
id: f4b46dd3-63d6-4c75-a54c-9f6bd095cd6f
|
||||||
level: informational
|
level: informational
|
||||||
status: stable
|
status: stable
|
||||||
detection:
|
detection:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ output_jp: 'ユーザ: %TargetUserName% : 端末: %WorkstationName% : IPア
|
|||||||
description: Prints logon information
|
description: Prints logon information
|
||||||
description_jp: Prints logon information
|
description_jp: Prints logon information
|
||||||
|
|
||||||
id: c7b22878-e5d8-4c30-b245-e51fd354359e
|
id: 7beb4832-f357-47a4-afd8-803d69a5c85c
|
||||||
level: informational
|
level: informational
|
||||||
status: stable
|
status: stable
|
||||||
detection:
|
detection:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ output_jp: 'ユーザ: %TargetUserName% : 端末: %WorkstationName% : IPア
|
|||||||
description: Prints logon information
|
description: Prints logon information
|
||||||
description_jp: Prints logon information
|
description_jp: Prints logon information
|
||||||
|
|
||||||
id: 408e1304-51d7-4d3e-ab31-afd07192400b
|
id: 8ad8b25f-6052-4cfd-9a50-717cb514af13
|
||||||
level: informational
|
level: informational
|
||||||
status: stable
|
status: stable
|
||||||
detection:
|
detection:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ output_jp: 'ユーザ: %TargetUserName% : ログオンID: %TargetLogonId%'
|
|||||||
description: Prints logon information.
|
description: Prints logon information.
|
||||||
description_jp: Prints logon information.
|
description_jp: Prints logon information.
|
||||||
|
|
||||||
id: 7309e070-56b9-408b-a2f4-f1840f8f1ebf
|
id: 6bad16f1-02c4-4075-b414-3cd16944bc65
|
||||||
level: informational
|
level: informational
|
||||||
status: stable
|
status: stable
|
||||||
detection:
|
detection:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ output_jp: 'ユーザ: %SubjectUserName% : ログオンID: %SubjectLogonId%'
|
|||||||
description: Prints logon information.
|
description: Prints logon information.
|
||||||
description_jp: Prints logon information.
|
description_jp: Prints logon information.
|
||||||
|
|
||||||
id: 7309e070-56b9-408b-a2f4-f1840f8f1ebf
|
id: fdd0b325-8b89-469c-8b0c-e5ddfe39b62e
|
||||||
level: informational
|
level: informational
|
||||||
status: stable
|
status: stable
|
||||||
detection:
|
detection:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ output_jp: 'ユーザ: %TargetUserName% : サービス: %ServiceName% : IP
|
|||||||
description: Prints logon information.
|
description: Prints logon information.
|
||||||
description_jp: Prints logon information.
|
description_jp: Prints logon information.
|
||||||
|
|
||||||
id: da6257f3-cf49-464a-96fc-c84a7ce20636
|
id: d9f336ea-bb16-4a35-8a9c-183216b8d59c
|
||||||
level: informational
|
level: informational
|
||||||
status: stable
|
status: stable
|
||||||
detection:
|
detection:
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ modified: 2021/11/26
|
|||||||
|
|
||||||
title: NTLM Logon to Local Account
|
title: NTLM Logon to Local Account
|
||||||
title_jp: ローカルアカウントへのNTLMログオン
|
title_jp: ローカルアカウントへのNTLMログオン
|
||||||
output: 'User: %TargetUserName% : Workstation %WorkstationName% : Status: %Status%'
|
output: 'User: %TargetUserName% : Workstation %Workstation% : Status: %Status%'
|
||||||
output_jp: 'ユーザ: %TargetUserName% : 端末: %WorkstationName% : ステータス: %Status%'
|
output_jp: 'ユーザ: %TargetUserName% : 端末: %Workstation% : ステータス: %Status%'
|
||||||
description: Prints logon information.
|
description: Prints logon information.
|
||||||
description_jp: Prints logon information.
|
description_jp: Prints logon information.
|
||||||
|
|
||||||
|
|||||||
119
src/afterfact.rs
119
src/afterfact.rs
@@ -72,19 +72,26 @@ pub fn after_fact() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn emit_csv<W: std::io::Write>(writer: &mut W, displayflag: bool) -> io::Result<()> {
|
fn emit_csv<W: std::io::Write>(writer: &mut W, displayflag: bool) -> io::Result<()> {
|
||||||
let mut wtr = csv::WriterBuilder::new().from_writer(writer);
|
let mut wtr;
|
||||||
|
if displayflag {
|
||||||
|
wtr = csv::WriterBuilder::new()
|
||||||
|
.delimiter(b'|')
|
||||||
|
.from_writer(writer);
|
||||||
|
} else {
|
||||||
|
wtr = csv::WriterBuilder::new().from_writer(writer);
|
||||||
|
}
|
||||||
let messages = print::MESSAGES.lock().unwrap();
|
let messages = print::MESSAGES.lock().unwrap();
|
||||||
let mut detect_count = 0;
|
let mut detect_count = 0;
|
||||||
for (time, detect_infos) in messages.iter() {
|
for (time, detect_infos) in messages.iter() {
|
||||||
for detect_info in detect_infos {
|
for detect_info in detect_infos {
|
||||||
if displayflag {
|
if displayflag {
|
||||||
wtr.serialize(DisplayFormat {
|
wtr.serialize(DisplayFormat {
|
||||||
time: &format_time(time),
|
time: &format!("{} ", &format_time(time)),
|
||||||
level: &detect_info.level,
|
level: &format!(" {} ", &detect_info.level),
|
||||||
computername: &detect_info.computername,
|
computername: &format!(" {} ", &detect_info.computername),
|
||||||
eventid: &detect_info.eventid,
|
eventid: &format!(" {} ", &detect_info.eventid),
|
||||||
alert: &detect_info.alert,
|
alert: &format!(" {} ", &detect_info.alert),
|
||||||
details: &detect_info.detail,
|
details: &format!(" {}", &detect_info.detail),
|
||||||
})?;
|
})?;
|
||||||
} else {
|
} else {
|
||||||
// csv出力時フォーマット
|
// csv出力時フォーマット
|
||||||
@@ -131,10 +138,24 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[cfg(test)]
|
||||||
fn test_emit_csv() {
|
mod tests {
|
||||||
|
use crate::afterfact::emit_csv;
|
||||||
|
use crate::detections::print;
|
||||||
|
use chrono::{Local, TimeZone, Utc};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
use std::fs::File;
|
||||||
use std::fs::{read_to_string, remove_file};
|
use std::fs::{read_to_string, remove_file};
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_emit_csv() {
|
||||||
|
//テストの並列処理によって読み込みの順序が担保できずstatic変数の内容が担保が取れない為、このテストはシーケンシャルで行う
|
||||||
|
test_emit_csv_output();
|
||||||
|
test_emit_csv_output();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_emit_csv_output() {
|
||||||
let testfilepath: &str = "test.evtx";
|
let testfilepath: &str = "test.evtx";
|
||||||
let testrulepath: &str = "test-rule.yml";
|
let testrulepath: &str = "test-rule.yml";
|
||||||
let test_title = "test_title";
|
let test_title = "test_title";
|
||||||
@@ -144,7 +165,7 @@ fn test_emit_csv() {
|
|||||||
let output = "pokepoke";
|
let output = "pokepoke";
|
||||||
{
|
{
|
||||||
let mut messages = print::MESSAGES.lock().unwrap();
|
let mut messages = print::MESSAGES.lock().unwrap();
|
||||||
|
messages.clear();
|
||||||
let val = r##"
|
let val = r##"
|
||||||
{
|
{
|
||||||
"Event": {
|
"Event": {
|
||||||
@@ -171,12 +192,12 @@ fn test_emit_csv() {
|
|||||||
output.to_string(),
|
output.to_string(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let expect_time = Utc
|
let expect_time = Utc
|
||||||
.datetime_from_str("1996-02-27T01:05:01Z", "%Y-%m-%dT%H:%M:%SZ")
|
.datetime_from_str("1996-02-27T01:05:01Z", "%Y-%m-%dT%H:%M:%SZ")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let expect_tz = expect_time.with_timezone(&Local);
|
let expect_tz = expect_time.with_timezone(&Local);
|
||||||
let expect = "Time,Computername,Eventid,Level,Alert,Details,Rulepath,Filepath\n".to_string()
|
let expect = "Time,Computername,Eventid,Level,Alert,Details,Rulepath,Filepath\n"
|
||||||
|
.to_string()
|
||||||
+ &expect_tz
|
+ &expect_tz
|
||||||
.clone()
|
.clone()
|
||||||
.format("%Y-%m-%d %H:%M:%S%.3f %:z")
|
.format("%Y-%m-%d %H:%M:%S%.3f %:z")
|
||||||
@@ -196,11 +217,9 @@ fn test_emit_csv() {
|
|||||||
+ ","
|
+ ","
|
||||||
+ &testfilepath.to_string()
|
+ &testfilepath.to_string()
|
||||||
+ "\n";
|
+ "\n";
|
||||||
|
|
||||||
let mut file: Box<dyn io::Write> =
|
let mut file: Box<dyn io::Write> =
|
||||||
Box::new(File::create("./test_emit_csv.csv".to_string()).unwrap());
|
Box::new(File::create("./test_emit_csv.csv".to_string()).unwrap());
|
||||||
assert!(emit_csv(&mut file, false).is_ok());
|
assert!(emit_csv(&mut file, false).is_ok());
|
||||||
|
|
||||||
match read_to_string("./test_emit_csv.csv") {
|
match read_to_string("./test_emit_csv.csv") {
|
||||||
Err(_) => panic!("Failed to open file."),
|
Err(_) => panic!("Failed to open file."),
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
@@ -208,4 +227,76 @@ fn test_emit_csv() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
assert!(remove_file("./test_emit_csv.csv").is_ok());
|
assert!(remove_file("./test_emit_csv.csv").is_ok());
|
||||||
|
check_emit_csv_display();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_emit_csv_display() {
|
||||||
|
let testfilepath: &str = "test2.evtx";
|
||||||
|
let testrulepath: &str = "test-rule2.yml";
|
||||||
|
let test_title = "test_title2";
|
||||||
|
let test_level = "medium";
|
||||||
|
let test_computername = "testcomputer2";
|
||||||
|
let test_eventid = "2222";
|
||||||
|
let output = "displaytest";
|
||||||
|
{
|
||||||
|
let mut messages = print::MESSAGES.lock().unwrap();
|
||||||
|
messages.clear();
|
||||||
|
let val = r##"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"EventData": {
|
||||||
|
"CommandRLine": "hoge"
|
||||||
|
},
|
||||||
|
"System": {
|
||||||
|
"TimeCreated_attributes": {
|
||||||
|
"SystemTime": "1996-02-27T01:05:01Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"##;
|
||||||
|
let event: Value = serde_json::from_str(val).unwrap();
|
||||||
|
messages.insert(
|
||||||
|
testfilepath.to_string(),
|
||||||
|
testrulepath.to_string(),
|
||||||
|
&event,
|
||||||
|
test_level.to_string(),
|
||||||
|
test_computername.to_string(),
|
||||||
|
test_eventid.to_string(),
|
||||||
|
test_title.to_string(),
|
||||||
|
output.to_string(),
|
||||||
|
);
|
||||||
|
messages.debug();
|
||||||
|
}
|
||||||
|
let expect_time = Utc
|
||||||
|
.datetime_from_str("1996-02-27T01:05:01Z", "%Y-%m-%dT%H:%M:%SZ")
|
||||||
|
.unwrap();
|
||||||
|
let expect_tz = expect_time.with_timezone(&Local);
|
||||||
|
let expect = "Time|Computername|Eventid|Level|Alert|Details\n".to_string()
|
||||||
|
+ &expect_tz
|
||||||
|
.clone()
|
||||||
|
.format("%Y-%m-%d %H:%M:%S%.3f %:z")
|
||||||
|
.to_string()
|
||||||
|
+ " | "
|
||||||
|
+ test_computername
|
||||||
|
+ " | "
|
||||||
|
+ test_eventid
|
||||||
|
+ " | "
|
||||||
|
+ test_level
|
||||||
|
+ " | "
|
||||||
|
+ test_title
|
||||||
|
+ " | "
|
||||||
|
+ output
|
||||||
|
+ "\n";
|
||||||
|
let mut file: Box<dyn io::Write> =
|
||||||
|
Box::new(File::create("./test_emit_csv_display.txt".to_string()).unwrap());
|
||||||
|
assert!(emit_csv(&mut file, true).is_ok());
|
||||||
|
match read_to_string("./test_emit_csv_display.txt") {
|
||||||
|
Err(_) => panic!("Failed to open file."),
|
||||||
|
Ok(s) => {
|
||||||
|
assert_eq!(s, expect);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
assert!(remove_file("./test_emit_csv_display.txt").is_ok());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,9 +157,9 @@ impl Detection {
|
|||||||
"Medium",
|
"Medium",
|
||||||
"Low",
|
"Low",
|
||||||
"Informational",
|
"Informational",
|
||||||
"Undeifned",
|
"Undefined",
|
||||||
]);
|
]);
|
||||||
// levclcounts is [(Undeifned), (Informational), (Low),(Medium),(High),(Critical)]
|
// levclcounts is [(Undefined), (Informational), (Low),(Medium),(High),(Critical)]
|
||||||
let mut levelcounts = Vec::from([0, 0, 0, 0, 0, 0]);
|
let mut levelcounts = Vec::from([0, 0, 0, 0, 0, 0]);
|
||||||
for rule in rules.into_iter() {
|
for rule in rules.into_iter() {
|
||||||
if rule.check_exist_countdata() {
|
if rule.check_exist_countdata() {
|
||||||
@@ -212,7 +212,9 @@ impl Detection {
|
|||||||
record_info.record["Event"]["System"]["Computer"]
|
record_info.record["Event"]["System"]["Computer"]
|
||||||
.to_string()
|
.to_string()
|
||||||
.replace("\"", ""),
|
.replace("\"", ""),
|
||||||
get_serde_number_to_string(&record_info.record["Event"]["System"]["EventID"]),
|
get_serde_number_to_string(&record_info.record["Event"]["System"]["EventID"])
|
||||||
|
.unwrap_or("-".to_owned())
|
||||||
|
.to_string(),
|
||||||
rule.yaml["title"].as_str().unwrap_or("").to_string(),
|
rule.yaml["title"].as_str().unwrap_or("").to_string(),
|
||||||
rule.yaml["output"].as_str().unwrap_or("").to_string(),
|
rule.yaml["output"].as_str().unwrap_or("").to_string(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ pub struct AlertMessage {}
|
|||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref MESSAGES: Mutex<Message> = Mutex::new(Message::new());
|
pub static ref MESSAGES: Mutex<Message> = Mutex::new(Message::new());
|
||||||
|
pub static ref ALIASREGEX: Regex = Regex::new(r"%[a-zA-Z0-9-_]+%").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Message {
|
impl Message {
|
||||||
@@ -101,8 +102,7 @@ impl Message {
|
|||||||
fn parse_message(&mut self, event_record: &Value, output: String) -> String {
|
fn parse_message(&mut self, event_record: &Value, output: String) -> String {
|
||||||
let mut return_message: String = output;
|
let mut return_message: String = output;
|
||||||
let mut hash_map: HashMap<String, String> = HashMap::new();
|
let mut hash_map: HashMap<String, String> = HashMap::new();
|
||||||
let re = Regex::new(r"%[a-zA-Z0-9-_]+%").unwrap();
|
for caps in ALIASREGEX.captures_iter(&return_message) {
|
||||||
for caps in re.captures_iter(&return_message) {
|
|
||||||
let full_target_str = &caps[0];
|
let full_target_str = &caps[0];
|
||||||
let target_length = full_target_str.chars().count() - 2; // The meaning of 2 is two percent
|
let target_length = full_target_str.chars().count() - 2; // The meaning of 2 is two percent
|
||||||
let target_str = full_target_str
|
let target_str = full_target_str
|
||||||
@@ -118,16 +118,20 @@ impl Message {
|
|||||||
.get_event_key(target_str.to_string())
|
.get_event_key(target_str.to_string())
|
||||||
{
|
{
|
||||||
let split: Vec<&str> = array_str.split(".").collect();
|
let split: Vec<&str> = array_str.split(".").collect();
|
||||||
|
let mut is_exist_event_key = false;
|
||||||
let mut tmp_event_record: &Value = event_record.into();
|
let mut tmp_event_record: &Value = event_record.into();
|
||||||
for s in split {
|
for s in split {
|
||||||
if let Some(record) = tmp_event_record.get(s) {
|
if let Some(record) = tmp_event_record.get(s) {
|
||||||
|
is_exist_event_key = true;
|
||||||
tmp_event_record = record;
|
tmp_event_record = record;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hash_map.insert(
|
if is_exist_event_key {
|
||||||
full_target_str.to_string(),
|
let hash_value = get_serde_number_to_string(tmp_event_record);
|
||||||
get_serde_number_to_string(tmp_event_record),
|
if hash_value.is_some() {
|
||||||
);
|
hash_map.insert(full_target_str.to_string(), hash_value.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,6 +192,11 @@ impl Message {
|
|||||||
return Option::Some(datetime.unwrap());
|
return Option::Some(datetime.unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// message内のマップをクリアする。テストする際の冪等性の担保のため作成。
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.map.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AlertMessage {
|
impl AlertMessage {
|
||||||
@@ -327,4 +336,87 @@ mod tests {
|
|||||||
let mut stdout = stdout.lock();
|
let mut stdout = stdout.lock();
|
||||||
AlertMessage::alert(&mut stdout, input.to_string()).expect("[WARN] TESTWarn!");
|
AlertMessage::alert(&mut stdout, input.to_string()).expect("[WARN] TESTWarn!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// outputで指定されているキー(eventkey_alias.txt内で設定済み)から対象のレコード内の情報でメッセージをパースしているか確認する関数
|
||||||
|
fn test_parse_message() {
|
||||||
|
let mut message = Message::new();
|
||||||
|
let json_str = r##"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"EventData": {
|
||||||
|
"CommandLine": "parsetest1"
|
||||||
|
},
|
||||||
|
"System": {
|
||||||
|
"Computer": "testcomputer1",
|
||||||
|
"TimeCreated_attributes": {
|
||||||
|
"SystemTime": "1996-02-27T01:05:01Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"##;
|
||||||
|
let event_record: Value = serde_json::from_str(json_str).unwrap();
|
||||||
|
let expected = "commandline:parsetest1 computername:testcomputer1";
|
||||||
|
assert_eq!(
|
||||||
|
message.parse_message(
|
||||||
|
&event_record,
|
||||||
|
"commandline:%CommandLine% computername:%ComputerName%".to_owned()
|
||||||
|
),
|
||||||
|
expected,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
/// outputで指定されているキーが、eventkey_alias.txt内で設定されていない場合の出力テスト
|
||||||
|
fn test_parse_message_not_exist_key_in_output() {
|
||||||
|
let mut message = Message::new();
|
||||||
|
let json_str = r##"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"EventData": {
|
||||||
|
"CommandLine": "parsetest2"
|
||||||
|
},
|
||||||
|
"System": {
|
||||||
|
"TimeCreated_attributes": {
|
||||||
|
"SystemTime": "1996-02-27T01:05:01Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"##;
|
||||||
|
let event_record: Value = serde_json::from_str(json_str).unwrap();
|
||||||
|
let expected = "NoExistKey:%TESTNoExistKey%";
|
||||||
|
assert_eq!(
|
||||||
|
message.parse_message(&event_record, "NoExistKey:%TESTNoExistKey%".to_owned()),
|
||||||
|
expected,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
/// outputで指定されているキー(eventkey_alias.txt内で設定済み)が対象のレコード内に該当する情報がない場合の出力テスト
|
||||||
|
fn test_parse_message_not_exist_value_in_record() {
|
||||||
|
let mut message = Message::new();
|
||||||
|
let json_str = r##"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"EventData": {
|
||||||
|
"CommandLine": "parsetest3"
|
||||||
|
},
|
||||||
|
"System": {
|
||||||
|
"TimeCreated_attributes": {
|
||||||
|
"SystemTime": "1996-02-27T01:05:01Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"##;
|
||||||
|
let event_record: Value = serde_json::from_str(json_str).unwrap();
|
||||||
|
let expected = "commandline:parsetest3 computername:%ComputerName%";
|
||||||
|
assert_eq!(
|
||||||
|
message.parse_message(
|
||||||
|
&event_record,
|
||||||
|
"commandline:%CommandLine% computername:%ComputerName%".to_owned()
|
||||||
|
),
|
||||||
|
expected,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,11 +94,14 @@ pub fn get_event_id_key() -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// serde:Valueの型を確認し、文字列を返します。
|
/// serde:Valueの型を確認し、文字列を返します。
|
||||||
pub fn get_serde_number_to_string(value: &serde_json::Value) -> String {
|
pub fn get_serde_number_to_string(value: &serde_json::Value) -> Option<String> {
|
||||||
if value.is_string() {
|
if value.is_string() {
|
||||||
return value.as_str().unwrap_or("").to_string();
|
return Option::Some(value.as_str().unwrap_or("").to_string());
|
||||||
|
} else if value.is_object() {
|
||||||
|
// Object type is not specified record value.
|
||||||
|
return Option::None;
|
||||||
} else {
|
} else {
|
||||||
return value.to_string();
|
return Option::Some(value.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +166,7 @@ pub fn create_tokio_runtime() -> Runtime {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use crate::detections::utils;
|
use crate::detections::utils;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_regex() {
|
fn test_check_regex() {
|
||||||
@@ -191,4 +195,62 @@ mod tests {
|
|||||||
let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate2.exe\"";
|
let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate2.exe\"";
|
||||||
assert!(false == utils::check_allowlist(commandline, &allowlist));
|
assert!(false == utils::check_allowlist(commandline, &allowlist));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// Serde::Valueの数値型の値を文字列として返却することを確かめるテスト
|
||||||
|
fn test_get_serde_number_to_string() {
|
||||||
|
let json_str = r##"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"System": {
|
||||||
|
"EventID": 11111
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"##;
|
||||||
|
let event_record: Value = serde_json::from_str(json_str).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
utils::get_serde_number_to_string(&event_record["Event"]["System"]["EventID"]).unwrap(),
|
||||||
|
"11111".to_owned()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// Serde::Valueの文字列型の値を文字列として返却することを確かめるテスト
|
||||||
|
fn test_get_serde_number_serde_string_to_string() {
|
||||||
|
let json_str = r##"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"EventData": {
|
||||||
|
"ComputerName": "HayabusaComputer1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"##;
|
||||||
|
let event_record: Value = serde_json::from_str(json_str).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
utils::get_serde_number_to_string(&event_record["Event"]["EventData"]["ComputerName"])
|
||||||
|
.unwrap(),
|
||||||
|
"HayabusaComputer1".to_owned()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// Serde::Valueのオブジェクト型の内容を誤って渡した際にNoneを返却することを確かめるテスト
|
||||||
|
fn test_get_serde_number_serde_object_ret_none() {
|
||||||
|
let json_str = r##"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"EventData": {
|
||||||
|
"ComputerName": "HayabusaComputer1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"##;
|
||||||
|
let event_record: Value = serde_json::from_str(json_str).unwrap();
|
||||||
|
|
||||||
|
assert!(utils::get_serde_number_to_string(&event_record["Event"]["EventData"]).is_none());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use hayabusa::omikuji::Omikuji;
|
|||||||
use hayabusa::{afterfact::after_fact, detections::utils};
|
use hayabusa::{afterfact::after_fact, detections::utils};
|
||||||
use hayabusa::{detections::configs, timeline::timeline::Timeline};
|
use hayabusa::{detections::configs, timeline::timeline::Timeline};
|
||||||
use hhmmss::Hhmmss;
|
use hhmmss::Hhmmss;
|
||||||
|
use pbr::ProgressBar;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::{
|
use std::{
|
||||||
@@ -152,12 +153,14 @@ fn analysis_files(evtx_files: Vec<PathBuf>) {
|
|||||||
configs::CONFIG.read().unwrap().args.value_of("rules"),
|
configs::CONFIG.read().unwrap().args.value_of("rules"),
|
||||||
&fill_ids,
|
&fill_ids,
|
||||||
);
|
);
|
||||||
|
let mut pb = ProgressBar::new(evtx_files.len() as u64);
|
||||||
let mut detection = detection::Detection::new(rule_files);
|
let mut detection = detection::Detection::new(rule_files);
|
||||||
for evtx_file in evtx_files {
|
for evtx_file in evtx_files {
|
||||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||||
println!("Checking target evtx FilePath: {:?}", &evtx_file);
|
println!("Checking target evtx FilePath: {:?}", &evtx_file);
|
||||||
}
|
}
|
||||||
detection = analysis_file(evtx_file, detection);
|
detection = analysis_file(evtx_file, detection);
|
||||||
|
pb.inc();
|
||||||
}
|
}
|
||||||
after_fact();
|
after_fact();
|
||||||
detection.print_unique_results();
|
detection.print_unique_results();
|
||||||
|
|||||||
Reference in New Issue
Block a user