Security module Implemented without 4674
This commit is contained in:
@@ -38,7 +38,7 @@ impl Detection {
|
||||
&common.detection(&event.system, &event_data);
|
||||
//&common.detection(&event.system, &event_data);
|
||||
if channel == "Security" {
|
||||
&security.detection(event_id, &event.system, event_data);
|
||||
&security.detection(event_id, &event.system, &event.user_data, event_data);
|
||||
} else if channel == "System" {
|
||||
&system.detection(event_id, &event.system, event_data);
|
||||
} else if channel == "Application" {
|
||||
|
||||
@@ -1,21 +1,45 @@
|
||||
use crate::models::event;
|
||||
use std::collections::HashMap;
|
||||
|
||||
// eventlogが用意できていない
|
||||
// 4674
|
||||
// 4756
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Security {
|
||||
max_total_sensitive_privuse: i32,
|
||||
max_passspray_login: i32,
|
||||
max_passspray_uniquser: i32,
|
||||
max_failed_logons: i32,
|
||||
alert_all_admin: i32,
|
||||
total_admin_logons: i32,
|
||||
total_failed_logons: i32,
|
||||
total_failed_account: i32,
|
||||
total_sensitive_privuse: i32,
|
||||
admin_logons: HashMap<String, HashMap<String, i32>>,
|
||||
multiple_admin_logons: HashMap<String, i32>,
|
||||
account_2_failedcnt: HashMap<String, i32>,
|
||||
passspray_2_user: HashMap<String, i32>,
|
||||
empty_str: String,
|
||||
}
|
||||
|
||||
impl Security {
|
||||
pub fn new() -> Security {
|
||||
Security {
|
||||
max_total_sensitive_privuse: 4,
|
||||
max_passspray_login: 6,
|
||||
max_passspray_uniquser: 6,
|
||||
max_failed_logons: 5,
|
||||
alert_all_admin: 0,
|
||||
total_admin_logons: 0,
|
||||
total_failed_logons: 0,
|
||||
total_failed_account: 0,
|
||||
total_sensitive_privuse: 0,
|
||||
admin_logons: HashMap::new(),
|
||||
multiple_admin_logons: HashMap::new(),
|
||||
account_2_failedcnt: HashMap::new(),
|
||||
passspray_2_user: HashMap::new(),
|
||||
empty_str: String::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,25 +47,54 @@ impl Security {
|
||||
if self.total_admin_logons > 0 {
|
||||
println!("total_admin_logons:{}", self.total_admin_logons);
|
||||
println!("admin_logons:{:?}", self.admin_logons);
|
||||
println!("multiple_admin_logons:{:?}", self.multiple_admin_logons);
|
||||
println!("multiple_admin_logons:{:?}\n", self.multiple_admin_logons);
|
||||
}
|
||||
|
||||
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", self.total_failed_logons);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detection(
|
||||
&mut self,
|
||||
event_id: String,
|
||||
system: &event::System,
|
||||
_system: &event::System,
|
||||
user_data: &Option<event::UserData>,
|
||||
event_data: HashMap<String, String>,
|
||||
) {
|
||||
if event_id == "4672" {
|
||||
&self.se_debug_privilege(event_data);
|
||||
self.process_craeted(&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.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);
|
||||
}
|
||||
|
||||
fn process_craeted(&mut self, event_id: &String, _event_data: &HashMap<String, String>) {
|
||||
if event_id != "4688" {
|
||||
return;
|
||||
}
|
||||
// TODO Check-Commnad
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Special privileges assigned to new logon (possible admin access)
|
||||
//
|
||||
fn se_debug_privilege(&mut self, event_data: HashMap<String, String>) {
|
||||
fn se_debug_privilege(&mut self, event_id: &String, event_data: &HashMap<String, String>) {
|
||||
if event_id != "4672" {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(privileage_list) = event_data.get("PrivilegeList") {
|
||||
if let Some(_data) = privileage_list.find("SeDebugPrivilege") {
|
||||
@@ -72,10 +125,8 @@ impl Security {
|
||||
event_data["SubjectUserSid"].to_string(),
|
||||
sid[&event_data["SubjectUserSid"]] + 1,
|
||||
);
|
||||
self.admin_logons.insert(
|
||||
event_data["SubjectUserName"].to_string(),
|
||||
count_hash,
|
||||
);
|
||||
self.admin_logons
|
||||
.insert(event_data["SubjectUserName"].to_string(), count_hash);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
@@ -89,4 +140,169 @@ impl Security {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// account craeted:OK
|
||||
fn account_created(&mut self, event_id: &String, event_data: &HashMap<String, String>) {
|
||||
if event_id != "4720" {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("New User Created");
|
||||
println!(
|
||||
"Username: {}",
|
||||
event_data.get("TargetUserName").unwrap_or(&"".to_string())
|
||||
);
|
||||
println!(
|
||||
"User SID:: {}\n",
|
||||
event_data.get("TargetSid").unwrap_or(&"".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
// add member to security group
|
||||
fn add_member_security_group(
|
||||
&mut self,
|
||||
event_id: &String,
|
||||
event_data: &HashMap<String, String>,
|
||||
) {
|
||||
// check if group is Administrator, may later expand to all groups
|
||||
if event_data.get("TargetUserName").unwrap_or(&self.empty_str) != "Administrators" {
|
||||
return;
|
||||
}
|
||||
|
||||
// A member was added to a security-enabled (global|local|universal) group.
|
||||
if event_id == "4728" {
|
||||
println!("User added to global Administrators group");
|
||||
} else if event_id == "4732" {
|
||||
println!("User added to local Administrators group");
|
||||
} else if event_id == "4756" {
|
||||
println!("User added to universal Administrators group");
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
println!(
|
||||
"Username: {}",
|
||||
event_data.get("TargetUserName").unwrap_or(&"".to_string())
|
||||
);
|
||||
println!(
|
||||
"User SID:: {}\n",
|
||||
event_data.get("TargetSid").unwrap_or(&"".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
// An account failed to log on.:OK
|
||||
// Requires auditing logon failures
|
||||
// https://technet.microsoft.com/en-us/library/cc976395.aspx
|
||||
fn failed_logon(&mut self, event_id: &String, event_data: &HashMap<String, String>) {
|
||||
if event_id != "4625" {
|
||||
return;
|
||||
}
|
||||
|
||||
// see fn disp()
|
||||
self.total_failed_logons += 1;
|
||||
let username = event_data.get("TargetUserName").unwrap_or(&self.empty_str);
|
||||
let failed_cnt = self.account_2_failedcnt.get(username).unwrap_or(&0) + &1;
|
||||
self.account_2_failedcnt
|
||||
.insert(username.to_string(), failed_cnt);
|
||||
}
|
||||
|
||||
// Sensitive Privilege Use (Mimikatz)
|
||||
fn sensitive_priviledge(&mut self, event_id: &String, event_data: &HashMap<String, String>) {
|
||||
if event_id != "4673" {
|
||||
return;
|
||||
}
|
||||
|
||||
self.total_sensitive_privuse += 1;
|
||||
// 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: {}",
|
||||
event_data
|
||||
.get("SubjectDomainName")
|
||||
.unwrap_or(&self.empty_str)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn attempt_priviledge(&mut self, _event_id: &String, _event_data: &HashMap<String, String>) {
|
||||
// event log cannot get...
|
||||
}
|
||||
|
||||
// A logon was attempted using explicit credentials.
|
||||
fn pass_spray(&mut self, event_id: &String, event_data: &HashMap<String, String>) {
|
||||
if event_id != "4648" {
|
||||
return;
|
||||
}
|
||||
|
||||
let targetusername = event_data.get("TargetUserName").unwrap_or(&self.empty_str);
|
||||
let spray_cnt = self.passspray_2_user.get(targetusername).unwrap_or(&0) + 1;
|
||||
self.passspray_2_user
|
||||
.insert(targetusername.to_string(), spray_cnt);
|
||||
|
||||
// check targetuser's attempt count.
|
||||
if self.passspray_2_user.get(targetusername).unwrap_or(&0) <= &self.max_passspray_login {
|
||||
return;
|
||||
}
|
||||
|
||||
// check exceeded targetuser count.
|
||||
let spray_uniq_user = self
|
||||
.passspray_2_user
|
||||
.values()
|
||||
.filter(|value| value > &&self.max_passspray_login)
|
||||
.count() as i32;
|
||||
if spray_uniq_user <= self.max_passspray_uniquser {
|
||||
return;
|
||||
}
|
||||
|
||||
let usernames: String = self.passspray_2_user.keys().fold(
|
||||
self.empty_str.to_string(),
|
||||
|mut acc: String, cur| -> String {
|
||||
acc.push_str(cur);
|
||||
acc.push_str(" ");
|
||||
return acc;
|
||||
},
|
||||
);
|
||||
|
||||
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)
|
||||
);
|
||||
println!(
|
||||
"Accessing Host Name: {}\n\n",
|
||||
event_data
|
||||
.get("SubjectDomainName")
|
||||
.unwrap_or(&self.empty_str)
|
||||
);
|
||||
|
||||
// reset
|
||||
self.passspray_2_user = HashMap::new();
|
||||
}
|
||||
|
||||
fn audit_log_cleared(
|
||||
&mut self,
|
||||
event_id: &String,
|
||||
user_data: &Option<event::UserData>
|
||||
) {
|
||||
if event_id != "1102" {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("Audit Log Clear");
|
||||
println!("The Audit log was cleared.");
|
||||
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: {}", username.unwrap_or(&"".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ pub struct System {
|
||||
#[serde(rename = "EventID")]
|
||||
pub event_id: String,
|
||||
#[serde(rename = "Version")]
|
||||
version: Option<String>,
|
||||
pub version: Option<String>,
|
||||
#[serde(rename = "Level")]
|
||||
level: String,
|
||||
#[serde(rename = "Task")]
|
||||
@@ -72,12 +72,32 @@ pub struct EventData {
|
||||
pub data: Option<Vec<Data>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
pub struct UserData {
|
||||
#[serde(rename = "LogFileCleared")]
|
||||
pub log_file_cleared: Option<LogFileCleared>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
pub struct LogFileCleared {
|
||||
#[serde(rename = "SubjectUserSid")]
|
||||
pub subject_user_sid: Option<String>,
|
||||
#[serde(rename = "SubjectUserName")]
|
||||
pub subject_user_name: Option<String>,
|
||||
#[serde(rename = "SubjectDomainName")]
|
||||
pub subject_domain_name: Option<String>,
|
||||
#[serde(rename = "SubjectLogonId")]
|
||||
pub subject_logon_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
pub struct Evtx {
|
||||
#[serde(rename = "System")]
|
||||
pub system: System,
|
||||
#[serde(rename = "EventData")]
|
||||
pub event_data: Option<EventData>,
|
||||
#[serde(rename = "UserData")]
|
||||
pub user_data: Option<UserData>,
|
||||
}
|
||||
|
||||
impl Evtx {
|
||||
|
||||
Reference in New Issue
Block a user