refactor for test.
This commit is contained in:
@@ -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<Vec<String>> {
|
||||
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<String> = 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<Vec<String>> {
|
||||
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<String> = 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<String>) -> Option<Vec<String>> {
|
||||
v.iter().for_each(|s| println!("{}", s));
|
||||
println!("\n");
|
||||
return Option::Some(v);
|
||||
}
|
||||
|
||||
fn process_created(&mut self, event_id: &String, _event_data: &HashMap<String, String>) {
|
||||
@@ -138,20 +175,24 @@ impl Security {
|
||||
}
|
||||
|
||||
// account craeted:OK
|
||||
fn account_created(&mut self, event_id: &String, event_data: &HashMap<String, String>) {
|
||||
fn account_created(
|
||||
&mut self,
|
||||
event_id: &String,
|
||||
event_data: &HashMap<String, String>,
|
||||
) -> Option<Vec<String>> {
|
||||
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<String> = 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<String, String>,
|
||||
) {
|
||||
) -> Option<Vec<String>> {
|
||||
// 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<String> = 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<String, String>) {
|
||||
fn sensitive_priviledge(
|
||||
&mut self,
|
||||
event_id: &String,
|
||||
event_data: &HashMap<String, String>,
|
||||
) -> Option<Vec<String>> {
|
||||
if event_id != "4673" {
|
||||
return;
|
||||
return Option::None;
|
||||
}
|
||||
|
||||
self.total_sensitive_privuse += 1;
|
||||
let mut msges: Vec<String> = 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<String, String>) {
|
||||
fn attempt_priviledge(
|
||||
&mut self,
|
||||
event_id: &String,
|
||||
event_data: &HashMap<String, String>,
|
||||
) -> Option<Vec<String>> {
|
||||
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<String> = 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<String, String>) {
|
||||
fn pass_spray(
|
||||
&mut self,
|
||||
event_id: &String,
|
||||
event_data: &HashMap<String, String>,
|
||||
) -> Option<Vec<String>> {
|
||||
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<String> = 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<event::UserData>) {
|
||||
fn audit_log_cleared(
|
||||
&mut self,
|
||||
event_id: &String,
|
||||
user_data: &Option<event::UserData>,
|
||||
) -> Option<Vec<String>> {
|
||||
if event_id != "1102" {
|
||||
return;
|
||||
return Option::None;
|
||||
}
|
||||
|
||||
println!("Audit Log Clear");
|
||||
println!("The Audit log was cleared.");
|
||||
|
||||
let mut msges: Vec<String> = 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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user