From c3feb1eca202276ab1cac2c596bb4f4c327bee74 Mon Sep 17 00:00:00 2001 From: ichiichi11 Date: Wed, 7 Oct 2020 00:53:19 +0900 Subject: [PATCH] refactor for test. --- src/detections/security.rs | 250 ++++++++++++++++++++++++------------- 1 file changed, 162 insertions(+), 88 deletions(-) diff --git a/src/detections/security.rs b/src/detections/security.rs index 9b4c968b..37bcc74b 100644 --- a/src/detections/security.rs +++ b/src/detections/security.rs @@ -40,22 +40,47 @@ impl Security { } pub fn disp(&self) { - if self.total_admin_logons > 0 { - println!("total_admin_logons:{}", self.total_admin_logons); - println!("admin_logons:{:?}", self.admin_logons); - println!("multiple_admin_logons:{:?}\n\n", self.multiple_admin_logons); + self.fmt_admin_logons().and_then(Security::print_console); + self.fmt_passspray().and_then(Security::print_console); + } + + fn fmt_admin_logons(&self) -> Option> { + if self.total_admin_logons < 1 { + return Option::None; } - let exceed_failed_logons = self.total_failed_logons > self.max_failed_logons; - let exist_failed_account = self.account_2_failedcnt.keys().count() as i32 > 1; - if exceed_failed_logons && exist_failed_account { - println!("High number of total logon failures for multiple accounts"); - println!( - "Total accounts: {}", - self.account_2_failedcnt.keys().count() - ); - println!("Total logon failures: {}\n\n", self.total_failed_logons); + let mut msges: Vec = Vec::new(); + msges.push(format!("total_admin_logons:{}", self.total_admin_logons)); + msges.push(format!("admin_logons:{:?}", self.admin_logons)); + msges.push(format!( + "multiple_admin_logons:{:?}\n\n", + self.multiple_admin_logons + )); + + return Option::Some(msges); + } + + fn fmt_passspray(&self) -> Option> { + let exceed_failed_logons = self.total_failed_logons <= self.max_failed_logons; + let exist_failed_account = self.account_2_failedcnt.keys().count() as i32 <= 1; + if exceed_failed_logons || exist_failed_account { + return Option::None; } + + let mut msges: Vec = Vec::new(); + msges.push(format!( + "High number of total logon failures for multiple accounts" + )); + msges.push(format!( + "Total accounts: {}", + self.account_2_failedcnt.keys().count() + )); + msges.push(format!( + "Total logon failures: {}\n\n", + self.total_failed_logons + )); + + return Option::Some(msges); } pub fn detection( @@ -67,13 +92,25 @@ impl Security { ) { self.process_created(&event_id, &event_data); self.se_debug_privilege(&event_id, &event_data); - self.account_created(&event_id, &event_data); - self.add_member_security_group(&event_id, &event_data); + self.account_created(&event_id, &event_data) + .and_then(Security::print_console); + self.add_member_security_group(&event_id, &event_data) + .and_then(Security::print_console); self.failed_logon(&event_id, &event_data); - self.sensitive_priviledge(&event_id, &event_data); - self.attempt_priviledge(&event_id, &event_data); - self.pass_spray(&event_id, &event_data); - self.audit_log_cleared(&event_id, &user_data); + self.sensitive_priviledge(&event_id, &event_data) + .and_then(Security::print_console); + self.attempt_priviledge(&event_id, &event_data) + .and_then(Security::print_console); + self.pass_spray(&event_id, &event_data) + .and_then(Security::print_console); + self.audit_log_cleared(&event_id, &user_data) + .and_then(Security::print_console); + } + + fn print_console(v: Vec) -> Option> { + v.iter().for_each(|s| println!("{}", s)); + println!("\n"); + return Option::Some(v); } fn process_created(&mut self, event_id: &String, _event_data: &HashMap) { @@ -138,20 +175,24 @@ impl Security { } // account craeted:OK - fn account_created(&mut self, event_id: &String, event_data: &HashMap) { + fn account_created( + &mut self, + event_id: &String, + event_data: &HashMap, + ) -> Option> { if event_id != "4720" { - return; + return Option::None; } - println!("New User Created"); - println!( - "Username: {}", - event_data.get("TargetUserName").unwrap_or(&self.empty_str) - ); - println!( - "User SID:: {}\n\n", - event_data.get("TargetSid").unwrap_or(&self.empty_str) - ); + let mut msges: Vec = Vec::new(); + msges.push("New User Created".to_string()); + + let username = event_data.get("TargetUserName").unwrap_or(&self.empty_str); + msges.push(format!("Username: {}", username)); + let sid = event_data.get("TargetSid").unwrap_or(&self.empty_str); + msges.push(format!("TargetSid: {}", sid)); + + return Option::Some(msges); } // add member to security group @@ -159,31 +200,30 @@ impl Security { &mut self, event_id: &String, event_data: &HashMap, - ) { + ) -> Option> { // check if group is Administrator, may later expand to all groups if event_data.get("TargetUserName").unwrap_or(&self.empty_str) != "Administrators" { - return; + return Option::None; } // A member was added to a security-enabled (global|local|universal) group. + let mut msges: Vec = Vec::new(); if event_id == "4728" { - println!("User added to global Administrators group"); + msges.push("User added to global Administrators group".to_string()); } else if event_id == "4732" { - println!("User added to local Administrators group"); + msges.push("User added to local Administrators group".to_string()); } else if event_id == "4756" { - println!("User added to universal Administrators group"); + msges.push("User added to universal Administrators group".to_string()); } else { - return; + return Option::None; } - println!( - "Username: {}", - event_data.get("TargetUserName").unwrap_or(&self.empty_str) - ); - println!( - "User SID:: {}\n\n", - event_data.get("TargetSid").unwrap_or(&self.empty_str) - ); + let username = event_data.get("TargetUserName").unwrap_or(&self.empty_str); + msges.push(format!("Username: {}", username)); + let sid = event_data.get("TargetSid").unwrap_or(&self.empty_str); + msges.push(format!("TargetSid: {}", sid)); + + return Option::Some(msges); } // An account failed to log on.:OK @@ -203,31 +243,42 @@ impl Security { } // Sensitive Privilege Use (Mimikatz) - fn sensitive_priviledge(&mut self, event_id: &String, event_data: &HashMap) { + fn sensitive_priviledge( + &mut self, + event_id: &String, + event_data: &HashMap, + ) -> Option> { if event_id != "4673" { - return; + return Option::None; } self.total_sensitive_privuse += 1; + let mut msges: Vec = Vec::new(); // use == operator here to avoid multiple log notices - if self.max_total_sensitive_privuse == self.total_sensitive_privuse { - println!("Sensititive Privilege Use Exceeds Threshold"); - println!( - "Username: {}", - event_data.get("SubjectUserName").unwrap_or(&self.empty_str) - ); - println!( - "Domain Name: {}\n\n", - event_data - .get("SubjectDomainName") - .unwrap_or(&self.empty_str) - ); + if self.max_total_sensitive_privuse != self.total_sensitive_privuse { + return Option::None; } + + msges.push("Sensititive Privilege Use Exceeds Threshold".to_string()); + + let username = event_data.get("SubjectUserName").unwrap_or(&self.empty_str); + msges.push(format!("Username: {}", username)); + + let domainname = event_data + .get("SubjectDomainName") + .unwrap_or(&self.empty_str); + msges.push(format!("Domain Name: {}", domainname)); + + return Option::Some(msges); } - fn attempt_priviledge(&mut self, event_id: &String, event_data: &HashMap) { + fn attempt_priviledge( + &mut self, + event_id: &String, + event_data: &HashMap, + ) -> Option> { if event_id != "4674" { - return; + return Option::None; } // "%%1539" means WRITE_DAC(see detail: https://docs.microsoft.com/ja-jp/windows/security/threat-protection/auditing/event-4663) @@ -237,23 +288,32 @@ impl Security { .to_uppercase(); let accessname = event_data.get("AccessMask").unwrap_or(&self.empty_str); if servicename != r"C:\WINDOWS\SYSTEM32\SERVICES.EXE" || accessname != "%%1539" { - return; + return Option::None; } - println!("Possible Hidden Service Attempt"); - println!("User requested to modify the Dynamic Access Control (DAC) permissions of a sevice, possibly to hide it from view."); + let mut msges: Vec = Vec::new(); + msges.push("Possible Hidden Service Attempt".to_string()); + msges.push("User requested to modify the Dynamic Access Control (DAC) permissions of a sevice, possibly to hide it from view.".to_string()); let username = event_data.get("SubjectUserName").unwrap_or(&self.empty_str); - println!("User: {}", username); + msges.push(format!("User: {}", username)); + let servicename = event_data.get("ObjectName").unwrap_or(&self.empty_str); - println!("Target service: {}", servicename); - println!("WRITE_DAC\n\n"); + msges.push(format!("Target service: {}", servicename)); + + msges.push("WRITE_DAC".to_string()); + + return Option::Some(msges); } // A logon was attempted using explicit credentials. - fn pass_spray(&mut self, event_id: &String, event_data: &HashMap) { + fn pass_spray( + &mut self, + event_id: &String, + event_data: &HashMap, + ) -> Option> { if event_id != "4648" { - return; + return Option::None; } let targetusername = event_data.get("TargetUserName").unwrap_or(&self.empty_str); @@ -263,7 +323,7 @@ impl Security { // check targetuser's attempt count. if self.passspray_2_user.get(targetusername).unwrap_or(&0) <= &self.max_passspray_login { - return; + return Option::None; } // check exceeded targetuser count. @@ -273,7 +333,7 @@ impl Security { .filter(|value| value > &&self.max_passspray_login) .count() as i32; if spray_uniq_user <= self.max_passspray_uniquser { - return; + return Option::None; } let usernames: String = self.passspray_2_user.keys().fold( @@ -285,37 +345,51 @@ impl Security { }, ); - println!("Distributed Account Explicit Credential Use (Password Spray Attack)"); - println!("The use of multiple user account access attempts with explicit credentials is "); - println!("an indicator of a password spray attack."); - println!("Target Usernames: {}", usernames.trim()); - println!( - "Accessing Username: {}", - event_data.get("SubjectUserName").unwrap_or(&self.empty_str) + let mut msges: Vec = Vec::new(); + msges.push( + "Distributed Account Explicit Credential Use (Password Spray Attack)".to_string(), ); - println!( - "Accessing Host Name: {}\n\n", - event_data - .get("SubjectDomainName") - .unwrap_or(&self.empty_str) + msges.push( + "The use of multiple user account access attempts with explicit credentials is " + .to_string(), ); + msges.push("an indicator of a password spray attack.".to_string()); + + msges.push(format!("Target Usernames: {}", usernames.trim())); + let access_username = event_data.get("SubjectUserName").unwrap_or(&self.empty_str); + msges.push(format!("Accessing Username: {}", access_username)); + let access_hostname = event_data + .get("SubjectDomainName") + .unwrap_or(&self.empty_str); + msges.push(format!("Accessing Host Name: {}", access_hostname)); // reset self.passspray_2_user = HashMap::new(); + + return Option::Some(msges); } - fn audit_log_cleared(&mut self, event_id: &String, user_data: &Option) { + fn audit_log_cleared( + &mut self, + event_id: &String, + user_data: &Option, + ) -> Option> { if event_id != "1102" { - return; + return Option::None; } - println!("Audit Log Clear"); - println!("The Audit log was cleared."); - + let mut msges: Vec = Vec::new(); + msges.push("Audit Log Clear".to_string()); + msges.push("The Audit log was cleared.".to_string()); let username = user_data .as_ref() .and_then(|u| u.log_file_cleared.as_ref()) .and_then(|l| l.subject_user_name.as_ref()); - println!("Security ID: {}\n\n", username.unwrap_or(&self.empty_str)); + msges.push(format!( + "Security ID: {}", + username.unwrap_or(&self.empty_str) + )); + + return Option::Some(msges); } }