diff --git a/src/detections/system.rs b/src/detections/system.rs new file mode 100644 index 00000000..68ab263c --- /dev/null +++ b/src/detections/system.rs @@ -0,0 +1,110 @@ +use crate::detections::utils; +use crate::models::event; +use std::collections::HashMap; + +pub struct System {} + +impl System { + pub fn new() -> System { + System {} + } + + pub fn detection( + &mut self, + event_id: String, + system: &event::System, + event_data: HashMap, + ) { + self.system_log_clear(&event_id); + self.windows_event_log(&event_id, &event_data); + self.new_service_created(&event_id, &event_data); + self.interactive_service_warning(&event_id, &event_data); + self.suspicious_service_name(&event_id, &event_data); + } + + fn new_service_created(&mut self, event_id: &String, event_data: &HashMap) { + if event_id != "7045" { + return; + } + + let default = String::from(""); + let servicename = &event_data.get("ServiceName").unwrap_or(&default); + let commandline = &event_data.get("ImagePath").unwrap_or(&default); + let text = utils::check_regex_old(&servicename, 1); + if !text.is_empty() { + println!("Message : New Service Created"); + println!("Command : {}", commandline); + println!("Results : Service name: {}", servicename); + println!("Results : {}", text); + } + if !commandline.is_empty() { + utils::check_command(7045, &commandline, 1000, 0, &servicename, &""); + } + } + + fn interactive_service_warning( + &mut self, + event_id: &String, + event_data: &HashMap, + ) { + if event_id != "7030" { + return; + } + + let default = String::from(""); + let servicename = &event_data.get("param1").unwrap_or(&default); + println!("Message : Interactive service warning"); + println!("Results : Service name: {}", servicename); + println!("Results : Malware (and some third party software) trigger this warning"); + println!("{}", utils::check_regex_old(&servicename, 1)); + } + + fn suspicious_service_name(&mut self, event_id: &String, event_data: &HashMap) { + if event_id != "7036" { + return; + } + + let default = String::from(""); + let servicename = &event_data.get("param1").unwrap_or(&default); + let text = utils::check_regex_old(&servicename, 1); + if !text.is_empty() { + println!("Message : Suspicious Service Name"); + println!("Results : Service name: {}", servicename); + println!("Results : {}", text); + } + } + + fn system_log_clear(&mut self, event_id: &String) { + if event_id != "104" { + return; + } + + println!("Message : System Log Clear"); + println!("Results : The System log was cleared."); + } + + fn windows_event_log(&mut self, event_id: &String, event_data: &HashMap) { + if event_id != "7040" { + return; + } + + if let Some(_param1) = event_data.get("param1") { + if _param1 == "Windows Event Log" { + println!("Service name : {}", _param1); + if let Some(_param2) = event_data.get("param2") { + if _param2 == "disabled" { + println!("Message : Event Log Service Stopped"); + println!( + "Results : Selective event log manipulation may follow this event." + ); + } else if _param2 == "auto start" { + println!("Message : Event Log Service Started"); + println!( + "Results : Selective event log manipulation may precede this event." + ); + } + } + } + } + } +} diff --git a/src/detections/utils.rs b/src/detections/utils.rs index 2e7026e0..728d3199 100644 --- a/src/detections/utils.rs +++ b/src/detections/utils.rs @@ -5,6 +5,7 @@ extern crate regex; use crate::detections::configs; use flate2::read::GzDecoder; use regex::Regex; +use std::fs::File; use std::io::prelude::*; use std::str; use std::string::String; @@ -20,17 +21,9 @@ pub fn check_command( let mut text = "".to_string(); let mut base64 = "".to_string(); - let empty = "".to_string(); - for line in configs::singleton().whitelist { - let r_str = line.get(0).unwrap_or(&empty); - if r_str.is_empty() { - continue; - } - - let r = Regex::new(r_str); - if r.is_ok() && r.unwrap().is_match(commandline) { - return; - } + let ret = check_whitelist(commandline, "whitelist.txt"); + if (ret) { + return; } if commandline.len() > minlength { @@ -39,7 +32,7 @@ pub fn check_command( text.push_str("bytes\n"); } text.push_str(&check_obfu(commandline)); - text.push_str(&check_regex(commandline, 0)); + text.push_str(&check_regex_old(commandline, 0)); text.push_str(&check_creator(commandline, creator)); if Regex::new(r"\-enc.*[A-Za-z0-9/+=]{100}") .unwrap() @@ -71,7 +64,10 @@ pub fn check_command( 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)); + text.push_str(&check_regex_old( + str::from_utf8(decoded.as_slice()).unwrap(), + 0, + )); } } } @@ -126,7 +122,7 @@ fn check_obfu(string: &str) -> std::string::String { return obfutext; } -pub fn check_regex(string: &str, r#type: usize) -> std::string::String { +pub fn check_regex_old(string: &str, r#type: usize) -> std::string::String { let empty = "".to_string(); let mut regextext = "".to_string(); for line in configs::singleton().regex { @@ -157,6 +153,79 @@ pub fn check_regex(string: &str, r#type: usize) -> std::string::String { return regextext; } +pub fn check_regex(string: &str, r#type: usize, regex_path: &str) -> std::string::String { + let empty = "".to_string(); + let mut regextext = "".to_string(); + let regex_list = read_csv(regex_path); + for line in regex_list { + let type_str = line.get(0).unwrap_or(&empty); + if type_str != &r#type.to_string() { + continue; + } + + let regex_str = line.get(1).unwrap_or(&empty); + if regex_str.is_empty() { + continue; + } + + let re = Regex::new(regex_str); + 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 regextext; +} + +pub fn check_whitelist(target: &str, whitelist_path: &str) -> bool { + let empty = "".to_string(); + let whitelist = read_csv(whitelist_path); + for line in whitelist { + let r_str = line.get(0).unwrap_or(&empty); + if r_str.is_empty() { + continue; + } + + let r = Regex::new(r_str); + if r.is_ok() && r.unwrap().is_match(target) { + return true; + } + } + + return false; +} + +fn read_csv(filename: &str) -> Vec> { + let mut f = File::open(filename).expect("file not found!!!"); + let mut contents: String = String::new(); + let mut ret = vec![]; + if f.read_to_string(&mut contents).is_err() { + return ret; + } + + let mut rdr = csv::Reader::from_reader(contents.as_bytes()); + rdr.records().for_each(|r| { + if r.is_err() { + return; + } + + let line = r.unwrap(); + let mut v = vec![]; + line.iter().for_each(|s| v.push(s.to_string())); + ret.push(v); + }); + + return ret; +} + fn check_creator(command: &str, creator: &str) -> std::string::String { let mut creatortext = "".to_string(); if !creator.is_empty() { @@ -180,8 +249,11 @@ mod tests { use crate::detections::utils; #[test] fn test_check_regex() { - let regextext = utils::check_regex("\\cvtres.exe", 0); + let regextext = utils::check_regex("\\cvtres.exe", 0, "regexes.txt"); assert!(regextext == "Resource File To COFF Object Conversion Utility cvtres.exe\n"); + + let regextext = utils::check_regex("\\hogehoge.exe", 0, "regexes.txt"); + assert!(regextext == ""); } #[test] @@ -212,4 +284,13 @@ mod tests { "dir", ); } + + #[test] + fn test_check_whitelist() { + let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate.exe\""; + assert!(true == utils::check_whitelist(commandline, "whitelist.txt")); + + let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate2.exe\""; + assert!(false == utils::check_whitelist(commandline, "whitelist.txt")); + } }