Feature/#140 document (#144)

* update

* fix regexes and whitelist

* underconstructing

* fix

* update

* add pic

* update

* update

* update

* fix
This commit is contained in:
James
2021-10-22 00:43:40 +09:00
committed by GitHub
parent 23c60fa8ff
commit 4a1e46e47e
38 changed files with 502 additions and 241 deletions
+13 -15
View File
@@ -39,22 +39,20 @@ fn build_app<'a>() -> ArgMatches<'a> {
}
App::new(&program)
.about("Yea! (Yamato Event Analyzer). Aiming to be the world's greatest Windows event log analysis tool!")
.version("0.0.1")
.author("Author name <author@example.com>")
.about("Lagotto. Aiming to be the world's greatest Windows event log analysis tool!")
.version("1.0.0")
.author("Author name Yamato-Security(https://github.com/Yamato-Security/YamatoEventAnalyzer)")
.setting(AppSettings::VersionlessSubcommands)
.arg(Arg::from_usage("-f --filepath=[FILEPATH] 'event file path'"))
.arg(Arg::from_usage("--attackhunt=[ATTACK_HUNT] 'Attack Hunt'"))
.arg(Arg::from_usage("--csv-timeline=[CSV_TIMELINE] 'csv output timeline'"))
.arg(Arg::from_usage("--human-readable-timeline=[HUMAN_READABLE_TIMELINE] 'human readable timeline'"))
.arg(Arg::from_usage("--rfc-2822 'output date and time in RFC 2822 format. Example: Mon, 07 Aug 2006 12:34:56 -0600'"))
.arg(Arg::from_usage("-l --lang=[LANG] 'output language'"))
.arg(Arg::from_usage("-u --utc 'output time in UTC format(default: local time)'"))
.arg(Arg::from_usage("-d --directory=[DIRECTORY] 'event log files directory'"))
.arg(Arg::from_usage("-s --statistics 'event statistics'"))
.arg(Arg::from_usage("-t --threadnum=[NUM] 'thread number'"))
.arg(Arg::from_usage("--slack 'slack notification'"))
.arg(Arg::from_usage("--credits 'Zachary Mathis, Akira Nishikawa'"))
.arg(Arg::from_usage("-f --filepath=[FILEPATH] 'Event file path'"))
.arg(Arg::from_usage("--csv-timeline=[CSV_TIMELINE] 'Csv output timeline'"))
.arg(Arg::from_usage("--rfc-2822 'Output date and time in RFC 2822 format. Example: Mon, 07 Aug 2006 12:34:56 -0600'"))
.arg(Arg::from_usage("-l --lang=[LANG] 'Output language'"))
.arg(Arg::from_usage("-u --utc 'Output time in UTC format(default: local time)'"))
.arg(Arg::from_usage("-d --directory=[DIRECTORY] 'Event log files directory'"))
.arg(Arg::from_usage("-s --statistics 'Prints statistics for event logs'"))
.arg(Arg::from_usage("-t --threadnum=[NUM] 'Thread number'"))
.arg(Arg::from_usage("--slack 'Slack notification'"))
.arg(Arg::from_usage("--credits 'Prints credits'"))
.get_matches()
}
+17 -39
View File
@@ -72,7 +72,7 @@ impl LeafMatcher for MinlengthMatcher {
/// 正規表現のリストが記載されたファイルを読み取って、比較するロジックを表すクラス
/// DeepBlueCLIのcheck_cmdメソッドの一部に同様の処理が実装されていた。
pub struct RegexesFileMatcher {
regexes_csv_content: Vec<Vec<String>>,
regexes_csv_content: Vec<String>,
}
impl RegexesFileMatcher {
@@ -107,15 +107,11 @@ impl LeafMatcher for RegexesFileMatcher {
return Result::Err(vec![errmsg]);
}
let csv_content = utils::read_csv(&value.unwrap());
if csv_content.is_err() {
let errmsg = format!(
"cannot read regexes file. [key:{}]",
utils::concat_selection_key(key_list)
);
return Result::Err(vec![errmsg]);
let regexes_csv_content = utils::read_txt(&value.unwrap());
if regexes_csv_content.is_err() {
return Result::Err(vec![regexes_csv_content.unwrap_err()]);
}
self.regexes_csv_content = csv_content.unwrap();
self.regexes_csv_content = regexes_csv_content.unwrap();
return Result::Ok(());
}
@@ -123,10 +119,8 @@ impl LeafMatcher for RegexesFileMatcher {
fn is_match(&self, event_value: Option<&Value>) -> bool {
//TODO Wildcardの場合、CaseInsensitiveなので、ToLowerする。
return match event_value.unwrap_or(&Value::Null) {
Value::String(s) => !utils::check_regex(s, 0, &self.regexes_csv_content).is_empty(),
Value::Number(n) => {
!utils::check_regex(&n.to_string(), 0, &self.regexes_csv_content).is_empty()
}
Value::String(s) => !utils::check_regex(s, &self.regexes_csv_content),
Value::Number(n) => !utils::check_regex(&n.to_string(), &self.regexes_csv_content),
_ => false,
};
}
@@ -135,7 +129,7 @@ impl LeafMatcher for RegexesFileMatcher {
/// ファイルに列挙された文字列に一致する場合に検知するロジックを表す
/// DeepBlueCLIのcheck_cmdメソッドの一部に同様の処理が実装されていた。
pub struct WhitelistFileMatcher {
whitelist_csv_content: Vec<Vec<String>>,
whitelist_csv_content: Vec<String>,
}
impl WhitelistFileMatcher {
@@ -170,15 +164,11 @@ impl LeafMatcher for WhitelistFileMatcher {
return Result::Err(vec![errmsg]);
}
let csv_content = utils::read_csv(&value.unwrap());
if csv_content.is_err() {
let errmsg = format!(
"cannot read whitelist file. [key:{}]",
utils::concat_selection_key(key_list)
);
return Result::Err(vec![errmsg]);
let whitelist_content = utils::read_txt(&value.unwrap());
if whitelist_content.is_err() {
return Result::Err(vec![whitelist_content.unwrap_err()]);
}
self.whitelist_csv_content = csv_content.unwrap();
self.whitelist_csv_content = whitelist_content.unwrap();
return Result::Ok(());
}
@@ -662,28 +652,16 @@ mod tests {
// regexes.txtの中身と一致していることを確認
let csvcontent = &ancestor_matcher.regexes_csv_content;
assert_eq!(csvcontent.len(), 14);
let firstcontent = &csvcontent[0];
assert_eq!(firstcontent.len(), 3);
assert_eq!(firstcontent[0], "0");
assert_eq!(csvcontent.len(), 17);
assert_eq!(
firstcontent[1],
csvcontent[0],
r"^cmd.exe /c echo [a-z]{6} > \\\\.\\pipe\\[a-z]{6}$"
);
assert_eq!(
firstcontent[2],
r"Metasploit-style cmd with pipe (possible use of Meterpreter 'getsystem')"
);
let lastcontent = &csvcontent[13];
assert_eq!(lastcontent.len(), 3);
assert_eq!(lastcontent[0], "0");
assert_eq!(
lastcontent[1],
csvcontent[14],
r"\\cvtres\.exe.*\\AppData\\Local\\Temp\\[A-Z0-9]{7}\.tmp"
);
assert_eq!(lastcontent[2], r"PSAttack-style command via cvtres.exe");
}
// whitelist.txtが読み込めることを確認
@@ -704,11 +682,11 @@ mod tests {
assert_eq!(csvcontent.len(), 2);
assert_eq!(
csvcontent[0][0],
csvcontent[0],
r#"^"C:\\Program Files\\Google\\Chrome\\Application\\chrome\.exe""#.to_string()
);
assert_eq!(
csvcontent[1][0],
csvcontent[1],
r#"^"C:\\Program Files\\Google\\Update\\GoogleUpdate\.exe""#.to_string()
);
}
+30 -34
View File
@@ -11,6 +11,7 @@ use regex::Regex;
use serde_json::Value;
use std::fs::File;
use std::io::prelude::*;
use std::io::{BufRead, BufReader};
use std::str;
use std::string::String;
@@ -23,50 +24,30 @@ pub fn concat_selection_key(key_list: &Vec<String>) -> String {
});
}
pub fn check_regex(
string: &str,
r#type: usize,
regex_list: &Vec<Vec<String>>,
) -> std::string::String {
let empty = "".to_string();
let mut regextext = "".to_string();
pub fn check_regex(string: &str, regex_list: &Vec<String>) -> bool {
for line in regex_list {
let type_str = line.get(0).unwrap_or(&empty);
if type_str != &r#type.to_string() {
if line.is_empty() {
continue;
}
let regex_str = line.get(1).unwrap_or(&empty);
if regex_str.is_empty() {
continue;
}
let re = Regex::new(regex_str);
let re = Regex::new(line);
if re.is_err() || re.unwrap().is_match(string) == false {
continue;
}
let text = line.get(2).unwrap_or(&empty);
if text.is_empty() {
continue;
}
regextext.push_str(text);
regextext.push_str("\n");
return true;
}
return regextext;
return false;
}
pub fn check_whitelist(target: &str, whitelist: &Vec<Vec<String>>) -> bool {
let empty = "".to_string();
pub fn check_whitelist(target: &str, whitelist: &Vec<String>) -> bool {
for line in whitelist {
let r_str = line.get(0).unwrap_or(&empty);
if r_str.is_empty() {
if line.is_empty() {
continue;
}
let r = Regex::new(r_str);
let r = Regex::new(line);
if r.is_ok() && r.unwrap().is_match(target) {
return true;
}
@@ -75,6 +56,21 @@ pub fn check_whitelist(target: &str, whitelist: &Vec<Vec<String>>) -> bool {
return false;
}
pub fn read_txt(filename: &str) -> Result<Vec<String>, String> {
let f = File::open(filename);
if f.is_err() {
let errmsg = format!("cannot open file. [file:{}]", filename);
return Result::Err(errmsg);
}
let reader = BufReader::new(f.unwrap());
return Result::Ok(
reader
.lines()
.map(|line| line.unwrap_or(String::default()))
.collect(),
);
}
pub fn read_csv(filename: &str) -> Result<Vec<Vec<String>>, String> {
let mut f = File::open(filename).expect("file not found!!!");
let mut contents: String = String::new();
@@ -165,18 +161,18 @@ mod tests {
use crate::detections::utils;
#[test]
fn test_check_regex() {
let regexes = utils::read_csv("regexes.txt").unwrap();
let regextext = utils::check_regex("\\cvtres.exe", 0, &regexes);
assert!(regextext == "Resource File To COFF Object Conversion Utility cvtres.exe\n");
let regexes = utils::read_txt("regexes.txt").unwrap();
let regextext = utils::check_regex("\\cvtres.exe", &regexes);
assert!(regextext == true);
let regextext = utils::check_regex("\\hogehoge.exe", 0, &regexes);
assert!(regextext == "");
let regextext = utils::check_regex("\\hogehoge.exe", &regexes);
assert!(regextext == false);
}
#[test]
fn test_check_whitelist() {
let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate.exe\"";
let whitelist = utils::read_csv("whitelist.txt").unwrap();
let whitelist = utils::read_txt("whitelist.txt").unwrap();
assert!(true == utils::check_whitelist(commandline, &whitelist));
let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate2.exe\"";