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)
|
||||
- 読み込んだイベント数と検知しなかったイベント数を表示するようにした。 (#538) (@hitenkoku)
|
||||
- 新しいロゴ。 (@YamatoSecurity)
|
||||
- 新しいロゴに変更した。(#536) (@YamatoSecurity)
|
||||
- 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)
|
||||
- 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)
|
||||
- Changed logo color. (#537) (@hitenkoku)
|
||||
|
||||
**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::AlertMessage;
|
||||
use crate::detections::utils;
|
||||
use crate::detections::utils::write_color_buffer;
|
||||
use chrono::{DateTime, Local, TimeZone, Utc};
|
||||
use csv::QuoteStyle;
|
||||
use hashbrown::HashMap;
|
||||
@@ -67,11 +68,7 @@ pub fn set_output_color() -> HashMap<String, Color> {
|
||||
}
|
||||
if read_result.is_err() {
|
||||
// color情報がない場合は通常の白色の出力が出てくるのみで動作への影響を与えない為warnとして処理する
|
||||
AlertMessage::warn(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
read_result.as_ref().unwrap_err(),
|
||||
)
|
||||
.ok();
|
||||
AlertMessage::warn(read_result.as_ref().unwrap_err()).ok();
|
||||
return color_map;
|
||||
}
|
||||
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 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(","))
|
||||
)
|
||||
AlertMessage::warn(&format!(
|
||||
"Failed hex convert in level_color.txt. Color output is disabled. Input Line: {}",
|
||||
line.join(",")
|
||||
))
|
||||
.ok();
|
||||
return;
|
||||
}
|
||||
@@ -93,7 +90,10 @@ pub fn set_output_color() -> HashMap<String, Color> {
|
||||
if level.is_empty() || color_code.len() < 3 {
|
||||
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
|
||||
}
|
||||
@@ -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 mut wtr = buf_wtr.buffer();
|
||||
wtr.set_color(ColorSpec::new().set_fg(None)).ok();
|
||||
|
||||
if timestamps.len() < 5 {
|
||||
write!(
|
||||
writeln!(
|
||||
wtr,
|
||||
"Event Frequency Timeline could not be displayed as there needs to be more than 5 events.",
|
||||
)
|
||||
.ok();
|
||||
writeln!(wtr).ok();
|
||||
buf_wtr.print(&wtr).ok();
|
||||
return;
|
||||
}
|
||||
|
||||
let title = "Event Frequency Timeline";
|
||||
let header_row_space = (length - title.len()) / 2;
|
||||
writeln!(wtr).ok();
|
||||
write!(wtr, "{}", " ".repeat(header_row_space)).ok();
|
||||
writeln!(wtr, "{}", title).ok();
|
||||
writeln!(wtr).ok();
|
||||
println!();
|
||||
writeln!(wtr, "{}{}", " ".repeat(header_row_space), title).ok();
|
||||
println!();
|
||||
|
||||
let timestamp_marker_max = if timestamps.len() < 2 {
|
||||
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) {
|
||||
let fn_emit_csv_err = |err: Box<dyn Error>| {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
&format!("Failed to write CSV. {}", err),
|
||||
)
|
||||
.ok();
|
||||
AlertMessage::alert(&format!("Failed to write CSV. {}", err)).ok();
|
||||
process::exit(1);
|
||||
};
|
||||
|
||||
@@ -177,11 +172,7 @@ pub fn after_fact(all_record_cnt: usize) {
|
||||
match File::create(csv_path) {
|
||||
Ok(file) => Box::new(BufWriter::new(file)),
|
||||
Err(err) => {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
&format!("Failed to open file. {}", err),
|
||||
)
|
||||
.ok();
|
||||
AlertMessage::alert(&format!("Failed to open file. {}", err)).ok();
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
@@ -372,10 +363,6 @@ fn _print_unique_results(
|
||||
tail_word: String,
|
||||
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([
|
||||
"critical",
|
||||
"high",
|
||||
@@ -389,12 +376,15 @@ fn _print_unique_results(
|
||||
counts_by_level.reverse();
|
||||
|
||||
// output total results
|
||||
writeln!(
|
||||
wtr,
|
||||
write_color_buffer(
|
||||
BufferWriter::stdout(ColorChoice::Always),
|
||||
None,
|
||||
&format!(
|
||||
"{} {}: {}",
|
||||
head_word,
|
||||
tail_word,
|
||||
counts_by_level.iter().sum::<u128>()
|
||||
),
|
||||
)
|
||||
.ok();
|
||||
|
||||
@@ -403,12 +393,13 @@ fn _print_unique_results(
|
||||
"{} {} {}: {}",
|
||||
head_word, level_name, tail_word, counts_by_level[i]
|
||||
);
|
||||
|
||||
wtr.set_color(ColorSpec::new().set_fg(_get_output_color(color_map, level_name)))
|
||||
write_color_buffer(
|
||||
BufferWriter::stdout(ColorChoice::Always),
|
||||
_get_output_color(color_map, level_name),
|
||||
&output_raw_str,
|
||||
)
|
||||
.ok();
|
||||
writeln!(wtr, "{}", output_raw_str).ok();
|
||||
}
|
||||
buf_wtr.print(&wtr).ok();
|
||||
}
|
||||
|
||||
fn format_time(time: &DateTime<Utc>) -> String {
|
||||
|
||||
@@ -8,7 +8,6 @@ use hashbrown::HashMap;
|
||||
use hashbrown::HashSet;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use std::io::BufWriter;
|
||||
use std::sync::RwLock;
|
||||
lazy_static! {
|
||||
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 lines = utils::read_txt(path); // ファイルが存在しなければエラーとする
|
||||
if lines.is_err() {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
lines.as_ref().unwrap_err(),
|
||||
)
|
||||
.ok();
|
||||
AlertMessage::alert(lines.as_ref().unwrap_err()).ok();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -195,7 +190,6 @@ impl TargetEventTime {
|
||||
Ok(dt) => Some(dt.with_timezone(&Utc)),
|
||||
Err(_) => {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
"start-timeline field: the timestamp format is not correct.",
|
||||
)
|
||||
.ok();
|
||||
@@ -213,7 +207,6 @@ impl TargetEventTime {
|
||||
Ok(dt) => Some(dt.with_timezone(&Utc)),
|
||||
Err(_) => {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
"end-timeline field: the timestamp format is not correct.",
|
||||
)
|
||||
.ok();
|
||||
@@ -296,11 +289,7 @@ fn load_eventkey_alias(path: &str) -> EventKeyAliasConfig {
|
||||
// eventkey_aliasが読み込めなかったらエラーで終了とする。
|
||||
let read_result = utils::read_csv(path);
|
||||
if read_result.is_err() {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
read_result.as_ref().unwrap_err(),
|
||||
)
|
||||
.ok();
|
||||
AlertMessage::alert(read_result.as_ref().unwrap_err()).ok();
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -332,11 +321,7 @@ fn load_eventkey_alias(path: &str) -> EventKeyAliasConfig {
|
||||
pub fn load_pivot_keywords(path: &str) {
|
||||
let read_result = utils::read_txt(path);
|
||||
if read_result.is_err() {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
read_result.as_ref().unwrap_err(),
|
||||
)
|
||||
.ok();
|
||||
AlertMessage::alert(read_result.as_ref().unwrap_err()).ok();
|
||||
}
|
||||
|
||||
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 read_result = utils::read_csv(path);
|
||||
if read_result.is_err() {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
read_result.as_ref().unwrap_err(),
|
||||
)
|
||||
.ok();
|
||||
AlertMessage::alert(read_result.as_ref().unwrap_err()).ok();
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ use hashbrown;
|
||||
use hashbrown::HashMap;
|
||||
use serde_json::Value;
|
||||
use std::fmt::Write;
|
||||
use std::io::BufWriter;
|
||||
use std::sync::Arc;
|
||||
use tokio::{runtime::Runtime, spawn, task::JoinHandle};
|
||||
|
||||
@@ -69,7 +68,7 @@ impl Detection {
|
||||
if result_readdir.is_err() {
|
||||
let errmsg = format!("{}", result_readdir.unwrap_err());
|
||||
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 {
|
||||
ERROR_LOG_STACK
|
||||
@@ -91,10 +90,10 @@ impl Detection {
|
||||
let errmsg_body =
|
||||
format!("Failed to parse rule file. (FilePath : {})", rule.rulepath);
|
||||
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| {
|
||||
AlertMessage::warn(&mut std::io::stdout().lock(), err_msg).ok();
|
||||
AlertMessage::warn(err_msg).ok();
|
||||
});
|
||||
}
|
||||
if !*QUIET_ERRORS_FLAG {
|
||||
|
||||
@@ -2,6 +2,7 @@ extern crate lazy_static;
|
||||
use crate::detections::configs;
|
||||
use crate::detections::utils;
|
||||
use crate::detections::utils::get_serde_number_to_string;
|
||||
use crate::detections::utils::write_color_buffer;
|
||||
use chrono::{DateTime, Local, TimeZone, Utc};
|
||||
use hashbrown::HashMap;
|
||||
use lazy_static::lazy_static;
|
||||
@@ -15,6 +16,7 @@ use std::io::BufWriter;
|
||||
use std::io::{self, Write};
|
||||
use std::path::Path;
|
||||
use std::sync::Mutex;
|
||||
use termcolor::{BufferWriter, ColorChoice};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Message {
|
||||
@@ -108,11 +110,7 @@ impl Message {
|
||||
}
|
||||
let read_result = utils::read_csv(path);
|
||||
if read_result.is_err() {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
read_result.as_ref().unwrap_err(),
|
||||
)
|
||||
.ok();
|
||||
AlertMessage::alert(read_result.as_ref().unwrap_err()).ok();
|
||||
return HashMap::default();
|
||||
}
|
||||
read_result.unwrap().into_iter().for_each(|line| {
|
||||
@@ -265,13 +263,21 @@ impl AlertMessage {
|
||||
}
|
||||
|
||||
/// ERRORメッセージを表示する関数
|
||||
pub fn alert<W: Write>(w: &mut W, contents: &str) -> io::Result<()> {
|
||||
writeln!(w, "[ERROR] {}", contents)
|
||||
pub fn alert(contents: &str) -> io::Result<()> {
|
||||
write_color_buffer(
|
||||
BufferWriter::stderr(ColorChoice::Always),
|
||||
None,
|
||||
&format!("[ERROR] {}", contents),
|
||||
)
|
||||
}
|
||||
|
||||
/// WARNメッセージを表示する関数
|
||||
pub fn warn<W: Write>(w: &mut W, contents: &str) -> io::Result<()> {
|
||||
writeln!(w, "[WARN] {}", contents)
|
||||
pub fn warn(contents: &str) -> io::Result<()> {
|
||||
write_color_buffer(
|
||||
BufferWriter::stderr(ColorChoice::Always),
|
||||
None,
|
||||
&format!("[WARN] {}", contents),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +287,6 @@ mod tests {
|
||||
use crate::detections::print::{AlertMessage, Message};
|
||||
use hashbrown::HashMap;
|
||||
use serde_json::Value;
|
||||
use std::io::BufWriter;
|
||||
|
||||
#[test]
|
||||
fn test_create_and_append_message() {
|
||||
@@ -422,15 +427,13 @@ mod tests {
|
||||
#[test]
|
||||
fn test_error_message() {
|
||||
let input = "TEST!";
|
||||
AlertMessage::alert(&mut BufWriter::new(std::io::stdout().lock()), input)
|
||||
.expect("[ERROR] TEST!");
|
||||
AlertMessage::alert(input).expect("[ERROR] TEST!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_warn_message() {
|
||||
let input = "TESTWarn!";
|
||||
AlertMessage::warn(&mut BufWriter::new(std::io::stdout().lock()), input)
|
||||
.expect("[WARN] TESTWarn!");
|
||||
AlertMessage::warn(input).expect("[WARN] TESTWarn!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -8,7 +8,6 @@ use crate::detections::rule::RuleNode;
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
use hashbrown::HashMap;
|
||||
use serde_json::Value;
|
||||
use std::io::BufWriter;
|
||||
use std::num::ParseIntError;
|
||||
use std::path::Path;
|
||||
|
||||
@@ -88,7 +87,7 @@ fn get_alias_value_in_record(
|
||||
),
|
||||
};
|
||||
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 {
|
||||
ERROR_LOG_STACK
|
||||
@@ -190,7 +189,7 @@ impl TimeFrameInfo {
|
||||
} else {
|
||||
let errmsg = format!("Timeframe is invalid. Input value:{}", value);
|
||||
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 {
|
||||
ERROR_LOG_STACK
|
||||
@@ -226,7 +225,7 @@ pub fn get_sec_timeframe(rule: &RuleNode) -> Option<i64> {
|
||||
Err(err) => {
|
||||
let errmsg = format!("Timeframe number is invalid. timeframe. {}", err);
|
||||
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 {
|
||||
ERROR_LOG_STACK
|
||||
|
||||
@@ -3,6 +3,7 @@ extern crate csv;
|
||||
extern crate regex;
|
||||
|
||||
use crate::detections::configs;
|
||||
use termcolor::Color;
|
||||
|
||||
use tokio::runtime::Builder;
|
||||
use tokio::runtime::Runtime;
|
||||
@@ -12,11 +13,13 @@ use regex::Regex;
|
||||
use serde_json::Value;
|
||||
use std::cmp::Ordering;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::str;
|
||||
use std::string::String;
|
||||
use std::vec;
|
||||
use termcolor::{BufferWriter, ColorSpec, WriteColor};
|
||||
|
||||
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カラムに出力する文字列を作る
|
||||
*/
|
||||
|
||||
@@ -5,7 +5,6 @@ use crate::detections::print::QUIET_ERRORS_FLAG;
|
||||
use hashbrown::HashSet;
|
||||
use regex::Regex;
|
||||
use std::fs::File;
|
||||
use std::io::BufWriter;
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -55,11 +54,7 @@ impl RuleExclude {
|
||||
let f = File::open(filename);
|
||||
if f.is_err() {
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
AlertMessage::warn(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
&format!("{} does not exist", filename),
|
||||
)
|
||||
.ok();
|
||||
AlertMessage::warn(&format!("{} does not exist", filename)).ok();
|
||||
}
|
||||
if !*QUIET_ERRORS_FLAG {
|
||||
ERROR_LOG_STACK
|
||||
|
||||
164
src/main.rs
164
src/main.rs
@@ -19,12 +19,12 @@ use hayabusa::detections::print::{
|
||||
QUIET_ERRORS_FLAG, STATISTICS_FLAG,
|
||||
};
|
||||
use hayabusa::detections::rule::{get_detection_keys, RuleNode};
|
||||
use hayabusa::filter;
|
||||
use hayabusa::omikuji::Omikuji;
|
||||
use hayabusa::options::level_tuning::LevelTuning;
|
||||
use hayabusa::yaml::ParseYaml;
|
||||
use hayabusa::{afterfact::after_fact, detections::utils};
|
||||
use hayabusa::{detections::configs, timeline::timelines::Timeline};
|
||||
use hayabusa::{detections::utils::write_color_buffer, filter};
|
||||
use hhmmss::Hhmmss;
|
||||
use pbr::ProgressBar;
|
||||
use serde_json::Value;
|
||||
@@ -42,6 +42,7 @@ use std::{
|
||||
path::PathBuf,
|
||||
vec,
|
||||
};
|
||||
use termcolor::{BufferWriter, Color, ColorChoice};
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::spawn;
|
||||
use tokio::task::JoinHandle;
|
||||
@@ -88,7 +89,12 @@ impl App {
|
||||
if std::env::args().len() == 1 {
|
||||
self.output_logo();
|
||||
println!();
|
||||
println!("{}", configs::CONFIG.read().unwrap().args.usage());
|
||||
write_color_buffer(
|
||||
BufferWriter::stdout(ColorChoice::Always),
|
||||
None,
|
||||
configs::CONFIG.read().unwrap().args.usage(),
|
||||
)
|
||||
.ok();
|
||||
println!();
|
||||
return;
|
||||
}
|
||||
@@ -105,7 +111,6 @@ impl App {
|
||||
|
||||
if !self.is_matched_architecture_and_binary() {
|
||||
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.)",
|
||||
)
|
||||
.ok();
|
||||
@@ -122,15 +127,16 @@ impl App {
|
||||
match self.update_rules() {
|
||||
Ok(output) => {
|
||||
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) => {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
&format!("Failed to update rules. {:?} ", e),
|
||||
)
|
||||
.ok();
|
||||
AlertMessage::alert(&format!("Failed to update rules. {:?} ", e)).ok();
|
||||
}
|
||||
}
|
||||
println!();
|
||||
@@ -139,7 +145,6 @@ impl App {
|
||||
|
||||
if !Path::new("./config").exists() {
|
||||
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"
|
||||
)
|
||||
.ok();
|
||||
@@ -150,25 +155,19 @@ impl App {
|
||||
for (key, _) in PIVOT_KEYWORD.read().unwrap().iter() {
|
||||
let keywords_file_name = csv_path.to_owned() + "-" + key + ".txt";
|
||||
if Path::new(&keywords_file_name).exists() {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
&format!(
|
||||
AlertMessage::alert(&format!(
|
||||
" The file {} already exists. Please specify a different filename.",
|
||||
&keywords_file_name
|
||||
),
|
||||
)
|
||||
))
|
||||
.ok();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if Path::new(csv_path).exists() {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
&format!(
|
||||
AlertMessage::alert(&format!(
|
||||
" The file {} already exists. Please specify a different filename.",
|
||||
csv_path
|
||||
),
|
||||
)
|
||||
))
|
||||
.ok();
|
||||
return;
|
||||
}
|
||||
@@ -180,11 +179,21 @@ impl App {
|
||||
}
|
||||
|
||||
if *STATISTICS_FLAG {
|
||||
println!("Generating Event ID Statistics");
|
||||
write_color_buffer(
|
||||
BufferWriter::stdout(ColorChoice::Always),
|
||||
None,
|
||||
"Generating Event ID Statistics",
|
||||
)
|
||||
.ok();
|
||||
println!();
|
||||
}
|
||||
if *LOGONSUMMARY_FLAG {
|
||||
println!("Generating Logons Summary");
|
||||
write_color_buffer(
|
||||
BufferWriter::stdout(ColorChoice::Always),
|
||||
None,
|
||||
"Generating Logons Summary",
|
||||
)
|
||||
.ok();
|
||||
println!();
|
||||
}
|
||||
if configs::CONFIG
|
||||
@@ -209,7 +218,6 @@ impl App {
|
||||
.starts_with('.')
|
||||
{
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
"--filepath only accepts .evtx files. Hidden files are ignored.",
|
||||
)
|
||||
.ok();
|
||||
@@ -219,11 +227,7 @@ impl App {
|
||||
} else if let Some(directory) = configs::CONFIG.read().unwrap().args.value_of("directory") {
|
||||
let evtx_files = self.collect_evtxfiles(directory);
|
||||
if evtx_files.is_empty() {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
"No .evtx files were found.",
|
||||
)
|
||||
.ok();
|
||||
AlertMessage::alert("No .evtx files were found.").ok();
|
||||
return;
|
||||
}
|
||||
self.analysis_files(evtx_files, &time_filter);
|
||||
@@ -262,11 +266,10 @@ impl App {
|
||||
.value_of("rules")
|
||||
.unwrap_or("rules"),
|
||||
) {
|
||||
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), &err).ok();
|
||||
AlertMessage::alert(&err).ok();
|
||||
}
|
||||
} else {
|
||||
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]",
|
||||
)
|
||||
.ok();
|
||||
@@ -277,7 +280,12 @@ impl App {
|
||||
let analysis_end_time: DateTime<Local> = Local::now();
|
||||
let analysis_duration = analysis_end_time.signed_duration_since(analysis_start_time);
|
||||
println!();
|
||||
println!("Elapsed Time: {}", &analysis_duration.hhmmssxxx());
|
||||
write_color_buffer(
|
||||
BufferWriter::stdout(ColorChoice::Always),
|
||||
None,
|
||||
&format!("Elapsed Time: {}", &analysis_duration.hhmmssxxx()),
|
||||
)
|
||||
.ok();
|
||||
println!();
|
||||
|
||||
// Qオプションを付けた場合もしくはパースのエラーがない場合はerrorのstackが9となるのでエラーログファイル自体が生成されない。
|
||||
@@ -315,7 +323,7 @@ impl App {
|
||||
for (key, _) in PIVOT_KEYWORD.read().unwrap().iter() {
|
||||
output += &(pivot_file.to_owned() + "-" + key + ".txt" + "\n");
|
||||
}
|
||||
println!("{}", output);
|
||||
write_color_buffer(BufferWriter::stdout(ColorChoice::Always), None, &output).ok();
|
||||
} else {
|
||||
//標準出力の場合
|
||||
let mut output = "The following pivot keywords were found:\n".to_string();
|
||||
@@ -335,18 +343,16 @@ impl App {
|
||||
|
||||
output += "\n";
|
||||
}
|
||||
print!("{}", output);
|
||||
write_color_buffer(BufferWriter::stdout(ColorChoice::Always), None, &output).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn collect_liveanalysis_files(&self) -> Option<Vec<PathBuf>> {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
"-l / --liveanalysis needs to be run as Administrator on Windows.\r\n",
|
||||
)
|
||||
AlertMessage::alert("-l / --liveanalysis needs to be run as Administrator on Windows.")
|
||||
.ok();
|
||||
println!();
|
||||
None
|
||||
}
|
||||
|
||||
@@ -357,20 +363,14 @@ impl App {
|
||||
let evtx_files =
|
||||
self.collect_evtxfiles(&[log_dir, "System32\\winevt\\Logs".to_string()].join("/"));
|
||||
if evtx_files.is_empty() {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
"No .evtx files were found.",
|
||||
)
|
||||
.ok();
|
||||
AlertMessage::alert("No .evtx files were found.").ok();
|
||||
return None;
|
||||
}
|
||||
Some(evtx_files)
|
||||
} else {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
"-l / --liveanalysis needs to be run as Administrator on Windows.\r\n",
|
||||
)
|
||||
AlertMessage::alert("-l / --liveanalysis needs to be run as Administrator on Windows.")
|
||||
.ok();
|
||||
println!();
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -380,7 +380,7 @@ impl App {
|
||||
if entries.is_err() {
|
||||
let errmsg = format!("{}", entries.unwrap_err());
|
||||
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 {
|
||||
ERROR_LOG_STACK
|
||||
@@ -426,11 +426,7 @@ impl App {
|
||||
match fs::read_to_string("./contributors.txt") {
|
||||
Ok(contents) => println!("{}", contents),
|
||||
Err(err) => {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
&format!("{}", err),
|
||||
)
|
||||
.ok();
|
||||
AlertMessage::alert(&format!("{}", err)).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -443,7 +439,12 @@ impl App {
|
||||
.value_of("min-level")
|
||||
.unwrap_or("informational")
|
||||
.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);
|
||||
for file_path in &evtx_files {
|
||||
@@ -461,7 +462,6 @@ impl App {
|
||||
|
||||
if rule_files.is_empty() {
|
||||
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",
|
||||
)
|
||||
.ok();
|
||||
@@ -524,8 +524,7 @@ impl App {
|
||||
record_result.unwrap_err()
|
||||
);
|
||||
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 {
|
||||
ERROR_LOG_STACK
|
||||
@@ -654,7 +653,17 @@ impl App {
|
||||
fn output_logo(&self) {
|
||||
let fp = &"art/logo.txt".to_string();
|
||||
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
|
||||
@@ -669,7 +678,7 @@ impl App {
|
||||
None => {}
|
||||
Some(path) => {
|
||||
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_rule_repo = Repository::open(Path::new("rules"));
|
||||
if hayabusa_repo.is_err() && hayabusa_rule_repo.is_err() {
|
||||
println!(
|
||||
"Attempting to git clone the hayabusa-rules repository into the rules folder."
|
||||
);
|
||||
write_color_buffer(
|
||||
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.
|
||||
result = self.clone_rules();
|
||||
} else if hayabusa_rule_repo.is_ok() {
|
||||
@@ -712,11 +724,7 @@ impl App {
|
||||
submodule.update(true, None)?;
|
||||
let submodule_repo = submodule.open()?;
|
||||
if let Err(e) = self.pull_repository(&submodule_repo) {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
&format!("Failed submodule update. {}", e),
|
||||
)
|
||||
.ok();
|
||||
AlertMessage::alert(&format!("Failed submodule update. {}", e)).ok();
|
||||
is_success_submodule_update = false;
|
||||
}
|
||||
}
|
||||
@@ -753,11 +761,7 @@ impl App {
|
||||
.find_remote("origin")?
|
||||
.fetch(&["main"], None, None)
|
||||
.map_err(|e| {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
&format!("Failed git fetch to rules folder. {}", e),
|
||||
)
|
||||
.ok();
|
||||
AlertMessage::alert(&format!("Failed git fetch to rules folder. {}", e)).ok();
|
||||
}) {
|
||||
Ok(it) => it,
|
||||
Err(_err) => return Err(git2::Error::from_str(&String::default())),
|
||||
@@ -775,7 +779,6 @@ impl App {
|
||||
Ok("Finished fast forward merge.".to_string())
|
||||
} else if analysis.0.is_normal() {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
"update-rules option is git Fast-Forward merge only. please check your rules folder."
|
||||
,
|
||||
).ok();
|
||||
@@ -797,7 +800,6 @@ impl App {
|
||||
}
|
||||
Err(e) => {
|
||||
AlertMessage::alert(
|
||||
&mut BufWriter::new(std::io::stderr().lock()),
|
||||
&format!(
|
||||
"Failed to git clone into the rules folder. Please rename your rules folder name. {}",
|
||||
e
|
||||
@@ -868,10 +870,15 @@ impl App {
|
||||
*update_count_by_rule_type
|
||||
.entry(tmp[3].to_string())
|
||||
.or_insert(0b0) += 1;
|
||||
println!(
|
||||
write_color_buffer(
|
||||
BufferWriter::stdout(ColorChoice::Always),
|
||||
None,
|
||||
&format!(
|
||||
"[Updated] {} (Modified: {} | Path: {})",
|
||||
tmp[0], tmp[1], tmp[2]
|
||||
);
|
||||
),
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
println!();
|
||||
for (key, value) in &update_count_by_rule_type {
|
||||
@@ -880,7 +887,12 @@ impl App {
|
||||
if !&update_count_by_rule_type.is_empty() {
|
||||
Ok("Rule updated".to_string())
|
||||
} 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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
use crate::detections::utils::write_color_buffer;
|
||||
use crate::detections::{configs, utils};
|
||||
use crate::filter::RuleExclude;
|
||||
use crate::yaml::ParseYaml;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Write;
|
||||
use termcolor::{BufferWriter, ColorChoice};
|
||||
|
||||
pub struct LevelTuning {}
|
||||
|
||||
impl LevelTuning {
|
||||
@@ -55,7 +58,12 @@ impl LevelTuning {
|
||||
// Convert rule files
|
||||
for (path, rule) in rulefile_loader.files {
|
||||
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) {
|
||||
Ok(_content) => _content,
|
||||
Err(e) => return Result::Err(e.to_string()),
|
||||
@@ -85,11 +93,16 @@ impl LevelTuning {
|
||||
|
||||
file.write_all(content.as_bytes()).unwrap();
|
||||
file.flush().unwrap();
|
||||
println!(
|
||||
write_color_buffer(
|
||||
BufferWriter::stdout(ColorChoice::Always),
|
||||
None,
|
||||
&format!(
|
||||
"level: {} -> {}",
|
||||
rule["level"].as_str().unwrap(),
|
||||
new_level
|
||||
);
|
||||
),
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
Result::Ok(())
|
||||
|
||||
11
src/yaml.rs
11
src/yaml.rs
@@ -10,7 +10,6 @@ use hashbrown::HashMap;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::BufWriter;
|
||||
use std::io::{BufReader, Read};
|
||||
use std::path::{Path, PathBuf};
|
||||
use yaml_rust::Yaml;
|
||||
@@ -65,7 +64,7 @@ impl ParseYaml {
|
||||
path.as_ref().to_path_buf().display(),
|
||||
);
|
||||
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 {
|
||||
ERROR_LOG_STACK
|
||||
@@ -97,7 +96,7 @@ impl ParseYaml {
|
||||
read_content.unwrap_err()
|
||||
);
|
||||
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 {
|
||||
ERROR_LOG_STACK
|
||||
@@ -118,7 +117,7 @@ impl ParseYaml {
|
||||
yaml_contents.unwrap_err()
|
||||
);
|
||||
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 {
|
||||
ERROR_LOG_STACK
|
||||
@@ -170,7 +169,7 @@ impl ParseYaml {
|
||||
read_content.unwrap_err()
|
||||
);
|
||||
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 {
|
||||
ERROR_LOG_STACK
|
||||
@@ -191,7 +190,7 @@ impl ParseYaml {
|
||||
yaml_contents.unwrap_err()
|
||||
);
|
||||
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 {
|
||||
ERROR_LOG_STACK
|
||||
|
||||
Reference in New Issue
Block a user