Merge pull request #682 from Yamato-Security/681-bug-custom-config-directory-doesnt-load-target_event_idstxt

Fixed custom config directory doesnt load files
This commit is contained in:
DustInDark
2022-09-05 09:11:29 +09:00
committed by GitHub
8 changed files with 172 additions and 60 deletions

View File

@@ -15,6 +15,7 @@
**バグ修正:** **バグ修正:**
- ログオン情報の要約オプションを追加した場合に、Hayabusaがクラッシュしていたのを修正した。 (#674) (@hitenkoku) - ログオン情報の要約オプションを追加した場合に、Hayabusaがクラッシュしていたのを修正した。 (#674) (@hitenkoku)
- configオプションで指定したルールコンフィグの読み込みができていない問題を修正した。 (#681) (@hitenkoku)
- 結果概要のtotal eventsで読み込んだレコード数が出力されていたのを、検査対象にしているevtxファイルの実際のレコード数に修正した。 (#683) (@hitenkoku) - 結果概要のtotal eventsで読み込んだレコード数が出力されていたのを、検査対象にしているevtxファイルの実際のレコード数に修正した。 (#683) (@hitenkoku)
## v1.5.1 [2022/08/20] ## v1.5.1 [2022/08/20]

View File

@@ -15,6 +15,7 @@
**Bug Fixes:** **Bug Fixes:**
- Hayabusa would crash with `-L` option (logon summary option). (#674) (@hitenkoku) - Hayabusa would crash with `-L` option (logon summary option). (#674) (@hitenkoku)
- Hayabusa would continue to scan without the correct config files but now will print and error and gracefully terminate. (#681) (@hitenkoku)
- Fixed total events from the number of scanned events to actual events in evtx. (#683) (@hitenkoku) - Fixed total events from the number of scanned events to actual events in evtx. (#683) (@hitenkoku)
## v1.5.1 [2022/08/20] ## v1.5.1 [2022/08/20]

View File

@@ -43,9 +43,14 @@ pub struct Colors {
/// level_color.txtファイルを読み込み対応する文字色のマッピングを返却する関数 /// level_color.txtファイルを読み込み対応する文字色のマッピングを返却する関数
pub fn set_output_color() -> HashMap<String, Colors> { pub fn set_output_color() -> HashMap<String, Colors> {
let read_result = utils::read_csv( let read_result = utils::read_csv(
utils::check_setting_path(&CURRENT_EXE_PATH.to_path_buf(), "config/level_color.txt") utils::check_setting_path(
.to_str() &CURRENT_EXE_PATH.to_path_buf(),
.unwrap(), "config/level_color.txt",
true,
)
.unwrap()
.to_str()
.unwrap(),
); );
let mut color_map: HashMap<String, Colors> = HashMap::new(); let mut color_map: HashMap<String, Colors> = HashMap::new();
if configs::CONFIG.read().unwrap().args.no_color { if configs::CONFIG.read().unwrap().args.no_color {

View File

@@ -23,10 +23,23 @@ lazy_static! {
levelmap.insert("CRITICAL".to_owned(), 5); levelmap.insert("CRITICAL".to_owned(), 5);
levelmap levelmap
}; };
pub static ref EVENTKEY_ALIAS: EventKeyAliasConfig = load_eventkey_alias(&format!( pub static ref EVENTKEY_ALIAS: EventKeyAliasConfig = load_eventkey_alias(
"{}/eventkey_alias.txt", utils::check_setting_path(
CONFIG.read().unwrap().args.config.as_path().display() &CONFIG.read().unwrap().args.config,
)); "eventkey_alias.txt",
false
)
.unwrap_or_else(|| {
utils::check_setting_path(
&CURRENT_EXE_PATH.to_path_buf(),
"rules/config/eventkey_alias.txt",
true,
)
.unwrap()
})
.to_str()
.unwrap()
);
pub static ref IDS_REGEX: Regex = pub static ref IDS_REGEX: Regex =
Regex::new(r"^[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}$").unwrap(); Regex::new(r"^[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}$").unwrap();
pub static ref TERM_SIZE: Option<(Width, Height)> = terminal_size(); pub static ref TERM_SIZE: Option<(Width, Height)> = terminal_size();
@@ -52,7 +65,7 @@ impl Default for ConfigReader<'_> {
} }
} }
#[derive(Parser)] #[derive(Parser, Clone)]
#[clap( #[clap(
name = "Hayabusa", name = "Hayabusa",
usage = "hayabusa.exe <INPUT> [OTHER-ACTIONS] [OPTIONS]", usage = "hayabusa.exe <INPUT> [OTHER-ACTIONS] [OPTIONS]",
@@ -242,23 +255,33 @@ impl ConfigReader<'_> {
.help_template("\n\nUSAGE:\n {usage}\n\nOPTIONS:\n{options}"); .help_template("\n\nUSAGE:\n {usage}\n\nOPTIONS:\n{options}");
ConfigReader { ConfigReader {
app: build_cmd, app: build_cmd,
args: parse, args: parse.clone(),
headless_help: String::default(), headless_help: String::default(),
event_timeline_config: load_eventcode_info( event_timeline_config: load_eventcode_info(
utils::check_setting_path( utils::check_setting_path(&parse.config, "statistics_event_info.txt", false)
&CURRENT_EXE_PATH.to_path_buf(), .unwrap_or_else(|| {
"rules/config/statistics_event_info.txt", utils::check_setting_path(
) &CURRENT_EXE_PATH.to_path_buf(),
.to_str() "rules/config/statistics_event_info.txt",
.unwrap(), true,
)
.unwrap()
})
.to_str()
.unwrap(),
), ),
target_eventids: load_target_ids( target_eventids: load_target_ids(
utils::check_setting_path( utils::check_setting_path(&parse.config, "target_event_IDs.txt", false)
&CURRENT_EXE_PATH.to_path_buf(), .unwrap_or_else(|| {
"rules/config/target_event_IDs.txt", utils::check_setting_path(
) &CURRENT_EXE_PATH.to_path_buf(),
.to_str() "rules/config/target_event_IDs.txt",
.unwrap(), true,
)
.unwrap()
})
.to_str()
.unwrap(),
), ),
} }
} }

View File

@@ -49,30 +49,32 @@ lazy_static! {
pub static ref STATISTICS_FLAG: bool = configs::CONFIG.read().unwrap().args.statistics; pub static ref STATISTICS_FLAG: bool = configs::CONFIG.read().unwrap().args.statistics;
pub static ref LOGONSUMMARY_FLAG: bool = configs::CONFIG.read().unwrap().args.logon_summary; pub static ref LOGONSUMMARY_FLAG: bool = configs::CONFIG.read().unwrap().args.logon_summary;
pub static ref TAGS_CONFIG: HashMap<String, String> = create_output_filter_config( pub static ref TAGS_CONFIG: HashMap<String, String> = create_output_filter_config(
utils::check_setting_path(&CURRENT_EXE_PATH.to_path_buf(), "config/mitre_tactics.txt") utils::check_setting_path(&CURRENT_EXE_PATH.to_path_buf(), "config/mitre_tactics.txt", true)
.to_str() .unwrap().to_str()
.unwrap(), .unwrap(),
); );
pub static ref CH_CONFIG: HashMap<String, String> = create_output_filter_config( pub static ref CH_CONFIG: HashMap<String, String> = create_output_filter_config(
utils::check_setting_path( utils::check_setting_path(&configs::CONFIG.read().unwrap().args.config, "channel_abbreviations.txt", false).unwrap_or_else(|| {
&CURRENT_EXE_PATH.to_path_buf(), utils::check_setting_path(
"rules/config/channel_abbreviations.txt" &CURRENT_EXE_PATH.to_path_buf(),
) "rules/config/channel_abbreviations.txt", true
).unwrap()
})
.to_str() .to_str()
.unwrap(), .unwrap(),
); );
pub static ref PIVOT_KEYWORD_LIST_FLAG: bool = pub static ref PIVOT_KEYWORD_LIST_FLAG: bool =
configs::CONFIG.read().unwrap().args.pivot_keywords_list; configs::CONFIG.read().unwrap().args.pivot_keywords_list;
pub static ref DEFAULT_DETAILS: HashMap<String, String> = get_default_details(&format!( pub static ref DEFAULT_DETAILS: HashMap<String, String> = get_default_details(
"{}/default_details.txt", utils::check_setting_path(&configs::CONFIG.read().unwrap().args.config, "default_details.txt", false).unwrap_or_else(|| {
configs::CONFIG utils::check_setting_path(
.read() &CURRENT_EXE_PATH.to_path_buf(),
.unwrap() "rules/config/default_details.txt", true
.args ).unwrap()
.config })
.as_path() .to_str()
.display() .unwrap()
)); );
pub static ref LEVEL_ABBR: LinkedHashMap<String, String> = LinkedHashMap::from_iter([ pub static ref LEVEL_ABBR: LinkedHashMap<String, String> = LinkedHashMap::from_iter([
("critical".to_string(), "crit".to_string()), ("critical".to_string(), "crit".to_string()),
("high".to_string(), "high".to_string()), ("high".to_string(), "high".to_string()),

View File

@@ -73,7 +73,8 @@ pub fn value_to_string(value: &Value) -> Option<String> {
pub fn read_txt(filename: &str) -> Result<Vec<String>, String> { pub fn read_txt(filename: &str) -> Result<Vec<String>, String> {
let filepath = if filename.starts_with("./") { let filepath = if filename.starts_with("./") {
check_setting_path(&CURRENT_EXE_PATH.to_path_buf(), filename) check_setting_path(&CURRENT_EXE_PATH.to_path_buf(), filename, true)
.unwrap()
.to_str() .to_str()
.unwrap() .unwrap()
.to_string() .to_string()
@@ -380,14 +381,59 @@ pub fn make_ascii_titlecase(s: &mut str) -> String {
} }
/// base_path/path が存在するかを確認し、存在しなければカレントディレクトリを参照するpathを返す関数 /// base_path/path が存在するかを確認し、存在しなければカレントディレクトリを参照するpathを返す関数
pub fn check_setting_path(base_path: &Path, path: &str) -> PathBuf { pub fn check_setting_path(base_path: &Path, path: &str, ignore_err: bool) -> Option<PathBuf> {
if base_path.join(path).exists() { if base_path.join(path).exists() {
base_path.join(path) Some(base_path.join(path))
} else if ignore_err {
Some(Path::new(path).to_path_buf())
} else { } else {
Path::new(path).to_path_buf() None
} }
} }
/// rule configのファイルの所在を確認する関数。
pub fn check_rule_config() -> Result<(), String> {
// rules/configのフォルダが存在するかを確認する
let exist_rule_config_folder =
if configs::CONFIG.read().unwrap().args.config == CURRENT_EXE_PATH.to_path_buf() {
check_setting_path(
&configs::CONFIG.read().unwrap().args.config,
"rules/config",
false,
)
.is_some()
} else {
check_setting_path(&configs::CONFIG.read().unwrap().args.config, "", false).is_some()
};
if !exist_rule_config_folder {
return Err("The required rules config files were not found. Please download them with --update-rules".to_string());
}
// 各種ファイルを確認する
let files = vec![
"channel_abbreviations.txt",
"target_event_IDs.txt",
"default_details.txt",
"level_tuning.txt",
"statistics_event_info.txt",
"eventkey_alias.txt",
];
let mut not_exist_file = vec![];
for file in &files {
if check_setting_path(&configs::CONFIG.read().unwrap().args.config, file, false).is_none() {
not_exist_file.push(*file);
}
}
if !not_exist_file.is_empty() {
return Err(format!(
"Could not find the following config files: {}\nPlease specify a correct rules config directory.\n",
not_exist_file.join(", ")
));
}
Ok(())
}
///タイムゾーンに合わせた情報を情報を取得する関数 ///タイムゾーンに合わせた情報を情報を取得する関数
pub fn format_time(time: &DateTime<Utc>, date_only: bool) -> String { pub fn format_time(time: &DateTime<Utc>, date_only: bool) -> String {
if configs::CONFIG.read().unwrap().args.utc { if configs::CONFIG.read().unwrap().args.utc {
@@ -613,23 +659,31 @@ mod tests {
let exist_path = Path::new("./test_files").to_path_buf(); let exist_path = Path::new("./test_files").to_path_buf();
let not_exist_path = Path::new("not_exist_path").to_path_buf(); let not_exist_path = Path::new("not_exist_path").to_path_buf();
assert_eq!( assert_eq!(
check_setting_path(&not_exist_path, "rules") check_setting_path(&not_exist_path, "rules", true)
.unwrap()
.to_str() .to_str()
.unwrap(), .unwrap(),
"rules" "rules"
); );
assert_eq!( assert_eq!(
check_setting_path(&not_exist_path, "fake") check_setting_path(&not_exist_path, "fake", true)
.unwrap()
.to_str() .to_str()
.unwrap(), .unwrap(),
"fake" "fake"
); );
assert_eq!( assert_eq!(
check_setting_path(&exist_path, "rules").to_str().unwrap(), check_setting_path(&exist_path, "rules", true)
.unwrap()
.to_str()
.unwrap(),
exist_path.join("rules").to_str().unwrap() exist_path.join("rules").to_str().unwrap()
); );
assert_eq!( assert_eq!(
check_setting_path(&exist_path, "fake").to_str().unwrap(), check_setting_path(&exist_path, "fake", true)
.unwrap()
.to_str()
.unwrap(),
"fake" "fake"
); );
} }

View File

@@ -7,8 +7,8 @@ use bytesize::ByteSize;
use chrono::{DateTime, Datelike, Local}; use chrono::{DateTime, Datelike, Local};
use evtx::{EvtxParser, ParserSettings}; use evtx::{EvtxParser, ParserSettings};
use hashbrown::{HashMap, HashSet}; use hashbrown::{HashMap, HashSet};
use hayabusa::detections::configs::CURRENT_EXE_PATH;
use hayabusa::detections::configs::{load_pivot_keywords, TargetEventTime, TARGET_EXTENSIONS}; use hayabusa::detections::configs::{load_pivot_keywords, TargetEventTime, TARGET_EXTENSIONS};
use hayabusa::detections::configs::{CONFIG, CURRENT_EXE_PATH};
use hayabusa::detections::detection::{self, EvtxRecordInfo}; use hayabusa::detections::detection::{self, EvtxRecordInfo};
use hayabusa::detections::message::{ use hayabusa::detections::message::{
AlertMessage, ERROR_LOG_PATH, ERROR_LOG_STACK, LOGONSUMMARY_FLAG, PIVOT_KEYWORD_LIST_FLAG, AlertMessage, ERROR_LOG_PATH, ERROR_LOG_STACK, LOGONSUMMARY_FLAG, PIVOT_KEYWORD_LIST_FLAG,
@@ -80,7 +80,9 @@ impl App {
utils::check_setting_path( utils::check_setting_path(
&CURRENT_EXE_PATH.to_path_buf(), &CURRENT_EXE_PATH.to_path_buf(),
"config/pivot_keywords.txt", "config/pivot_keywords.txt",
true,
) )
.unwrap()
.to_str() .to_str()
.unwrap(), .unwrap(),
); );
@@ -148,13 +150,19 @@ impl App {
// カレントディレクトリ以外からの実行の際にrules-configオプションの指定がないとエラーが発生することを防ぐための処理 // カレントディレクトリ以外からの実行の際にrules-configオプションの指定がないとエラーが発生することを防ぐための処理
if configs::CONFIG.read().unwrap().args.config == Path::new("./rules/config") { if configs::CONFIG.read().unwrap().args.config == Path::new("./rules/config") {
configs::CONFIG.write().unwrap().args.config = configs::CONFIG.write().unwrap().args.config =
utils::check_setting_path(&CURRENT_EXE_PATH.to_path_buf(), "./rules/config"); utils::check_setting_path(&CURRENT_EXE_PATH.to_path_buf(), "rules/config", true)
.unwrap();
} }
// カレントディレクトリ以外からの実行の際にrulesオプションの指定がないとエラーが発生することを防ぐための処理 // カレントディレクトリ以外からの実行の際にrulesオプションの指定がないとエラーが発生することを防ぐための処理
if configs::CONFIG.read().unwrap().args.rules == Path::new("./rules") { if configs::CONFIG.read().unwrap().args.rules == Path::new("./rules") {
configs::CONFIG.write().unwrap().args.rules = configs::CONFIG.write().unwrap().args.rules =
utils::check_setting_path(&CURRENT_EXE_PATH.to_path_buf(), "./rules"); utils::check_setting_path(&CURRENT_EXE_PATH.to_path_buf(), "rules", true).unwrap();
}
// rule configのフォルダ、ファイルを確認してエラーがあった場合は終了とする
if let Err(e) = utils::check_rule_config() {
AlertMessage::alert(&e).ok();
return;
} }
if let Some(csv_path) = &configs::CONFIG.read().unwrap().args.output { if let Some(csv_path) = &configs::CONFIG.read().unwrap().args.output {
@@ -264,9 +272,18 @@ impl App {
let level_tuning_config_path = match level_tuning_val { let level_tuning_config_path = match level_tuning_val {
Some(path) => path.to_owned(), Some(path) => path.to_owned(),
_ => utils::check_setting_path( _ => utils::check_setting_path(
&CURRENT_EXE_PATH.to_path_buf(), &CONFIG.read().unwrap().args.config,
"./rules/config/level_tuning.txt", "level_tuning.txt",
false,
) )
.unwrap_or_else(|| {
utils::check_setting_path(
&CURRENT_EXE_PATH.to_path_buf(),
"rules/config/level_tuning.txt",
true,
)
.unwrap()
})
.display() .display()
.to_string(), .to_string(),
}; };
@@ -469,10 +486,10 @@ impl App {
} }
fn print_contributors(&self) { fn print_contributors(&self) {
match fs::read_to_string(utils::check_setting_path( match fs::read_to_string(
&CURRENT_EXE_PATH.to_path_buf(), utils::check_setting_path(&CURRENT_EXE_PATH.to_path_buf(), "contributors.txt", true)
"contributors.txt", .unwrap(),
)) { ) {
Ok(contents) => { Ok(contents) => {
write_color_buffer( write_color_buffer(
&BufferWriter::stdout(ColorChoice::Always), &BufferWriter::stdout(ColorChoice::Always),
@@ -721,7 +738,8 @@ impl App {
/// output logo /// output logo
fn output_logo(&self) { fn output_logo(&self) {
let fp = utils::check_setting_path(&CURRENT_EXE_PATH.to_path_buf(), "art/logo.txt"); let fp = utils::check_setting_path(&CURRENT_EXE_PATH.to_path_buf(), "art/logo.txt", true)
.unwrap();
let content = fs::read_to_string(fp).unwrap_or_default(); let content = fs::read_to_string(fp).unwrap_or_default();
let output_color = if configs::CONFIG.read().unwrap().args.no_color { let output_color = if configs::CONFIG.read().unwrap().args.no_color {
None None
@@ -748,7 +766,8 @@ impl App {
match eggs.get(exec_datestr) { match eggs.get(exec_datestr) {
None => {} None => {}
Some(path) => { Some(path) => {
let egg_path = utils::check_setting_path(&CURRENT_EXE_PATH.to_path_buf(), path); let egg_path =
utils::check_setting_path(&CURRENT_EXE_PATH.to_path_buf(), path, true).unwrap();
let content = fs::read_to_string(egg_path).unwrap_or_default(); let content = fs::read_to_string(egg_path).unwrap_or_default();
write_color_buffer( write_color_buffer(
&BufferWriter::stdout(ColorChoice::Always), &BufferWriter::stdout(ColorChoice::Always),

View File

@@ -15,13 +15,20 @@ lazy_static! {
pub static ref PROFILES: Option<LinkedHashMap<String, String>> = load_profile( pub static ref PROFILES: Option<LinkedHashMap<String, String>> = load_profile(
check_setting_path( check_setting_path(
&CURRENT_EXE_PATH.to_path_buf(), &CURRENT_EXE_PATH.to_path_buf(),
"config/default_profile.yaml" "config/default_profile.yaml",
true
) )
.unwrap()
.to_str() .to_str()
.unwrap(), .unwrap(),
check_setting_path(&CURRENT_EXE_PATH.to_path_buf(), "config/profiles.yaml") check_setting_path(
.to_str() &CURRENT_EXE_PATH.to_path_buf(),
.unwrap() "config/profiles.yaml",
true
)
.unwrap()
.to_str()
.unwrap()
); );
pub static ref LOAEDED_PROFILE_ALIAS: HashSet<String> = HashSet::from_iter( pub static ref LOAEDED_PROFILE_ALIAS: HashSet<String> = HashSet::from_iter(
PROFILES PROFILES