Merge branch 'main' into clap_update_v3

This commit is contained in:
DustInDark
2022-06-12 23:45:10 +09:00
10 changed files with 128 additions and 59 deletions

View File

@@ -1,21 +1,20 @@
# 変更点
## v1.3.1 [2022/xx/xx]
## v1.3.1 [2022/06/13]
**新機能:**
- ルール内の`details`で複数の`Data`レコードから特定のデータを指定して出力できるようにした。 (#487) (@hitenkoku)
- 読み込んだルールのステータス情報の要約を追加した。 (#583) (@hitenkoku)
**改善:**
- LinuxとmacOSのバイナリサイズをより小さくするために、デバッグシンボルをストリップします。(#568) (@YamatoSecurity)
- Crateパッケージの更新 (@YamatoSecurity)
- 新たな時刻表示のオプションとして`--US-time``--US-military-time``--European-time`の3つを追加した (#574) (@hitenkoku)
- `--rfc-3339` オプションの時刻表示形式を変更した。 (#574) (@hitenkoku)
- `-R/ --display-record-id`オプションを`-R/ --hide-record-id`に変更。レコードIDはデフォルトで出力するようにして`-R`オプションを付けた際に表示しないように変更した。(#579) (@hitenkoku)
**バグ修正:**
- xxx
- ルール読み込み時のメッセージを追加した。 (#583) (@hitenkoku)
**バグ修正:**

View File

@@ -1,25 +1,24 @@
# Changes
## v1.3.1 [2022/xx/xx]
## v1.3.1 [2022/06/13]
**New Features:**
- You can now specify specific fields when there are multiple fields with the same name (Ex: `Data`). In the `details` line in a rule, specify a placeholder like `%Data[1]%` to display the first `Data` field. (#487) (@hitenkoku)
- Added loaded rules status summary. (#583) (@hitenkoku)
**Enhancements:**
- Strip debug symbols by default for smaller Linux and macOS binaries. (#568) (@YamatoSecurity)
- Debug symbols are stripped by default for smaller Linux and macOS binaries. (#568) (@YamatoSecurity)
- Updated crate packages (@YamatoSecurity)
- Added new output time format options. (`--US-time`, `--US-military-time`, `--European-time`) (#574) (@hitenkoku)
- Changed output time format when `--rfc-3339` option is enabled. (#574) (@hitenkoku)
- Changed the output time format when `--rfc-3339` option is enabled. (#574) (@hitenkoku)
- Changed the `-R / --display-record-id` option to `-R / --hide-record-id` and now by default the event record ID is displayed. You can hide the record ID with `-R / --hide-record-id`. (#579) (@hitenkoku)
- Added rule loading message. (#583) (@hitenkoku)
**Bug Fixes:**
- xxx
**Bug Fixes:**
- fixed bug that RecordID and RecordInformation column is showed when options is not enabled. (#577) (@hitenkoku)
- The RecordID and RecordInformation column headers would be shown even if those options were not enabled. (#577) (@hitenkoku)
## v1.3.0 [2022/06/06]

8
Cargo.lock generated
View File

@@ -175,7 +175,7 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
dependencies = [
"camino",
"cargo-platform",
"semver 1.0.9",
"semver 1.0.10",
"serde",
"serde_json",
]
@@ -692,7 +692,7 @@ dependencies = [
[[package]]
name = "hayabusa"
version = "1.3.1-dev"
version = "1.3.1"
dependencies = [
"base64",
"bytesize",
@@ -1474,9 +1474,9 @@ dependencies = [
[[package]]
name = "semver"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd"
checksum = "a41d061efea015927ac527063765e73601444cdc344ba855bc7bd44578b25e1c"
dependencies = [
"serde",
]

View File

@@ -1,6 +1,6 @@
[package]
name = "hayabusa"
version = "1.3.1-dev"
version = "1.3.1"
authors = ["Yamato Security @SecurityYamato"]
edition = "2021"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 737 KiB

View File

@@ -102,7 +102,7 @@ fn build_app<'a>() -> ArgMatches<'a> {
--contributors 'Prints the list of contributors.'";
App::new(&program)
.about("Hayabusa: Aiming to be the world's greatest Windows event log analysis tool!")
.version("1.3.1-dev")
.version("1.3.1")
.author("Yamato Security (https://github.com/Yamato-Security/hayabusa) @SecurityYamato")
.setting(AppSettings::VersionlessSubcommands)
.arg(

View File

@@ -13,7 +13,7 @@ use crate::detections::print::{CH_CONFIG, IS_HIDE_RECORD_ID, TAGS_CONFIG};
use crate::detections::rule;
use crate::detections::rule::AggResult;
use crate::detections::rule::RuleNode;
use crate::detections::utils::get_serde_number_to_string;
use crate::detections::utils::{get_serde_number_to_string, make_ascii_titlecase};
use crate::filter;
use crate::yaml::ParseYaml;
use hashbrown;
@@ -109,7 +109,7 @@ impl Detection {
});
}
parseerror_count += 1;
println!(); // 一行開けるためのprintln
println!();
});
Option::None
};
@@ -126,12 +126,13 @@ impl Detection {
.args
.is_present("logon-summary")
{
let _ = &rulefile_loader
.rule_load_cnt
.insert(String::from("rule parsing error"), parseerror_count);
Detection::print_rule_load_info(
&rulefile_loader.rulecounter,
&parseerror_count,
&rulefile_loader.exclude_rule_count,
&rulefile_loader.noisy_rule_count,
&rulefile_loader.deprecate_rule_count,
&rulefile_loader.rule_load_cnt,
&rulefile_loader.rule_status_cnt,
);
}
ret
@@ -355,27 +356,49 @@ impl Detection {
pub fn print_rule_load_info(
rc: &HashMap<String, u128>,
parseerror_count: &u128,
exclude_count: &u128,
noisy_count: &u128,
deprecate_count: &u128,
ld_rc: &HashMap<String, u128>,
st_rc: &HashMap<String, u128>,
) {
if *STATISTICS_FLAG {
return;
}
println!("Deprecated rules: {}", deprecate_count);
println!("Excluded rules: {}", exclude_count);
println!("Noisy rules: {}", noisy_count);
println!("Rule parsing errors: {}", parseerror_count);
let mut sorted_ld_rc: Vec<(&String, &u128)> = ld_rc.iter().collect();
sorted_ld_rc.sort_by(|a, b| a.0.cmp(b.0));
sorted_ld_rc.into_iter().for_each(|(key, value)| {
//タイトルに利用するものはascii文字であることを前提として1文字目を大文字にするように変更する
println!(
"{} rules: {}",
make_ascii_titlecase(key.clone().as_mut()),
value,
);
});
println!();
let mut sorted_st_rc: Vec<(&String, &u128)> = st_rc.iter().collect();
let total_loaded_rule_cnt: u128 = sorted_st_rc.iter().map(|(_, v)| v.to_owned()).sum();
sorted_st_rc.sort_by(|a, b| a.0.cmp(b.0));
sorted_st_rc.into_iter().for_each(|(key, value)| {
let rate = if value == &0_u128 {
0 as f64
} else {
(*value as f64) / (total_loaded_rule_cnt as f64) * 100.0
};
//タイトルに利用するものはascii文字であることを前提として1文字目を大文字にするように変更する
println!(
"{} rules: {} ({:.2}%)",
make_ascii_titlecase(key.clone().as_mut()),
value,
rate
);
});
println!();
let mut sorted_rc: Vec<(&String, &u128)> = rc.iter().collect();
sorted_rc.sort_by(|a, b| a.0.cmp(b.0));
let mut enable_total = 0;
sorted_rc.into_iter().for_each(|(key, value)| {
println!("{} rules: {}", key, value);
enable_total += value;
});
println!("Total enabled detection rules: {}", enable_total);
println!("Total enabled detection rules: {}", total_loaded_rule_cnt);
println!();
}
}

View File

@@ -341,9 +341,26 @@ fn _collect_recordinfo<'a>(
}
}
/**
* 最初の文字を大文字にする関数
*/
pub fn make_ascii_titlecase(s: &mut str) -> String {
let mut c = s.chars();
match c.next() {
None => String::new(),
Some(f) => {
if !f.is_ascii() {
s.to_string()
} else {
f.to_uppercase().collect::<String>() + c.as_str()
}
}
}
}
#[cfg(test)]
mod tests {
use crate::detections::utils;
use crate::detections::utils::{self, make_ascii_titlecase};
use regex::Regex;
use serde_json::Value;
@@ -494,4 +511,15 @@ mod tests {
assert!(utils::get_serde_number_to_string(&event_record["Event"]["EventData"]).is_none());
}
#[test]
/// 文字列を与えてascii文字を大文字にするように対応する関数のテスト
fn test_make_ascii_titlecase() {
assert_eq!(make_ascii_titlecase("aaaa".to_string().as_mut()), "Aaaa");
assert_eq!(
make_ascii_titlecase("i am Test".to_string().as_mut()),
"I am Test"
);
assert_eq!(make_ascii_titlecase("β".to_string().as_mut()), "β");
}
}

View File

@@ -455,6 +455,8 @@ impl App {
}
println!("Total file size: {}", total_file_size.to_string_as(false));
println!();
println!("Loading detections rules. Please wait.");
println!();
let rule_files = detection::Detection::parse_rule_files(
level,

View File

@@ -18,9 +18,8 @@ use yaml_rust::YamlLoader;
pub struct ParseYaml {
pub files: Vec<(String, yaml_rust::Yaml)>,
pub rulecounter: HashMap<String, u128>,
pub exclude_rule_count: u128,
pub noisy_rule_count: u128,
pub deprecate_rule_count: u128,
pub rule_load_cnt: HashMap<String, u128>,
pub rule_status_cnt: HashMap<String, u128>,
pub errorrule_count: u128,
}
@@ -35,9 +34,11 @@ impl ParseYaml {
ParseYaml {
files: Vec::new(),
rulecounter: HashMap::new(),
exclude_rule_count: 0,
noisy_rule_count: 0,
deprecate_rule_count: 0,
rule_load_cnt: HashMap::from([
("excluded".to_string(), 0_u128),
("noisy".to_string(), 0_u128),
]),
rule_status_cnt: HashMap::from([("deprecated".to_string(), 0_u128)]),
errorrule_count: 0,
}
}
@@ -221,19 +222,18 @@ impl ParseYaml {
//除外されたルールは無視する
let rule_id = &yaml_doc["id"].as_str();
if rule_id.is_some() {
match exclude_ids
if let Some(v) = exclude_ids
.no_use_rule
.get(&rule_id.unwrap_or("").to_string())
.get(&rule_id.unwrap_or(&String::default()).to_string())
{
None => (),
Some(v) => {
if v.contains("exclude_rule") {
self.exclude_rule_count += 1;
} else {
self.noisy_rule_count += 1;
}
return Option::None;
}
let entry_key = if v.contains("exclude_rule") {
"excluded"
} else {
"noisy"
};
let entry = self.rule_load_cnt.entry(entry_key.to_string()).or_insert(0);
*entry += 1;
return Option::None;
}
}
@@ -245,6 +245,17 @@ impl ParseYaml {
+ 1,
);
let status_cnt = self
.rule_status_cnt
.entry(
yaml_doc["status"]
.as_str()
.unwrap_or("undefined")
.to_string(),
)
.or_insert(0);
*status_cnt += 1;
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
println!("Loaded yml file path: {}", filepath);
}
@@ -269,7 +280,11 @@ impl ParseYaml {
{
let rule_status = &yaml_doc["status"].as_str().unwrap_or_default();
if *rule_status == "deprecated" {
self.deprecate_rule_count += 1;
let entry = self
.rule_status_cnt
.entry(rule_status.to_string())
.or_insert(0);
*entry += 1;
return Option::None;
}
}
@@ -400,7 +415,7 @@ mod tests {
let mut yaml = yaml::ParseYaml::new();
let path = Path::new("test_files/rules/yaml");
yaml.read_dir(path, "", &filter::exclude_ids()).unwrap();
assert_eq!(yaml.exclude_rule_count, 5);
assert_eq!(yaml.rule_load_cnt.get("excluded").unwrap().to_owned(), 5);
}
#[test]
fn test_all_noisy_rules_file() {
@@ -409,7 +424,7 @@ mod tests {
let mut yaml = yaml::ParseYaml::new();
let path = Path::new("test_files/rules/yaml");
yaml.read_dir(path, "", &filter::exclude_ids()).unwrap();
assert_eq!(yaml.noisy_rule_count, 5);
assert_eq!(yaml.rule_load_cnt.get("noisy").unwrap().to_owned(), 5);
}
#[test]
fn test_none_exclude_rules_file() {
@@ -419,7 +434,7 @@ mod tests {
let path = Path::new("test_files/rules/yaml");
let exclude_ids = RuleExclude::default();
yaml.read_dir(path, "", &exclude_ids).unwrap();
assert_eq!(yaml.exclude_rule_count, 0);
assert_eq!(yaml.rule_load_cnt.get("excluded").unwrap().to_owned(), 0);
}
#[test]
fn test_exclude_deprecated_rules_file() {
@@ -427,6 +442,9 @@ mod tests {
let path = Path::new("test_files/rules/deprecated");
let exclude_ids = RuleExclude::default();
yaml.read_dir(path, "", &exclude_ids).unwrap();
assert_eq!(yaml.deprecate_rule_count, 1);
assert_eq!(
yaml.rule_status_cnt.get("deprecated").unwrap().to_owned(),
2
);
}
}