diff --git a/Cargo.lock b/Cargo.lock index 551ac12e..988e8b95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -139,14 +139,16 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "chrono" -version = "0.4.15" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" dependencies = [ + "libc", "num-integer", "num-traits", "serde", "time", + "winapi", ] [[package]] @@ -1184,10 +1186,12 @@ name = "yamato_event_analyzer" version = "0.1.0" dependencies = [ "base64", + "chrono", "clap", "csv", "evtx", "flate2", + "lazy_static", "quick-xml 0.17.2", "regex", "serde", diff --git a/Cargo.toml b/Cargo.toml index d4de1c60..51077334 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,4 +17,12 @@ regex = "1" csv = "1.1" base64 = "*" flate2 = "1.0" +lazy_static = "1.4.0" +chrono = "0.4.19" yaml-rust = "0.4" + +[target.x86_64-pc-windows-gnu] +linker = "x86_64-w64-mingw32-gcc" + +[target.i686-pc-windows-gnu] +linker = "i686-w64-mingw32-gcc" diff --git a/rules/deep_blue_cli/powershell/powershell4103.toml b/rules/deep_blue_cli/powershell/powershell4103.toml new file mode 100644 index 00000000..d70c52bf --- /dev/null +++ b/rules/deep_blue_cli/powershell/powershell4103.toml @@ -0,0 +1,4 @@ +[rule] +severity = "high" +name = "4103" +message = "Execute Pipeline" \ No newline at end of file diff --git a/rules/deep_blue_cli/powershell/powershell4104.toml b/rules/deep_blue_cli/powershell/powershell4104.toml new file mode 100644 index 00000000..a4262b86 --- /dev/null +++ b/rules/deep_blue_cli/powershell/powershell4104.toml @@ -0,0 +1,4 @@ +[rule] +severity = "high" +name = "4104" +message = "Excute Remote Command" \ No newline at end of file diff --git a/src/detections/configs.rs b/src/detections/configs.rs index 1862e2d4..763f50f1 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -1,3 +1,4 @@ +use clap::{App, AppSettings, Arg, ArgMatches}; use std::fs::File; use std::io::prelude::*; use std::sync::Once; @@ -6,6 +7,7 @@ use std::sync::Once; pub struct SingletonReader { pub regex: Vec>, pub whitelist: Vec>, + pub args: ArgMatches<'static>, } pub fn singleton() -> Box { @@ -17,6 +19,7 @@ pub fn singleton() -> Box { let singleton = SingletonReader { regex: read_csv("regexes.txt"), whitelist: read_csv("whitelist.txt"), + args: build_app().get_matches(), }; SINGLETON = Some(Box::new(singleton)); @@ -26,6 +29,33 @@ pub fn singleton() -> Box { } } +fn build_app() -> clap::App<'static, 'static> { + let program = std::env::args() + .nth(0) + .and_then(|s| { + std::path::PathBuf::from(s) + .file_stem() + .map(|s| s.to_string_lossy().into_owned()) + }) + .unwrap(); + + 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 ") + .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("-l --lang=[LANG] 'output language'")) + .arg(Arg::from_usage("-t --timezone=[TIMEZONE] 'timezone setting'")) + .arg(Arg::from_usage("-d --directory 'event log files directory'")) + .arg(Arg::from_usage("-s --statistics 'event statistics'")) + .arg(Arg::from_usage("-u --update 'signature update'")) + .arg(Arg::from_usage("--credits 'Zachary Mathis, Akira Nishikawa'")) +} + fn read_csv(filename: &str) -> Vec> { let mut f = File::open(filename).expect("file not found!!!"); let mut contents: String = String::new(); diff --git a/src/detections/mod.rs b/src/detections/mod.rs index ad3011e6..4aaee9ff 100644 --- a/src/detections/mod.rs +++ b/src/detections/mod.rs @@ -1,9 +1,10 @@ mod application; mod applocker; mod common; -mod configs; +pub mod configs; pub mod detection; mod powershell; +pub mod print; mod security; mod sysmon; mod system; diff --git a/src/detections/powershell.rs b/src/detections/powershell.rs index c628a011..4fa956b0 100644 --- a/src/detections/powershell.rs +++ b/src/detections/powershell.rs @@ -2,7 +2,6 @@ use crate::detections::utils; use crate::models::event; use regex::Regex; use std::collections::HashMap; -extern crate csv; pub struct PowerShell {} @@ -25,6 +24,7 @@ impl PowerShell { if event_id != "4103" { return; } + let default = String::from(""); let commandline = event_data.get("ContextInfo").unwrap_or(&default); @@ -48,6 +48,7 @@ impl PowerShell { if event_id != "4104" { return; } + let default = String::from(""); let path = event_data.get("Path").unwrap().to_string(); if path == "".to_string() { diff --git a/src/detections/print.rs b/src/detections/print.rs new file mode 100644 index 00000000..793737e4 --- /dev/null +++ b/src/detections/print.rs @@ -0,0 +1,63 @@ +extern crate chrono; +extern crate lazy_static; +use chrono::{DateTime, TimeZone, Utc}; +use lazy_static::lazy_static; +use std::collections::BTreeMap; +use std::sync::Mutex; + +#[derive(Debug)] +pub struct Message { + map: BTreeMap, Vec>, +} + +lazy_static! { + pub static ref MESSAGES: Mutex = Mutex::new(Message::new()); +} + +impl Message { + pub fn new() -> Self { + let messages: BTreeMap, Vec> = BTreeMap::new(); + Message { map: messages } + } + + /// メッセージを設定 + pub fn insert(&mut self, time: DateTime, message: String) { + match self.map.get_mut(&time) { + Some(v) => { + v.push(message.to_string()); + } + None => { + let m = vec![message.to_string(); 1]; + self.map.insert(time, m); + } + } + } + + /// メッセージを返す + pub fn get(&self, time: DateTime) -> Vec { + match self.map.get(&time) { + Some(v) => (&v).to_vec(), + None => Vec::new(), + } + } + + /// Messageのなかに入っているメッセージすべてを表示する + pub fn debug(&self) { + println!("{:?}", self.map); + } +} + +#[test] +fn test_create_and_append_message() { + let mut message = Message::new(); + let poke = Utc.ymd(1996, 2, 27).and_hms(1, 5, 1); + let taka = Utc.ymd(2000, 1, 21).and_hms(9, 6, 1); + + message.insert(poke, "TEST".to_string()); + message.insert(poke, "TEST2".to_string()); + message.insert(taka, "TEST3".to_string()); + + let display = format!("{}", format_args!("{:?}", message)); + let expect = "Message { map: {1996-02-27T01:05:01Z: [\"TEST\", \"TEST2\"], 2000-01-21T09:06:01Z: [\"TEST3\"]} }"; + assert_eq!(display, expect); +} diff --git a/src/detections/utils.rs b/src/detections/utils.rs index deae75df..2e7026e0 100644 --- a/src/detections/utils.rs +++ b/src/detections/utils.rs @@ -56,27 +56,23 @@ pub fn check_command( let re = Regex::new(r"'.*$").unwrap(); base64.push_str(&re.replace_all(&base64.to_string(), "")); } - if !base64.is_empty() { - if Regex::new(r"Compression.GzipStream.*Decompress") - .unwrap() - .is_match(commandline) - { - /* - if let decoded = base64::decode(&base64) { - let mut d = GzDecoder::new(decoded.as_slice()); - let mut uncompressed = String::new(); - d.read_to_string(&mut uncompressed).unwrap(); - println!("Decoded : {}", uncompressed); - text.push_str("Base64-encoded and compressed function\n"); - + if let Ok(decoded) = base64::decode(&base64) { + if !base64.is_empty() { + if Regex::new(r"Compression.GzipStream.*Decompress") + .unwrap() + .is_match(commandline) + { + let mut d = GzDecoder::new(decoded.as_slice()); + let mut uncompressed = String::new(); + d.read_to_string(&mut uncompressed).unwrap(); + println!("Decoded : {}", uncompressed); + text.push_str("Base64-encoded and compressed function\n"); + } else { + println!("Decoded : {}", str::from_utf8(decoded.as_slice()).unwrap()); + text.push_str("Base64-encoded function\n"); + text.push_str(&check_obfu(str::from_utf8(decoded.as_slice()).unwrap())); + text.push_str(&check_regex(str::from_utf8(decoded.as_slice()).unwrap(), 0)); } - */ - } else { - let decoded = base64::decode(base64).unwrap(); - println!("Decoded : {}", str::from_utf8(decoded.as_slice()).unwrap()); - text.push_str("Base64-encoded function\n"); - text.push_str(&check_obfu(str::from_utf8(decoded.as_slice()).unwrap())); - text.push_str(&check_regex(str::from_utf8(decoded.as_slice()).unwrap(), 0)); } } if !text.is_empty() { diff --git a/src/main.rs b/src/main.rs index ce910bf5..c2e44c76 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,48 +1,20 @@ -extern crate clap; extern crate serde; -use clap::{App, AppSettings, Arg}; use evtx::EvtxParser; use quick_xml::de::DeError; use std::{fs, path::PathBuf, process}; +use yamato_event_analyzer::detections::configs; use yamato_event_analyzer::detections::detection; use yamato_event_analyzer::omikuji::Omikuji; -use yamato_event_analyzer::yaml; - -fn build_app() -> clap::App<'static, 'static> { - let program = std::env::args() - .nth(0) - .and_then(|s| { - std::path::PathBuf::from(s) - .file_stem() - .map(|s| s.to_string_lossy().into_owned()) - }) - .unwrap(); - - 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 ") - .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("-l --lang=[LANG] 'output language'")) - .arg(Arg::from_usage("-t --timezone=[TIMEZONE] 'timezone setting'")) - .arg(Arg::from_usage("-d --directory 'event log files directory'")) - .arg(Arg::from_usage("-s --statistics 'event statistics'")) - .arg(Arg::from_usage("-u --update 'signature update'")) - .arg(Arg::from_usage("-o --omikuji 'output with omikuji'")) - .arg(Arg::from_usage("--credits 'Zachary Mathis, Akira Nishikawa'")) -} fn main() -> Result<(), DeError> { - let args = build_app().get_matches(); - let filepath: Option<&str> = args.value_of("filepath"); - - if let Some(filepath) = filepath { - parse_file(filepath); + let filepath: String = configs::singleton() + .args + .value_of("filepath") + .unwrap_or("") + .to_string(); + if filepath != "" { + parse_file(&filepath); } Ok(())