display logo in green (#552)
* added termcolor reset function #537 * added logo green output #537 * fixed test * cargo fmt * updated changelog #537 * fixed clippy error * update logo screenshot * updated rules * changed no colored logo when --no-color option is enabled * fixed colored reset bug when --update-rules option is enabled * fixed color reset bug when --level-tuning option is enabled * cargo fmt Co-authored-by: Tanaka Zakku <71482215+YamatoSecurity@users.noreply.github.com>
This commit is contained in:
@@ -12,8 +12,9 @@
|
|||||||
|
|
||||||
- ルールの`details`でeventkey_alias.txtやEvent.EventData内に存在しない情報を`n/a` (not available)と表記するようにした。(#528) (@hitenkoku)
|
- ルールの`details`でeventkey_alias.txtやEvent.EventData内に存在しない情報を`n/a` (not available)と表記するようにした。(#528) (@hitenkoku)
|
||||||
- 読み込んだイベント数と検知しなかったイベント数を表示するようにした。 (#538) (@hitenkoku)
|
- 読み込んだイベント数と検知しなかったイベント数を表示するようにした。 (#538) (@hitenkoku)
|
||||||
- 新しいロゴ。 (@YamatoSecurity)
|
- 新しいロゴに変更した。(#536) (@YamatoSecurity)
|
||||||
- evtxファイルのファイルサイズの合計を出力するようにした。(#540) (@hitenkoku)
|
- evtxファイルのファイルサイズの合計を出力するようにした。(#540) (@hitenkoku)
|
||||||
|
- ロゴの色を変更した (#537) (@hitenkoku)
|
||||||
|
|
||||||
**バグ修正:**
|
**バグ修正:**
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,9 @@
|
|||||||
|
|
||||||
- In the `details` line in a rule, when a placeholder points to a field that does not exist or there is an incorrect alias mapping, it will be outputted as `n/a` (not available). (#528) (@hitenkoku)
|
- In the `details` line in a rule, when a placeholder points to a field that does not exist or there is an incorrect alias mapping, it will be outputted as `n/a` (not available). (#528) (@hitenkoku)
|
||||||
- Display total event and data reduction count. (How many and what percent of events were ignored.) (#538) (@hitenkoku)
|
- Display total event and data reduction count. (How many and what percent of events were ignored.) (#538) (@hitenkoku)
|
||||||
- New logo. (@YamatoSecurity)
|
- New logo. (#536) (@YamatoSecurity)
|
||||||
- Display total evtx file size. (#540) (@hitenkoku)
|
- Display total evtx file size. (#540) (@hitenkoku)
|
||||||
|
- Changed logo color. (#537) (@hitenkoku)
|
||||||
|
|
||||||
**Bug Fixes:**
|
**Bug Fixes:**
|
||||||
|
|
||||||
|
|||||||
2
rules
2
rules
Submodule rules updated: 7a1cb7c0cf...3312639216
Binary file not shown.
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 77 KiB |
@@ -2,6 +2,7 @@ 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 crate::detections::utils;
|
||||||
|
use crate::detections::utils::write_color_buffer;
|
||||||
use chrono::{DateTime, Local, TimeZone, Utc};
|
use chrono::{DateTime, Local, TimeZone, Utc};
|
||||||
use csv::QuoteStyle;
|
use csv::QuoteStyle;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
@@ -67,11 +68,7 @@ pub fn set_output_color() -> HashMap<String, Color> {
|
|||||||
}
|
}
|
||||||
if read_result.is_err() {
|
if read_result.is_err() {
|
||||||
// color情報がない場合は通常の白色の出力が出てくるのみで動作への影響を与えない為warnとして処理する
|
// color情報がない場合は通常の白色の出力が出てくるのみで動作への影響を与えない為warnとして処理する
|
||||||
AlertMessage::warn(
|
AlertMessage::warn(read_result.as_ref().unwrap_err()).ok();
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
read_result.as_ref().unwrap_err(),
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
return color_map;
|
return color_map;
|
||||||
}
|
}
|
||||||
read_result.unwrap().into_iter().for_each(|line| {
|
read_result.unwrap().into_iter().for_each(|line| {
|
||||||
@@ -82,10 +79,10 @@ pub fn set_output_color() -> HashMap<String, Color> {
|
|||||||
let level = line.get(0).unwrap_or(empty);
|
let level = line.get(0).unwrap_or(empty);
|
||||||
let convert_color_result = hex::decode(line.get(1).unwrap_or(empty).trim());
|
let convert_color_result = hex::decode(line.get(1).unwrap_or(empty).trim());
|
||||||
if convert_color_result.is_err() {
|
if convert_color_result.is_err() {
|
||||||
AlertMessage::warn(
|
AlertMessage::warn(&format!(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
"Failed hex convert in level_color.txt. Color output is disabled. Input Line: {}",
|
||||||
&format!("Failed hex convert in level_color.txt. Color output is disabled. Input Line: {}",line.join(","))
|
line.join(",")
|
||||||
)
|
))
|
||||||
.ok();
|
.ok();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -93,7 +90,10 @@ pub fn set_output_color() -> HashMap<String, Color> {
|
|||||||
if level.is_empty() || color_code.len() < 3 {
|
if level.is_empty() || color_code.len() < 3 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
color_map.insert(level.to_lowercase(), Color::Rgb(color_code[0], color_code[1], color_code[2]));
|
color_map.insert(
|
||||||
|
level.to_lowercase(),
|
||||||
|
Color::Rgb(color_code[0], color_code[1], color_code[2]),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
color_map
|
color_map
|
||||||
}
|
}
|
||||||
@@ -115,23 +115,22 @@ fn _print_timeline_hist(timestamps: Vec<i64>, length: usize, side_margin_size: u
|
|||||||
let buf_wtr = BufferWriter::stdout(ColorChoice::Always);
|
let buf_wtr = BufferWriter::stdout(ColorChoice::Always);
|
||||||
let mut wtr = buf_wtr.buffer();
|
let mut wtr = buf_wtr.buffer();
|
||||||
wtr.set_color(ColorSpec::new().set_fg(None)).ok();
|
wtr.set_color(ColorSpec::new().set_fg(None)).ok();
|
||||||
|
|
||||||
if timestamps.len() < 5 {
|
if timestamps.len() < 5 {
|
||||||
write!(
|
writeln!(
|
||||||
wtr,
|
wtr,
|
||||||
"Event Frequency Timeline could not be displayed as there needs to be more than 5 events.",
|
"Event Frequency Timeline could not be displayed as there needs to be more than 5 events.",
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
writeln!(wtr).ok();
|
|
||||||
buf_wtr.print(&wtr).ok();
|
buf_wtr.print(&wtr).ok();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let title = "Event Frequency Timeline";
|
let title = "Event Frequency Timeline";
|
||||||
let header_row_space = (length - title.len()) / 2;
|
let header_row_space = (length - title.len()) / 2;
|
||||||
writeln!(wtr).ok();
|
println!();
|
||||||
write!(wtr, "{}", " ".repeat(header_row_space)).ok();
|
writeln!(wtr, "{}{}", " ".repeat(header_row_space), title).ok();
|
||||||
writeln!(wtr, "{}", title).ok();
|
println!();
|
||||||
writeln!(wtr).ok();
|
|
||||||
|
|
||||||
let timestamp_marker_max = if timestamps.len() < 2 {
|
let timestamp_marker_max = if timestamps.len() < 2 {
|
||||||
0
|
0
|
||||||
@@ -162,11 +161,7 @@ fn _print_timeline_hist(timestamps: Vec<i64>, length: usize, side_margin_size: u
|
|||||||
|
|
||||||
pub fn after_fact(all_record_cnt: usize) {
|
pub fn after_fact(all_record_cnt: usize) {
|
||||||
let fn_emit_csv_err = |err: Box<dyn Error>| {
|
let fn_emit_csv_err = |err: Box<dyn Error>| {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(&format!("Failed to write CSV. {}", err)).ok();
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
&format!("Failed to write CSV. {}", err),
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -177,11 +172,7 @@ pub fn after_fact(all_record_cnt: usize) {
|
|||||||
match File::create(csv_path) {
|
match File::create(csv_path) {
|
||||||
Ok(file) => Box::new(BufWriter::new(file)),
|
Ok(file) => Box::new(BufWriter::new(file)),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(&format!("Failed to open file. {}", err)).ok();
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
&format!("Failed to open file. {}", err),
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -372,10 +363,6 @@ fn _print_unique_results(
|
|||||||
tail_word: String,
|
tail_word: String,
|
||||||
color_map: &HashMap<String, Color>,
|
color_map: &HashMap<String, Color>,
|
||||||
) {
|
) {
|
||||||
let buf_wtr = BufferWriter::stdout(ColorChoice::Always);
|
|
||||||
let mut wtr = buf_wtr.buffer();
|
|
||||||
wtr.set_color(ColorSpec::new().set_fg(None)).ok();
|
|
||||||
|
|
||||||
let levels = Vec::from([
|
let levels = Vec::from([
|
||||||
"critical",
|
"critical",
|
||||||
"high",
|
"high",
|
||||||
@@ -389,12 +376,15 @@ fn _print_unique_results(
|
|||||||
counts_by_level.reverse();
|
counts_by_level.reverse();
|
||||||
|
|
||||||
// output total results
|
// output total results
|
||||||
writeln!(
|
write_color_buffer(
|
||||||
wtr,
|
BufferWriter::stdout(ColorChoice::Always),
|
||||||
"{} {}: {}",
|
None,
|
||||||
head_word,
|
&format!(
|
||||||
tail_word,
|
"{} {}: {}",
|
||||||
counts_by_level.iter().sum::<u128>()
|
head_word,
|
||||||
|
tail_word,
|
||||||
|
counts_by_level.iter().sum::<u128>()
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
@@ -403,12 +393,13 @@ fn _print_unique_results(
|
|||||||
"{} {} {}: {}",
|
"{} {} {}: {}",
|
||||||
head_word, level_name, tail_word, counts_by_level[i]
|
head_word, level_name, tail_word, counts_by_level[i]
|
||||||
);
|
);
|
||||||
|
write_color_buffer(
|
||||||
wtr.set_color(ColorSpec::new().set_fg(_get_output_color(color_map, level_name)))
|
BufferWriter::stdout(ColorChoice::Always),
|
||||||
.ok();
|
_get_output_color(color_map, level_name),
|
||||||
writeln!(wtr, "{}", output_raw_str).ok();
|
&output_raw_str,
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
buf_wtr.print(&wtr).ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_time(time: &DateTime<Utc>) -> String {
|
fn format_time(time: &DateTime<Utc>) -> String {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ use hashbrown::HashMap;
|
|||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::io::BufWriter;
|
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref CONFIG: RwLock<ConfigReader> = RwLock::new(ConfigReader::new());
|
pub static ref CONFIG: RwLock<ConfigReader> = RwLock::new(ConfigReader::new());
|
||||||
@@ -153,11 +152,7 @@ fn load_target_ids(path: &str) -> TargetEventIds {
|
|||||||
let mut ret = TargetEventIds::new();
|
let mut ret = TargetEventIds::new();
|
||||||
let lines = utils::read_txt(path); // ファイルが存在しなければエラーとする
|
let lines = utils::read_txt(path); // ファイルが存在しなければエラーとする
|
||||||
if lines.is_err() {
|
if lines.is_err() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(lines.as_ref().unwrap_err()).ok();
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
lines.as_ref().unwrap_err(),
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +190,6 @@ impl TargetEventTime {
|
|||||||
Ok(dt) => Some(dt.with_timezone(&Utc)),
|
Ok(dt) => Some(dt.with_timezone(&Utc)),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
"start-timeline field: the timestamp format is not correct.",
|
"start-timeline field: the timestamp format is not correct.",
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
@@ -213,7 +207,6 @@ impl TargetEventTime {
|
|||||||
Ok(dt) => Some(dt.with_timezone(&Utc)),
|
Ok(dt) => Some(dt.with_timezone(&Utc)),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
"end-timeline field: the timestamp format is not correct.",
|
"end-timeline field: the timestamp format is not correct.",
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
@@ -296,11 +289,7 @@ fn load_eventkey_alias(path: &str) -> EventKeyAliasConfig {
|
|||||||
// eventkey_aliasが読み込めなかったらエラーで終了とする。
|
// eventkey_aliasが読み込めなかったらエラーで終了とする。
|
||||||
let read_result = utils::read_csv(path);
|
let read_result = utils::read_csv(path);
|
||||||
if read_result.is_err() {
|
if read_result.is_err() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(read_result.as_ref().unwrap_err()).ok();
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
read_result.as_ref().unwrap_err(),
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,11 +321,7 @@ fn load_eventkey_alias(path: &str) -> EventKeyAliasConfig {
|
|||||||
pub fn load_pivot_keywords(path: &str) {
|
pub fn load_pivot_keywords(path: &str) {
|
||||||
let read_result = utils::read_txt(path);
|
let read_result = utils::read_txt(path);
|
||||||
if read_result.is_err() {
|
if read_result.is_err() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(read_result.as_ref().unwrap_err()).ok();
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
read_result.as_ref().unwrap_err(),
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
read_result.unwrap().into_iter().for_each(|line| {
|
read_result.unwrap().into_iter().for_each(|line| {
|
||||||
@@ -406,11 +391,7 @@ fn load_eventcode_info(path: &str) -> EventInfoConfig {
|
|||||||
let mut config = EventInfoConfig::new();
|
let mut config = EventInfoConfig::new();
|
||||||
let read_result = utils::read_csv(path);
|
let read_result = utils::read_csv(path);
|
||||||
if read_result.is_err() {
|
if read_result.is_err() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(read_result.as_ref().unwrap_err()).ok();
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
read_result.as_ref().unwrap_err(),
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ use hashbrown;
|
|||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::io::BufWriter;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::{runtime::Runtime, spawn, task::JoinHandle};
|
use tokio::{runtime::Runtime, spawn, task::JoinHandle};
|
||||||
|
|
||||||
@@ -69,7 +68,7 @@ impl Detection {
|
|||||||
if result_readdir.is_err() {
|
if result_readdir.is_err() {
|
||||||
let errmsg = format!("{}", result_readdir.unwrap_err());
|
let errmsg = format!("{}", result_readdir.unwrap_err());
|
||||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||||
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), &errmsg).ok();
|
AlertMessage::alert(&errmsg).ok();
|
||||||
}
|
}
|
||||||
if !*QUIET_ERRORS_FLAG {
|
if !*QUIET_ERRORS_FLAG {
|
||||||
ERROR_LOG_STACK
|
ERROR_LOG_STACK
|
||||||
@@ -91,10 +90,10 @@ impl Detection {
|
|||||||
let errmsg_body =
|
let errmsg_body =
|
||||||
format!("Failed to parse rule file. (FilePath : {})", rule.rulepath);
|
format!("Failed to parse rule file. (FilePath : {})", rule.rulepath);
|
||||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||||
AlertMessage::warn(&mut std::io::stdout().lock(), &errmsg_body).ok();
|
AlertMessage::warn(&errmsg_body).ok();
|
||||||
|
|
||||||
err_msgs.iter().for_each(|err_msg| {
|
err_msgs.iter().for_each(|err_msg| {
|
||||||
AlertMessage::warn(&mut std::io::stdout().lock(), err_msg).ok();
|
AlertMessage::warn(err_msg).ok();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if !*QUIET_ERRORS_FLAG {
|
if !*QUIET_ERRORS_FLAG {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ extern crate lazy_static;
|
|||||||
use crate::detections::configs;
|
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 crate::detections::utils::write_color_buffer;
|
||||||
use chrono::{DateTime, Local, TimeZone, Utc};
|
use chrono::{DateTime, Local, TimeZone, Utc};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
@@ -15,6 +16,7 @@ use std::io::BufWriter;
|
|||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use termcolor::{BufferWriter, ColorChoice};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
@@ -108,11 +110,7 @@ impl Message {
|
|||||||
}
|
}
|
||||||
let read_result = utils::read_csv(path);
|
let read_result = utils::read_csv(path);
|
||||||
if read_result.is_err() {
|
if read_result.is_err() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(read_result.as_ref().unwrap_err()).ok();
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
read_result.as_ref().unwrap_err(),
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
return HashMap::default();
|
return HashMap::default();
|
||||||
}
|
}
|
||||||
read_result.unwrap().into_iter().for_each(|line| {
|
read_result.unwrap().into_iter().for_each(|line| {
|
||||||
@@ -265,13 +263,21 @@ impl AlertMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// ERRORメッセージを表示する関数
|
/// ERRORメッセージを表示する関数
|
||||||
pub fn alert<W: Write>(w: &mut W, contents: &str) -> io::Result<()> {
|
pub fn alert(contents: &str) -> io::Result<()> {
|
||||||
writeln!(w, "[ERROR] {}", contents)
|
write_color_buffer(
|
||||||
|
BufferWriter::stderr(ColorChoice::Always),
|
||||||
|
None,
|
||||||
|
&format!("[ERROR] {}", contents),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// WARNメッセージを表示する関数
|
/// WARNメッセージを表示する関数
|
||||||
pub fn warn<W: Write>(w: &mut W, contents: &str) -> io::Result<()> {
|
pub fn warn(contents: &str) -> io::Result<()> {
|
||||||
writeln!(w, "[WARN] {}", contents)
|
write_color_buffer(
|
||||||
|
BufferWriter::stderr(ColorChoice::Always),
|
||||||
|
None,
|
||||||
|
&format!("[WARN] {}", contents),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +287,6 @@ mod tests {
|
|||||||
use crate::detections::print::{AlertMessage, Message};
|
use crate::detections::print::{AlertMessage, Message};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::io::BufWriter;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_and_append_message() {
|
fn test_create_and_append_message() {
|
||||||
@@ -422,15 +427,13 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_error_message() {
|
fn test_error_message() {
|
||||||
let input = "TEST!";
|
let input = "TEST!";
|
||||||
AlertMessage::alert(&mut BufWriter::new(std::io::stdout().lock()), input)
|
AlertMessage::alert(input).expect("[ERROR] TEST!");
|
||||||
.expect("[ERROR] TEST!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_warn_message() {
|
fn test_warn_message() {
|
||||||
let input = "TESTWarn!";
|
let input = "TESTWarn!";
|
||||||
AlertMessage::warn(&mut BufWriter::new(std::io::stdout().lock()), input)
|
AlertMessage::warn(input).expect("[WARN] TESTWarn!");
|
||||||
.expect("[WARN] TESTWarn!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ use crate::detections::rule::RuleNode;
|
|||||||
use chrono::{DateTime, TimeZone, Utc};
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::io::BufWriter;
|
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
@@ -88,7 +87,7 @@ fn get_alias_value_in_record(
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||||
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), &errmsg).ok();
|
AlertMessage::alert(&errmsg).ok();
|
||||||
}
|
}
|
||||||
if !*QUIET_ERRORS_FLAG {
|
if !*QUIET_ERRORS_FLAG {
|
||||||
ERROR_LOG_STACK
|
ERROR_LOG_STACK
|
||||||
@@ -190,7 +189,7 @@ impl TimeFrameInfo {
|
|||||||
} else {
|
} else {
|
||||||
let errmsg = format!("Timeframe is invalid. Input value:{}", value);
|
let errmsg = format!("Timeframe is invalid. Input value:{}", value);
|
||||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||||
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), &errmsg).ok();
|
AlertMessage::alert(&errmsg).ok();
|
||||||
}
|
}
|
||||||
if !*QUIET_ERRORS_FLAG {
|
if !*QUIET_ERRORS_FLAG {
|
||||||
ERROR_LOG_STACK
|
ERROR_LOG_STACK
|
||||||
@@ -226,7 +225,7 @@ pub fn get_sec_timeframe(rule: &RuleNode) -> Option<i64> {
|
|||||||
Err(err) => {
|
Err(err) => {
|
||||||
let errmsg = format!("Timeframe number is invalid. timeframe. {}", err);
|
let errmsg = format!("Timeframe number is invalid. timeframe. {}", err);
|
||||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||||
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), &errmsg).ok();
|
AlertMessage::alert(&errmsg).ok();
|
||||||
}
|
}
|
||||||
if !*QUIET_ERRORS_FLAG {
|
if !*QUIET_ERRORS_FLAG {
|
||||||
ERROR_LOG_STACK
|
ERROR_LOG_STACK
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ extern crate csv;
|
|||||||
extern crate regex;
|
extern crate regex;
|
||||||
|
|
||||||
use crate::detections::configs;
|
use crate::detections::configs;
|
||||||
|
use termcolor::Color;
|
||||||
|
|
||||||
use tokio::runtime::Builder;
|
use tokio::runtime::Builder;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
@@ -12,11 +13,13 @@ use regex::Regex;
|
|||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use std::io;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use std::vec;
|
use std::vec;
|
||||||
|
use termcolor::{BufferWriter, ColorSpec, WriteColor};
|
||||||
|
|
||||||
use super::detection::EvtxRecordInfo;
|
use super::detection::EvtxRecordInfo;
|
||||||
|
|
||||||
@@ -239,6 +242,20 @@ pub fn create_rec_info(data: Value, path: String, keys: &[String]) -> EvtxRecord
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 標準出力のカラー出力設定を指定した値に変更し画面出力を行う関数
|
||||||
|
*/
|
||||||
|
pub fn write_color_buffer(
|
||||||
|
wtr: BufferWriter,
|
||||||
|
color: Option<Color>,
|
||||||
|
output_str: &str,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
let mut buf = wtr.buffer();
|
||||||
|
buf.set_color(ColorSpec::new().set_fg(color)).ok();
|
||||||
|
writeln!(buf, "{}", output_str).ok();
|
||||||
|
wtr.print(&buf)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSVのrecord infoカラムに出力する文字列を作る
|
* CSVのrecord infoカラムに出力する文字列を作る
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use crate::detections::print::QUIET_ERRORS_FLAG;
|
|||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::BufWriter;
|
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -55,11 +54,7 @@ impl RuleExclude {
|
|||||||
let f = File::open(filename);
|
let f = File::open(filename);
|
||||||
if f.is_err() {
|
if f.is_err() {
|
||||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||||
AlertMessage::warn(
|
AlertMessage::warn(&format!("{} does not exist", filename)).ok();
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
&format!("{} does not exist", filename),
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
}
|
}
|
||||||
if !*QUIET_ERRORS_FLAG {
|
if !*QUIET_ERRORS_FLAG {
|
||||||
ERROR_LOG_STACK
|
ERROR_LOG_STACK
|
||||||
|
|||||||
180
src/main.rs
180
src/main.rs
@@ -19,12 +19,12 @@ use hayabusa::detections::print::{
|
|||||||
QUIET_ERRORS_FLAG, STATISTICS_FLAG,
|
QUIET_ERRORS_FLAG, STATISTICS_FLAG,
|
||||||
};
|
};
|
||||||
use hayabusa::detections::rule::{get_detection_keys, RuleNode};
|
use hayabusa::detections::rule::{get_detection_keys, RuleNode};
|
||||||
use hayabusa::filter;
|
|
||||||
use hayabusa::omikuji::Omikuji;
|
use hayabusa::omikuji::Omikuji;
|
||||||
use hayabusa::options::level_tuning::LevelTuning;
|
use hayabusa::options::level_tuning::LevelTuning;
|
||||||
use hayabusa::yaml::ParseYaml;
|
use hayabusa::yaml::ParseYaml;
|
||||||
use hayabusa::{afterfact::after_fact, detections::utils};
|
use hayabusa::{afterfact::after_fact, detections::utils};
|
||||||
use hayabusa::{detections::configs, timeline::timelines::Timeline};
|
use hayabusa::{detections::configs, timeline::timelines::Timeline};
|
||||||
|
use hayabusa::{detections::utils::write_color_buffer, filter};
|
||||||
use hhmmss::Hhmmss;
|
use hhmmss::Hhmmss;
|
||||||
use pbr::ProgressBar;
|
use pbr::ProgressBar;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
@@ -42,6 +42,7 @@ use std::{
|
|||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
vec,
|
vec,
|
||||||
};
|
};
|
||||||
|
use termcolor::{BufferWriter, Color, ColorChoice};
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
use tokio::spawn;
|
use tokio::spawn;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
@@ -88,7 +89,12 @@ impl App {
|
|||||||
if std::env::args().len() == 1 {
|
if std::env::args().len() == 1 {
|
||||||
self.output_logo();
|
self.output_logo();
|
||||||
println!();
|
println!();
|
||||||
println!("{}", configs::CONFIG.read().unwrap().args.usage());
|
write_color_buffer(
|
||||||
|
BufferWriter::stdout(ColorChoice::Always),
|
||||||
|
None,
|
||||||
|
configs::CONFIG.read().unwrap().args.usage(),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
println!();
|
println!();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -105,7 +111,6 @@ impl App {
|
|||||||
|
|
||||||
if !self.is_matched_architecture_and_binary() {
|
if !self.is_matched_architecture_and_binary() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
"The hayabusa version you ran does not match your PC architecture.\nPlease use the correct architecture. (Binary ending in -x64.exe for 64-bit and -x86.exe for 32-bit.)",
|
"The hayabusa version you ran does not match your PC architecture.\nPlease use the correct architecture. (Binary ending in -x64.exe for 64-bit and -x86.exe for 32-bit.)",
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
@@ -122,15 +127,16 @@ impl App {
|
|||||||
match self.update_rules() {
|
match self.update_rules() {
|
||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
if output != "You currently have the latest rules." {
|
if output != "You currently have the latest rules." {
|
||||||
println!("Rules updated successfully.");
|
write_color_buffer(
|
||||||
|
BufferWriter::stdout(ColorChoice::Always),
|
||||||
|
None,
|
||||||
|
"Rules updated successfully.",
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(&format!("Failed to update rules. {:?} ", e)).ok();
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
&format!("Failed to update rules. {:?} ", e),
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
@@ -139,7 +145,6 @@ impl App {
|
|||||||
|
|
||||||
if !Path::new("./config").exists() {
|
if !Path::new("./config").exists() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
"Hayabusa could not find the config directory.\nPlease run it from the Hayabusa root directory.\nExample: ./hayabusa-1.0.0-windows-x64.exe"
|
"Hayabusa could not find the config directory.\nPlease run it from the Hayabusa root directory.\nExample: ./hayabusa-1.0.0-windows-x64.exe"
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
@@ -150,25 +155,19 @@ impl App {
|
|||||||
for (key, _) in PIVOT_KEYWORD.read().unwrap().iter() {
|
for (key, _) in PIVOT_KEYWORD.read().unwrap().iter() {
|
||||||
let keywords_file_name = csv_path.to_owned() + "-" + key + ".txt";
|
let keywords_file_name = csv_path.to_owned() + "-" + key + ".txt";
|
||||||
if Path::new(&keywords_file_name).exists() {
|
if Path::new(&keywords_file_name).exists() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(&format!(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
" The file {} already exists. Please specify a different filename.",
|
||||||
&format!(
|
&keywords_file_name
|
||||||
" The file {} already exists. Please specify a different filename.",
|
))
|
||||||
&keywords_file_name
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.ok();
|
.ok();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if Path::new(csv_path).exists() {
|
if Path::new(csv_path).exists() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(&format!(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
" The file {} already exists. Please specify a different filename.",
|
||||||
&format!(
|
csv_path
|
||||||
" The file {} already exists. Please specify a different filename.",
|
))
|
||||||
csv_path
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.ok();
|
.ok();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -180,11 +179,21 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if *STATISTICS_FLAG {
|
if *STATISTICS_FLAG {
|
||||||
println!("Generating Event ID Statistics");
|
write_color_buffer(
|
||||||
|
BufferWriter::stdout(ColorChoice::Always),
|
||||||
|
None,
|
||||||
|
"Generating Event ID Statistics",
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
if *LOGONSUMMARY_FLAG {
|
if *LOGONSUMMARY_FLAG {
|
||||||
println!("Generating Logons Summary");
|
write_color_buffer(
|
||||||
|
BufferWriter::stdout(ColorChoice::Always),
|
||||||
|
None,
|
||||||
|
"Generating Logons Summary",
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
if configs::CONFIG
|
if configs::CONFIG
|
||||||
@@ -209,7 +218,6 @@ impl App {
|
|||||||
.starts_with('.')
|
.starts_with('.')
|
||||||
{
|
{
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
"--filepath only accepts .evtx files. Hidden files are ignored.",
|
"--filepath only accepts .evtx files. Hidden files are ignored.",
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
@@ -219,11 +227,7 @@ impl App {
|
|||||||
} else if let Some(directory) = configs::CONFIG.read().unwrap().args.value_of("directory") {
|
} else if let Some(directory) = configs::CONFIG.read().unwrap().args.value_of("directory") {
|
||||||
let evtx_files = self.collect_evtxfiles(directory);
|
let evtx_files = self.collect_evtxfiles(directory);
|
||||||
if evtx_files.is_empty() {
|
if evtx_files.is_empty() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert("No .evtx files were found.").ok();
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
"No .evtx files were found.",
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.analysis_files(evtx_files, &time_filter);
|
self.analysis_files(evtx_files, &time_filter);
|
||||||
@@ -262,11 +266,10 @@ impl App {
|
|||||||
.value_of("rules")
|
.value_of("rules")
|
||||||
.unwrap_or("rules"),
|
.unwrap_or("rules"),
|
||||||
) {
|
) {
|
||||||
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), &err).ok();
|
AlertMessage::alert(&err).ok();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
"Need rule_levels.txt file to use --level-tuning option [default: ./rules/config/level_tuning.txt]",
|
"Need rule_levels.txt file to use --level-tuning option [default: ./rules/config/level_tuning.txt]",
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
@@ -277,7 +280,12 @@ impl App {
|
|||||||
let analysis_end_time: DateTime<Local> = Local::now();
|
let analysis_end_time: DateTime<Local> = Local::now();
|
||||||
let analysis_duration = analysis_end_time.signed_duration_since(analysis_start_time);
|
let analysis_duration = analysis_end_time.signed_duration_since(analysis_start_time);
|
||||||
println!();
|
println!();
|
||||||
println!("Elapsed Time: {}", &analysis_duration.hhmmssxxx());
|
write_color_buffer(
|
||||||
|
BufferWriter::stdout(ColorChoice::Always),
|
||||||
|
None,
|
||||||
|
&format!("Elapsed Time: {}", &analysis_duration.hhmmssxxx()),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
// Qオプションを付けた場合もしくはパースのエラーがない場合はerrorのstackが9となるのでエラーログファイル自体が生成されない。
|
// Qオプションを付けた場合もしくはパースのエラーがない場合はerrorのstackが9となるのでエラーログファイル自体が生成されない。
|
||||||
@@ -315,7 +323,7 @@ impl App {
|
|||||||
for (key, _) in PIVOT_KEYWORD.read().unwrap().iter() {
|
for (key, _) in PIVOT_KEYWORD.read().unwrap().iter() {
|
||||||
output += &(pivot_file.to_owned() + "-" + key + ".txt" + "\n");
|
output += &(pivot_file.to_owned() + "-" + key + ".txt" + "\n");
|
||||||
}
|
}
|
||||||
println!("{}", output);
|
write_color_buffer(BufferWriter::stdout(ColorChoice::Always), None, &output).ok();
|
||||||
} else {
|
} else {
|
||||||
//標準出力の場合
|
//標準出力の場合
|
||||||
let mut output = "The following pivot keywords were found:\n".to_string();
|
let mut output = "The following pivot keywords were found:\n".to_string();
|
||||||
@@ -335,18 +343,16 @@ impl App {
|
|||||||
|
|
||||||
output += "\n";
|
output += "\n";
|
||||||
}
|
}
|
||||||
print!("{}", output);
|
write_color_buffer(BufferWriter::stdout(ColorChoice::Always), None, &output).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
fn collect_liveanalysis_files(&self) -> Option<Vec<PathBuf>> {
|
fn collect_liveanalysis_files(&self) -> Option<Vec<PathBuf>> {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert("-l / --liveanalysis needs to be run as Administrator on Windows.")
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
.ok();
|
||||||
"-l / --liveanalysis needs to be run as Administrator on Windows.\r\n",
|
println!();
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,20 +363,14 @@ impl App {
|
|||||||
let evtx_files =
|
let evtx_files =
|
||||||
self.collect_evtxfiles(&[log_dir, "System32\\winevt\\Logs".to_string()].join("/"));
|
self.collect_evtxfiles(&[log_dir, "System32\\winevt\\Logs".to_string()].join("/"));
|
||||||
if evtx_files.is_empty() {
|
if evtx_files.is_empty() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert("No .evtx files were found.").ok();
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
"No .evtx files were found.",
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(evtx_files)
|
Some(evtx_files)
|
||||||
} else {
|
} else {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert("-l / --liveanalysis needs to be run as Administrator on Windows.")
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
.ok();
|
||||||
"-l / --liveanalysis needs to be run as Administrator on Windows.\r\n",
|
println!();
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -380,7 +380,7 @@ impl App {
|
|||||||
if entries.is_err() {
|
if entries.is_err() {
|
||||||
let errmsg = format!("{}", entries.unwrap_err());
|
let errmsg = format!("{}", entries.unwrap_err());
|
||||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||||
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), &errmsg).ok();
|
AlertMessage::alert(&errmsg).ok();
|
||||||
}
|
}
|
||||||
if !*QUIET_ERRORS_FLAG {
|
if !*QUIET_ERRORS_FLAG {
|
||||||
ERROR_LOG_STACK
|
ERROR_LOG_STACK
|
||||||
@@ -426,11 +426,7 @@ impl App {
|
|||||||
match fs::read_to_string("./contributors.txt") {
|
match fs::read_to_string("./contributors.txt") {
|
||||||
Ok(contents) => println!("{}", contents),
|
Ok(contents) => println!("{}", contents),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(&format!("{}", err)).ok();
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
&format!("{}", err),
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -443,7 +439,12 @@ impl App {
|
|||||||
.value_of("min-level")
|
.value_of("min-level")
|
||||||
.unwrap_or("informational")
|
.unwrap_or("informational")
|
||||||
.to_uppercase();
|
.to_uppercase();
|
||||||
println!("Analyzing event files: {:?}", evtx_files.len());
|
write_color_buffer(
|
||||||
|
BufferWriter::stdout(ColorChoice::Always),
|
||||||
|
None,
|
||||||
|
&format!("Analyzing event files: {:?}", evtx_files.len()),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
|
||||||
let mut total_file_size = ByteSize::b(0);
|
let mut total_file_size = ByteSize::b(0);
|
||||||
for file_path in &evtx_files {
|
for file_path in &evtx_files {
|
||||||
@@ -461,7 +462,6 @@ impl App {
|
|||||||
|
|
||||||
if rule_files.is_empty() {
|
if rule_files.is_empty() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
"No rules were loaded. Please download the latest rules with the --update-rules option.\r\n",
|
"No rules were loaded. Please download the latest rules with the --update-rules option.\r\n",
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
@@ -524,8 +524,7 @@ impl App {
|
|||||||
record_result.unwrap_err()
|
record_result.unwrap_err()
|
||||||
);
|
);
|
||||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||||
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), &errmsg)
|
AlertMessage::alert(&errmsg).ok();
|
||||||
.ok();
|
|
||||||
}
|
}
|
||||||
if !*QUIET_ERRORS_FLAG {
|
if !*QUIET_ERRORS_FLAG {
|
||||||
ERROR_LOG_STACK
|
ERROR_LOG_STACK
|
||||||
@@ -654,7 +653,17 @@ impl App {
|
|||||||
fn output_logo(&self) {
|
fn output_logo(&self) {
|
||||||
let fp = &"art/logo.txt".to_string();
|
let fp = &"art/logo.txt".to_string();
|
||||||
let content = fs::read_to_string(fp).unwrap_or_default();
|
let content = fs::read_to_string(fp).unwrap_or_default();
|
||||||
println!("{}", content);
|
let output_color = if configs::CONFIG.read().unwrap().args.is_present("no-color") {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Color::Green)
|
||||||
|
};
|
||||||
|
write_color_buffer(
|
||||||
|
BufferWriter::stdout(ColorChoice::Always),
|
||||||
|
output_color,
|
||||||
|
&content,
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// output easter egg arts
|
/// output easter egg arts
|
||||||
@@ -669,7 +678,7 @@ impl App {
|
|||||||
None => {}
|
None => {}
|
||||||
Some(path) => {
|
Some(path) => {
|
||||||
let content = fs::read_to_string(path).unwrap_or_default();
|
let content = fs::read_to_string(path).unwrap_or_default();
|
||||||
println!("{}", content);
|
write_color_buffer(BufferWriter::stdout(ColorChoice::Always), None, &content).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -682,9 +691,12 @@ impl App {
|
|||||||
let hayabusa_repo = Repository::open(Path::new("."));
|
let hayabusa_repo = Repository::open(Path::new("."));
|
||||||
let hayabusa_rule_repo = Repository::open(Path::new("rules"));
|
let hayabusa_rule_repo = Repository::open(Path::new("rules"));
|
||||||
if hayabusa_repo.is_err() && hayabusa_rule_repo.is_err() {
|
if hayabusa_repo.is_err() && hayabusa_rule_repo.is_err() {
|
||||||
println!(
|
write_color_buffer(
|
||||||
"Attempting to git clone the hayabusa-rules repository into the rules folder."
|
BufferWriter::stdout(ColorChoice::Always),
|
||||||
);
|
None,
|
||||||
|
"Attempting to git clone the hayabusa-rules repository into the rules folder.",
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
// execution git clone of hayabusa-rules repository when failed open hayabusa repository.
|
// execution git clone of hayabusa-rules repository when failed open hayabusa repository.
|
||||||
result = self.clone_rules();
|
result = self.clone_rules();
|
||||||
} else if hayabusa_rule_repo.is_ok() {
|
} else if hayabusa_rule_repo.is_ok() {
|
||||||
@@ -712,11 +724,7 @@ impl App {
|
|||||||
submodule.update(true, None)?;
|
submodule.update(true, None)?;
|
||||||
let submodule_repo = submodule.open()?;
|
let submodule_repo = submodule.open()?;
|
||||||
if let Err(e) = self.pull_repository(&submodule_repo) {
|
if let Err(e) = self.pull_repository(&submodule_repo) {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(&format!("Failed submodule update. {}", e)).ok();
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
&format!("Failed submodule update. {}", e),
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
is_success_submodule_update = false;
|
is_success_submodule_update = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -753,11 +761,7 @@ impl App {
|
|||||||
.find_remote("origin")?
|
.find_remote("origin")?
|
||||||
.fetch(&["main"], None, None)
|
.fetch(&["main"], None, None)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(&format!("Failed git fetch to rules folder. {}", e)).ok();
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
&format!("Failed git fetch to rules folder. {}", e),
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
}) {
|
}) {
|
||||||
Ok(it) => it,
|
Ok(it) => it,
|
||||||
Err(_err) => return Err(git2::Error::from_str(&String::default())),
|
Err(_err) => return Err(git2::Error::from_str(&String::default())),
|
||||||
@@ -775,7 +779,6 @@ impl App {
|
|||||||
Ok("Finished fast forward merge.".to_string())
|
Ok("Finished fast forward merge.".to_string())
|
||||||
} else if analysis.0.is_normal() {
|
} else if analysis.0.is_normal() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
"update-rules option is git Fast-Forward merge only. please check your rules folder."
|
"update-rules option is git Fast-Forward merge only. please check your rules folder."
|
||||||
,
|
,
|
||||||
).ok();
|
).ok();
|
||||||
@@ -797,7 +800,6 @@ impl App {
|
|||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
|
||||||
&format!(
|
&format!(
|
||||||
"Failed to git clone into the rules folder. Please rename your rules folder name. {}",
|
"Failed to git clone into the rules folder. Please rename your rules folder name. {}",
|
||||||
e
|
e
|
||||||
@@ -868,10 +870,15 @@ impl App {
|
|||||||
*update_count_by_rule_type
|
*update_count_by_rule_type
|
||||||
.entry(tmp[3].to_string())
|
.entry(tmp[3].to_string())
|
||||||
.or_insert(0b0) += 1;
|
.or_insert(0b0) += 1;
|
||||||
println!(
|
write_color_buffer(
|
||||||
"[Updated] {} (Modified: {} | Path: {})",
|
BufferWriter::stdout(ColorChoice::Always),
|
||||||
tmp[0], tmp[1], tmp[2]
|
None,
|
||||||
);
|
&format!(
|
||||||
|
"[Updated] {} (Modified: {} | Path: {})",
|
||||||
|
tmp[0], tmp[1], tmp[2]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
for (key, value) in &update_count_by_rule_type {
|
for (key, value) in &update_count_by_rule_type {
|
||||||
@@ -880,7 +887,12 @@ impl App {
|
|||||||
if !&update_count_by_rule_type.is_empty() {
|
if !&update_count_by_rule_type.is_empty() {
|
||||||
Ok("Rule updated".to_string())
|
Ok("Rule updated".to_string())
|
||||||
} else {
|
} else {
|
||||||
println!("You currently have the latest rules.");
|
write_color_buffer(
|
||||||
|
BufferWriter::stdout(ColorChoice::Always),
|
||||||
|
None,
|
||||||
|
"You currently have the latest rules.",
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
Ok("You currently have the latest rules.".to_string())
|
Ok("You currently have the latest rules.".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
use crate::detections::utils::write_color_buffer;
|
||||||
use crate::detections::{configs, utils};
|
use crate::detections::{configs, utils};
|
||||||
use crate::filter::RuleExclude;
|
use crate::filter::RuleExclude;
|
||||||
use crate::yaml::ParseYaml;
|
use crate::yaml::ParseYaml;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use termcolor::{BufferWriter, ColorChoice};
|
||||||
|
|
||||||
pub struct LevelTuning {}
|
pub struct LevelTuning {}
|
||||||
|
|
||||||
impl LevelTuning {
|
impl LevelTuning {
|
||||||
@@ -55,7 +58,12 @@ impl LevelTuning {
|
|||||||
// Convert rule files
|
// Convert rule files
|
||||||
for (path, rule) in rulefile_loader.files {
|
for (path, rule) in rulefile_loader.files {
|
||||||
if let Some(new_level) = tuning_map.get(rule["id"].as_str().unwrap()) {
|
if let Some(new_level) = tuning_map.get(rule["id"].as_str().unwrap()) {
|
||||||
println!("path: {}", path);
|
write_color_buffer(
|
||||||
|
BufferWriter::stdout(ColorChoice::Always),
|
||||||
|
None,
|
||||||
|
&format!("path: {}", path),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
let mut content = match fs::read_to_string(&path) {
|
let mut content = match fs::read_to_string(&path) {
|
||||||
Ok(_content) => _content,
|
Ok(_content) => _content,
|
||||||
Err(e) => return Result::Err(e.to_string()),
|
Err(e) => return Result::Err(e.to_string()),
|
||||||
@@ -85,11 +93,16 @@ impl LevelTuning {
|
|||||||
|
|
||||||
file.write_all(content.as_bytes()).unwrap();
|
file.write_all(content.as_bytes()).unwrap();
|
||||||
file.flush().unwrap();
|
file.flush().unwrap();
|
||||||
println!(
|
write_color_buffer(
|
||||||
"level: {} -> {}",
|
BufferWriter::stdout(ColorChoice::Always),
|
||||||
rule["level"].as_str().unwrap(),
|
None,
|
||||||
new_level
|
&format!(
|
||||||
);
|
"level: {} -> {}",
|
||||||
|
rule["level"].as_str().unwrap(),
|
||||||
|
new_level
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Result::Ok(())
|
Result::Ok(())
|
||||||
|
|||||||
11
src/yaml.rs
11
src/yaml.rs
@@ -10,7 +10,6 @@ use hashbrown::HashMap;
|
|||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::BufWriter;
|
|
||||||
use std::io::{BufReader, Read};
|
use std::io::{BufReader, Read};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use yaml_rust::Yaml;
|
use yaml_rust::Yaml;
|
||||||
@@ -65,7 +64,7 @@ impl ParseYaml {
|
|||||||
path.as_ref().to_path_buf().display(),
|
path.as_ref().to_path_buf().display(),
|
||||||
);
|
);
|
||||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||||
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), &errmsg)?;
|
AlertMessage::alert(&errmsg)?;
|
||||||
}
|
}
|
||||||
if !*QUIET_ERRORS_FLAG {
|
if !*QUIET_ERRORS_FLAG {
|
||||||
ERROR_LOG_STACK
|
ERROR_LOG_STACK
|
||||||
@@ -97,7 +96,7 @@ impl ParseYaml {
|
|||||||
read_content.unwrap_err()
|
read_content.unwrap_err()
|
||||||
);
|
);
|
||||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||||
AlertMessage::warn(&mut BufWriter::new(std::io::stderr().lock()), &errmsg)?;
|
AlertMessage::warn(&errmsg)?;
|
||||||
}
|
}
|
||||||
if !*QUIET_ERRORS_FLAG {
|
if !*QUIET_ERRORS_FLAG {
|
||||||
ERROR_LOG_STACK
|
ERROR_LOG_STACK
|
||||||
@@ -118,7 +117,7 @@ impl ParseYaml {
|
|||||||
yaml_contents.unwrap_err()
|
yaml_contents.unwrap_err()
|
||||||
);
|
);
|
||||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||||
AlertMessage::warn(&mut BufWriter::new(std::io::stderr().lock()), &errmsg)?;
|
AlertMessage::warn(&errmsg)?;
|
||||||
}
|
}
|
||||||
if !*QUIET_ERRORS_FLAG {
|
if !*QUIET_ERRORS_FLAG {
|
||||||
ERROR_LOG_STACK
|
ERROR_LOG_STACK
|
||||||
@@ -170,7 +169,7 @@ impl ParseYaml {
|
|||||||
read_content.unwrap_err()
|
read_content.unwrap_err()
|
||||||
);
|
);
|
||||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||||
AlertMessage::warn(&mut BufWriter::new(std::io::stderr().lock()), &errmsg)?;
|
AlertMessage::warn(&errmsg)?;
|
||||||
}
|
}
|
||||||
if !*QUIET_ERRORS_FLAG {
|
if !*QUIET_ERRORS_FLAG {
|
||||||
ERROR_LOG_STACK
|
ERROR_LOG_STACK
|
||||||
@@ -191,7 +190,7 @@ impl ParseYaml {
|
|||||||
yaml_contents.unwrap_err()
|
yaml_contents.unwrap_err()
|
||||||
);
|
);
|
||||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||||
AlertMessage::warn(&mut BufWriter::new(std::io::stderr().lock()), &errmsg)?;
|
AlertMessage::warn(&errmsg)?;
|
||||||
}
|
}
|
||||||
if !*QUIET_ERRORS_FLAG {
|
if !*QUIET_ERRORS_FLAG {
|
||||||
ERROR_LOG_STACK
|
ERROR_LOG_STACK
|
||||||
|
|||||||
Reference in New Issue
Block a user