Feature/colorlog#239 (#365)

* added color carete #239

* added hex library

* added color config file parser #239

* added color output feature #239

* changed fast hashmap library

* added color output description(Japanese) #239

* added color output description(English) #239

* fixed medium level typo

* removed white color font level #239

* added trim and loose colorcode condition #239

* fixed hex convert error panic #239

- output warn and go next iterator when happen hex convert panic

- added user input in hex convert warn output to use easily
This commit is contained in:
DustInDark
2022-01-26 01:39:14 +09:00
committed by GitHub
parent 15ee980711
commit b12029de5c
7 changed files with 147 additions and 18 deletions

21
Cargo.lock generated
View File

@@ -232,6 +232,17 @@ dependencies = [
"bitflags", "bitflags",
] ]
[[package]]
name = "colored"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
dependencies = [
"atty",
"lazy_static",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "console" name = "console"
version = "0.11.3" version = "0.11.3"
@@ -766,11 +777,13 @@ dependencies = [
"base64", "base64",
"chrono", "chrono",
"clap", "clap",
"colored",
"csv", "csv",
"dotenv", "dotenv",
"evtx", "evtx",
"flate2", "flate2",
"hashbrown", "hashbrown",
"hex 0.4.3",
"hhmmss", "hhmmss",
"lazy_static", "lazy_static",
"linked-hash-map", "linked-hash-map",
@@ -802,6 +815,12 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]] [[package]]
name = "hhmmss" name = "hhmmss"
version = "0.1.0" version = "0.1.0"
@@ -1927,7 +1946,7 @@ checksum = "50e26b33762cd2ec755267c4a4af36adb0864b93afbe595ea8ff61b5528f4c11"
dependencies = [ dependencies = [
"chrono", "chrono",
"error-chain 0.11.0", "error-chain 0.11.0",
"hex", "hex 0.3.2",
"reqwest", "reqwest",
"serde", "serde",
"serde_derive", "serde_derive",

View File

@@ -29,6 +29,8 @@ dotenv = "0.15.0"
hhmmss = "*" hhmmss = "*"
pbr = "*" pbr = "*"
hashbrown = "0.11.2" hashbrown = "0.11.2"
colored = "2.0.0"
hex = "0.4.3"
[target.x86_64-pc-windows-gnu] [target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc" linker = "x86_64-w64-mingw32-gcc"

View File

@@ -242,6 +242,10 @@ When saving to a CSV file an additional two fields will be added:
The progress bar will only work with multiple evtx files. The progress bar will only work with multiple evtx files.
It will display in real time the number and percent of evtx files that it has analyzed. It will display in real time the number and percent of evtx files that it has analyzed.
## Color Output
Hayabusa output to the screen can change font color by `Level`.
Config file is `.\config\level_color.txt`. format is `level,(RGB 6digit ColorHex)`.
# Hayabusa rules # Hayabusa rules
Hayabusa detection rules are written in a sigma-like YML format and are located in the `rules` folder. In the future, we plan to host the rules at [https://github.com/Yamato-Security/hayabusa-rules](https://github.com/Yamato-Security/hayabusa-rules) so please send any issues and pull requests for rules there instead of the main hayabusa repository. Hayabusa detection rules are written in a sigma-like YML format and are located in the `rules` folder. In the future, we plan to host the rules at [https://github.com/Yamato-Security/hayabusa-rules](https://github.com/Yamato-Security/hayabusa-rules) so please send any issues and pull requests for rules there instead of the main hayabusa repository.

View File

@@ -243,6 +243,11 @@ CSVファイルとして保存する場合、以下の2つのフィールドが
プログレス・バーは、複数のevtxファイルに対してのみ機能します。 プログレス・バーは、複数のevtxファイルに対してのみ機能します。
解析したevtxファイルの数と割合をリアルタイムで表示します。 解析したevtxファイルの数と割合をリアルタイムで表示します。
## 標準出力へのカラー設定
Hayabusaの結果はLevel毎に文字色を変えることができます。
`.\config\level_color.txt`の値を変更することで文字色を変えることができます。
形式は`level名,(6桁のRGBのカラーhex)`です。
# Hayabusa ルール # Hayabusa ルール
Hayabusa検知ルールはSigmaのようなYML形式で記述されています。`rules`ディレクトリに入っていますが、将来的には[https://github.com/Yamato-Security/hayabusa-rules](https://github.com/Yamato-Security/hayabusa-rules)のレポジトリで管理する予定なので、ルールのissueとpull requestはhayabusaのレポジトリではなく、ルールレポジトリへお願いします。 Hayabusa検知ルールはSigmaのようなYML形式で記述されています。`rules`ディレクトリに入っていますが、将来的には[https://github.com/Yamato-Security/hayabusa-rules](https://github.com/Yamato-Security/hayabusa-rules)のレポジトリで管理する予定なので、ルールのissueとpull requestはhayabusaのレポジトリではなく、ルールレポジトリへお願いします。

5
config/level_color.txt Normal file
View File

@@ -0,0 +1,5 @@
level,colorcode
critical,ff0000
high,ffff00
medium,00ff00
low,00ffff

View File

@@ -1,7 +1,10 @@
use crate::detections::configs; use crate::detections::configs;
use crate::detections::print; use crate::detections::print;
use crate::detections::print::AlertMessage; use crate::detections::print::AlertMessage;
use crate::detections::utils;
use chrono::{DateTime, Local, TimeZone, Utc}; use chrono::{DateTime, Local, TimeZone, Utc};
use colored::*;
use hashbrown::HashMap;
use serde::Serialize; use serde::Serialize;
use std::error::Error; use std::error::Error;
use std::fs::File; use std::fs::File;
@@ -26,11 +29,48 @@ pub struct CsvFormat<'a> {
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]
pub struct DisplayFormat<'a> { pub struct DisplayFormat<'a> {
timestamp: &'a str, timestamp: &'a str,
computer: &'a str, pub computer: &'a str,
event_i_d: &'a str, pub event_i_d: &'a str,
level: &'a str, pub level: &'a str,
rule_title: &'a str, pub rule_title: &'a str,
details: &'a str, pub details: &'a str,
}
/// level_color.txtファイルを読み込み対応する文字色のマッピングを返却する関数
pub fn set_output_color() -> Option<HashMap<String, Vec<u8>>> {
let read_result = utils::read_csv("config/level_color.txt");
if read_result.is_err() {
// color情報がない場合は通常の白色の出力が出てくるのみで動作への影響を与えない為warnとして処理する
AlertMessage::warn(
&mut BufWriter::new(std::io::stderr().lock()),
&read_result.as_ref().unwrap_err(),
)
.ok();
return None;
}
let mut color_map: HashMap<String, Vec<u8>> = HashMap::new();
read_result.unwrap().into_iter().for_each(|line| {
if line.len() != 2 {
return;
}
let empty = &"".to_string();
let level = line.get(0).unwrap_or(empty);
let convert_color_result = hex::decode(line.get(1).unwrap_or(empty).trim());
if convert_color_result.is_err() {
AlertMessage::warn(
&mut BufWriter::new(std::io::stderr().lock()),
&format!("Failed hex convert in level_color.txt. Color output is disabled. Input Line: {}",line.join(","))
)
.ok();
return;
}
let color_code = convert_color_result.unwrap();
if level.len() == 0 || color_code.len() < 3 {
return;
}
color_map.insert(level.to_string(), color_code);
});
return Some(color_map);
} }
pub fn after_fact() { pub fn after_fact() {
@@ -63,13 +103,17 @@ pub fn after_fact() {
// 標準出力に出力する場合 // 標準出力に出力する場合
Box::new(BufWriter::new(io::stdout())) Box::new(BufWriter::new(io::stdout()))
}; };
let color_map = set_output_color();
if let Err(err) = emit_csv(&mut target, displayflag) { if let Err(err) = emit_csv(&mut target, displayflag, color_map) {
fn_emit_csv_err(Box::new(err)); fn_emit_csv_err(Box::new(err));
} }
} }
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,
color_map: Option<HashMap<String, Vec<u8>>>,
) -> io::Result<()> {
let mut wtr; let mut wtr;
if displayflag { if displayflag {
wtr = csv::WriterBuilder::new() wtr = csv::WriterBuilder::new()
@@ -88,13 +132,63 @@ fn emit_csv<W: std::io::Write>(writer: &mut W, displayflag: bool) -> io::Result<
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 {
// カラーをつけない場合は255,255,255で出力する
let mut output_color: Vec<u8> = vec![255, 255, 255];
if color_map.is_some() {
let target_color = color_map.as_ref().unwrap().get(&detect_info.level);
if target_color.is_some() {
output_color = target_color.unwrap().to_vec();
}
}
wtr.serialize(DisplayFormat { wtr.serialize(DisplayFormat {
timestamp: &format!("{} ", &format_time(time)), timestamp: &format!(
level: &format!(" {} ", &detect_info.level), "{} ",
computer: &format!(" {} ", &detect_info.computername), &format_time(time).truecolor(
event_i_d: &format!(" {} ", &detect_info.eventid), output_color[0],
rule_title: &format!(" {} ", &detect_info.alert), output_color[1],
details: &format!(" {}", &detect_info.detail), output_color[2]
)
),
level: &format!(
" {} ",
&detect_info.level.truecolor(
output_color[0],
output_color[1],
output_color[2]
)
),
computer: &format!(
" {} ",
&detect_info.computername.truecolor(
output_color[0],
output_color[1],
output_color[2]
)
),
event_i_d: &format!(
" {} ",
&detect_info.eventid.truecolor(
output_color[0],
output_color[1],
output_color[2]
)
),
rule_title: &format!(
" {} ",
&detect_info.alert.truecolor(
output_color[0],
output_color[1],
output_color[2]
)
),
details: &format!(
" {}",
&detect_info.detail.truecolor(
output_color[0],
output_color[1],
output_color[2]
)
),
})?; })?;
} else { } else {
// csv出力時フォーマット // csv出力時フォーマット
@@ -266,7 +360,7 @@ mod tests {
+ "\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, None).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) => {
@@ -337,7 +431,7 @@ mod tests {
+ "\n"; + "\n";
let mut file: Box<dyn io::Write> = let mut file: Box<dyn io::Write> =
Box::new(File::create("./test_emit_csv_display.txt".to_string()).unwrap()); Box::new(File::create("./test_emit_csv_display.txt".to_string()).unwrap());
assert!(emit_csv(&mut file, true).is_ok()); assert!(emit_csv(&mut file, true, None).is_ok());
match read_to_string("./test_emit_csv_display.txt") { match read_to_string("./test_emit_csv_display.txt") {
Err(_) => panic!("Failed to open file."), Err(_) => panic!("Failed to open file."),
Ok(s) => { Ok(s) => {

View File

@@ -3,11 +3,11 @@ use crate::detections::configs;
use crate::detections::utils; use crate::detections::utils;
use crate::detections::utils::get_serde_number_to_string; use crate::detections::utils::get_serde_number_to_string;
use chrono::{DateTime, Local, TimeZone, Utc}; use chrono::{DateTime, Local, TimeZone, Utc};
use hashbrown::HashMap;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use regex::Regex; use regex::Regex;
use serde_json::Value; use serde_json::Value;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::HashMap;
use std::env; use std::env;
use std::fs::create_dir; use std::fs::create_dir;
use std::fs::File; use std::fs::File;