diff --git a/src/detections/security.rs b/src/detections/security.rs index 37bcc74b..97a30ce5 100644 --- a/src/detections/security.rs +++ b/src/detections/security.rs @@ -40,11 +40,11 @@ impl Security { } pub fn disp(&self) { - self.fmt_admin_logons().and_then(Security::print_console); - self.fmt_passspray().and_then(Security::print_console); + self.disp_admin_logons().and_then(Security::print_console); + self.disp_login_failed().and_then(Security::print_console); } - fn fmt_admin_logons(&self) -> Option> { + fn disp_admin_logons(&self) -> Option> { if self.total_admin_logons < 1 { return Option::None; } @@ -53,15 +53,16 @@ impl Security { 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", + "multiple_admin_logons:{:?}", self.multiple_admin_logons )); return Option::Some(msges); } - fn fmt_passspray(&self) -> Option> { + fn disp_login_failed(&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; @@ -76,7 +77,7 @@ impl Security { self.account_2_failedcnt.keys().count() )); msges.push(format!( - "Total logon failures: {}\n\n", + "Total logon failures: {}", self.total_failed_logons )); @@ -190,7 +191,7 @@ impl Security { 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)); + msges.push(format!("User SID: {}", sid)); return Option::Some(msges); } @@ -218,10 +219,10 @@ impl Security { return Option::None; } - let username = event_data.get("TargetUserName").unwrap_or(&self.empty_str); + let username = event_data.get("MemberName").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)); + let sid = event_data.get("MemberSid").unwrap_or(&self.empty_str); + msges.push(format!("User SID: {}", sid)); return Option::Some(msges); } @@ -260,6 +261,7 @@ impl Security { } msges.push("Sensititive Privilege Use Exceeds Threshold".to_string()); + msges.push("Potentially indicative of Mimikatz, multiple sensitive privilege calls have been made".to_string()); let username = event_data.get("SubjectUserName").unwrap_or(&self.empty_str); msges.push(format!("Username: {}", username)); @@ -293,7 +295,7 @@ impl Security { 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()); + 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); msges.push(format!("User: {}", username)); @@ -321,11 +323,6 @@ impl Security { 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 Option::None; - } - // check exceeded targetuser count. let spray_uniq_user = self .passspray_2_user @@ -336,7 +333,11 @@ impl Security { return Option::None; } - let usernames: String = self.passspray_2_user.keys().fold( + // let v_username = Vec::new(); + let mut v_username = Vec::new(); + self.passspray_2_user.keys().for_each(|u| v_username.push(u)); + v_username.sort(); + let usernames: String = v_username.iter().fold( self.empty_str.to_string(), |mut acc: String, cur| -> String { acc.push_str(cur); @@ -353,11 +354,13 @@ impl Security { "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("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); @@ -380,7 +383,7 @@ impl Security { let mut msges: Vec = Vec::new(); msges.push("Audit Log Clear".to_string()); - msges.push("The Audit log was cleared.".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()) @@ -393,3 +396,878 @@ impl Security { return Option::Some(msges); } } + +#[cfg(test)] +mod tests { + extern crate quick_xml; + + use crate::detections::security; + use crate::models::event; + + // 正しくヒットするパターン + #[test] + fn test_account_created_hit() { + let xml_str = get_account_created_xml(); + let event: event::Evtx = quick_xml::de::from_str(&xml_str) + .map_err(|e| { + println!("{}", e.to_string()); + }) + .unwrap(); + + let mut sec = security::Security::new(); + let option_v = sec.account_created( + &event.system.event_id.to_string(), + &event.parse_event_data(), + ); + + let v = option_v.unwrap(); + let mut ite = v.iter(); + assert_eq!( + &"New User Created".to_string(), + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!( + &"Username: IEUser".to_string(), + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!( + &"User SID: S-1-5-21-3463664321-2923530833-3546627382-1000", + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!(Option::None, ite.next()); + } + + // event idが異なるパターン + #[test] + fn test_account_created_noteq_eventid() { + let xml_str = + get_account_created_xml().replace("4720", "4721"); + let event: event::Evtx = quick_xml::de::from_str(&xml_str) + .map_err(|e| { + println!("{}", e.to_string()); + }) + .unwrap(); + + let mut sec = security::Security::new(); + let option_v = sec.account_created( + &event.system.event_id.to_string(), + &event.parse_event_data(), + ); + + assert_eq!(Option::None, option_v); + } + + // 実在するかどうか不明だが、EventDataの必要なフィールドがないパターン + #[test] + fn test_account_created_none_check() { + let xml_str = r#" + + + + + 4720 + 0 + 0 + 13824 + 0 + 0x8020000000000000 + + 112 + + + Security + IE8Win7 + + + + "#; + let event: event::Evtx = quick_xml::de::from_str(&xml_str) + .map_err(|e| { + println!("{}", e.to_string()); + }) + .unwrap(); + + let mut sec = security::Security::new(); + let option_v = sec.account_created( + &event.system.event_id.to_string(), + &event.parse_event_data(), + ); + + let v = option_v.unwrap(); + let mut ite = v.iter(); + assert_eq!( + &"New User Created".to_string(), + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!( + &"Username: ".to_string(), + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!(&"User SID: ", ite.next().unwrap_or(&"".to_string())); + assert_eq!(Option::None, ite.next()); + } + + fn get_account_created_xml() -> String { + return r#" + + + + + 4720 + 0 + 0 + 13824 + 0 + 0x8020000000000000 + + 112 + + + Security + IE8Win7 + + + + IEUser + IE8Win7 + S-1-5-21-3463664321-2923530833-3546627382-1000 + S-1-5-18 + WIN-QALA5Q3KJ43$ + WORKGROUP + 0x3e7 + - + IEUserSam + %%1793 + - + %%1793 + %%1793 + %%1793 + %%1793 + %%1793 + %%1794 + %%1794 + 513 + - + 0x0 + 0x15 + + %%2080 + %%2082 + %%2084 + %%1793 + - + %%1797 + + "#.to_string(); + } + + // 正しくヒットするパターン(eventid=4732) + #[test] + fn test_add_member_security_group_hit_4732() { + let xml_str = get_add_member_security_group_xml(); + let event: event::Evtx = quick_xml::de::from_str(&xml_str) + .map_err(|e| { + println!("{}", e.to_string()); + }) + .unwrap(); + + let mut sec = security::Security::new(); + let option_v = sec.add_member_security_group( + &event.system.event_id.to_string(), + &event.parse_event_data(), + ); + + let v = option_v.unwrap(); + let mut ite = v.iter(); + assert_eq!( + &"User added to local Administrators group".to_string(), + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!( + &"Username: testnamess".to_string(), + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!( + &"User SID: S-1-5-21-3463664321-2923530833-3546627382-1000", + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!(Option::None, ite.next()); + } + + // 正しくヒットするパターン(eventid=4728は一行目が変わる) + #[test] + fn test_add_member_security_group_hit_4728() { + let xml_str = get_add_member_security_group_xml() + .replace(r"4732", r"4728"); + let event: event::Evtx = quick_xml::de::from_str(&xml_str) + .map_err(|e| { + println!("{}", e.to_string()); + }) + .unwrap(); + + let mut sec = security::Security::new(); + let option_v = sec.add_member_security_group( + &event.system.event_id.to_string(), + &event.parse_event_data(), + ); + + let v = option_v.unwrap(); + let mut ite = v.iter(); + assert_eq!( + &"User added to global Administrators group".to_string(), + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!( + &"Username: testnamess".to_string(), + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!( + &"User SID: S-1-5-21-3463664321-2923530833-3546627382-1000", + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!(Option::None, ite.next()); + } + + // 正しくヒットするパターン(eventid=4756は一行目が変わる) + #[test] + fn test_add_member_security_group_hit_4756() { + let xml_str = get_add_member_security_group_xml() + .replace(r"4732", r"4756"); + let event: event::Evtx = quick_xml::de::from_str(&xml_str) + .map_err(|e| { + println!("{}", e.to_string()); + }) + .unwrap(); + + let mut sec = security::Security::new(); + let option_v = sec.add_member_security_group( + &event.system.event_id.to_string(), + &event.parse_event_data(), + ); + + let v = option_v.unwrap(); + let mut ite = v.iter(); + assert_eq!( + &"User added to universal Administrators group".to_string(), + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!( + &"Username: testnamess".to_string(), + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!( + &"User SID: S-1-5-21-3463664321-2923530833-3546627382-1000", + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!(Option::None, ite.next()); + } + + // eventidが異なりヒットしないパターン + #[test] + fn test_add_member_security_group_noteq_eventid() { + let xml_str = get_add_member_security_group_xml().replace(r"4732", r"4757"); + let event: event::Evtx = quick_xml::de::from_str(&xml_str) + .map_err(|e| { + println!("{}", e.to_string()); + }) + .unwrap(); + + let mut sec = security::Security::new(); + let option_v = sec.add_member_security_group( + &event.system.event_id.to_string(), + &event.parse_event_data(), + ); + assert_eq!(Option::None, option_v); + } + + // グループがAdministratorsじゃなくてHitしないパターン + #[test] + fn test_add_member_security_not_administrators() { + let xml_str = get_add_member_security_group_xml().replace(r"Administrators", r"local"); + let event: event::Evtx = quick_xml::de::from_str(&xml_str) + .map_err(|e| { + println!("{}", e.to_string()); + }) + .unwrap(); + + let mut sec = security::Security::new(); + let option_v = sec.add_member_security_group( + &event.system.event_id.to_string(), + &event.parse_event_data(), + ); + assert_eq!(Option::None, option_v); + } + + // hitするけど表示するフィールドがない場合 + #[test] + fn test_add_member_security_group_none() { + let xml_str = r#" + + + + + 4732 + 0 + 0 + 13826 + 0 + 0x8020000000000000 + + 116 + + + Security + IE8Win7 + + + + Administrators + + "#; + let event: event::Evtx = quick_xml::de::from_str(&xml_str) + .map_err(|e| { + println!("{}", e.to_string()); + }) + .unwrap(); + + let mut sec = security::Security::new(); + let option_v = sec.add_member_security_group( + &event.system.event_id.to_string(), + &event.parse_event_data(), + ); + + let v = option_v.unwrap(); + let mut ite = v.iter(); + assert_eq!( + &"User added to local Administrators group".to_string(), + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!( + &"Username: ".to_string(), + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!( + &"User SID: ", + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!(Option::None, ite.next()); + } + + fn get_add_member_security_group_xml() -> String { + return r#" + + + + + 4732 + 0 + 0 + 13826 + 0 + 0x8020000000000000 + + 116 + + + Security + IE8Win7 + + + + testnamess + S-1-5-21-3463664321-2923530833-3546627382-1000 + Administrators + Builtin + S-1-5-32-544 + S-1-5-18 + WIN-QALA5Q3KJ43$ + WORKGROUP + 0x3e7 + - + + "#.to_string(); + } + + // ユーザー数が一つなら、ログ数が幾らあっても、メッセージは表示されないはず。 + #[test] + fn test_failed_logon_nothit_onlyoneuser() { + let xml_str = get_failed_logon_xml(); + let event: event::Evtx = quick_xml::de::from_str(&xml_str).unwrap(); + + let mut sec = security::Security::new(); + + sec.max_failed_logons = 5; + let ite = [1,2,3,4,5,6,7].iter(); + ite.for_each(|i|{ + sec.failed_logon( + &event.system.event_id.to_string(), + &event.parse_event_data(), + ); + assert_eq!(i, &sec.total_failed_logons); + assert_eq!(Option::None, sec.disp_login_failed()); + }); + } + + // 失敗回数を増やしていき、境界値でメッセージが表示されることのテスト。 + #[test] + fn test_failed_logon_hit() { + let xml_str = get_failed_logon_xml(); + let event: event::Evtx = quick_xml::de::from_str(&xml_str).unwrap(); + let event_another: event::Evtx = quick_xml::de::from_str(&xml_str.replace(r"Administrator", r"localuser")).unwrap(); + + let mut sec = security::Security::new(); + sec.max_failed_logons = 5; + + // メッセージが表示されるには2ユーザー以上失敗している必要がある。まず一人目 + sec.failed_logon( + &event.system.event_id.to_string(), + &event.parse_event_data(), + ); + assert_eq!(1, sec.total_failed_logons); + + let ite = [1,2,3,4,5,6,7].iter(); + ite.for_each(|i|{ + sec.failed_logon( + &event_another.system.event_id.to_string(), + &event_another.parse_event_data(), + ); + let fail_cnt = i +1; + assert_eq!(fail_cnt, sec.total_failed_logons); + if fail_cnt > 5 { + let v = sec.disp_login_failed().unwrap(); + let mut ite = v.iter(); + assert_eq!( + &"High number of total logon failures for multiple accounts".to_string(), + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!( + &"Total accounts: 2".to_string(), + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!( + &format!("Total logon failures: {}", fail_cnt), + ite.next().unwrap_or(&"".to_string()) + ); + // assert_eq!(Option::None, ite.next()); + } else { + assert_eq!(Option::None, sec.disp_login_failed()); + } + }); + + // hitするけど表示するフィールドがない場合 + let xml_nofield = r#" + + + 4625001254400x80100000000000006016SecurityDESKTOP-M5SN04R + + "#; + + // エラーにならなければOK + let event_nofield: event::Evtx = quick_xml::de::from_str(xml_nofield).unwrap(); + sec.failed_logon( + &event_nofield.system.event_id.to_string(), + &event_nofield.parse_event_data(), + ); + } + + // 失敗回数を増やしていき、境界値でメッセージが表示されることのテスト。 + #[test] + fn test_failed_logon_noteq_eventid() { + let xml_str = get_failed_logon_xml(); + let event: event::Evtx = quick_xml::de::from_str(&xml_str.replace(r"4625",r"4626")).unwrap(); + let event_another: event::Evtx = quick_xml::de::from_str(&xml_str.replace(r"4625",r"4626").replace(r"Administrator", r"localuser")).unwrap(); + + let mut sec = security::Security::new(); + sec.max_failed_logons = 5; + + // メッセージが表示されるには2ユーザー以上失敗している必要がある。まず一人目 + sec.failed_logon( + &event.system.event_id.to_string(), + &event.parse_event_data(), + ); + assert_eq!(0, sec.total_failed_logons); + + let ite = [1,2,3,4,5,6,7].iter(); + ite.for_each(|_i|{ + sec.failed_logon( + &event_another.system.event_id.to_string(), + &event_another.parse_event_data(), + ); + assert_eq!(0, sec.total_failed_logons); + assert_eq!(Option::None, sec.disp_login_failed()); + }); + } + + fn get_failed_logon_xml() -> String { + return r#" + + + + 4625 + 0 + 0 + 12544 + 0 + 0x8010000000000000 + + 6016 + + + Security + DESKTOP-M5SN04R + + + + S-1-0-0 + - + - + 0x0 + S-1-0-0 + Administrator + . + 0xc000006d + %%2313 + 0xc000006a + 3 + NtLmSsp + NTLM + fpEbpiox2Q3Qf8av + - + - + 0 + 0x0 + - + 192.168.198.149 + 33083 + + "#.to_string(); + } + + // Hitするパターンとしないパターンをまとめてテスト + #[test] + fn test_sensitive_priviledge_hit() { + let xml_str = get_sensitive_prividedge_hit(); + let event: event::Evtx = quick_xml::de::from_str(&xml_str).unwrap(); + + let mut sec = security::Security::new(); + sec.max_total_sensitive_privuse = 6; + + let ite = [1,2,3,4,5,6,7].iter(); + ite.for_each(|i| { + let msg = sec.sensitive_priviledge(&event.system.event_id.to_string(), &event.parse_event_data()); + // i == 7ときにHitしない + if i == &6 { + let v = msg.unwrap(); + let mut ite = v.iter(); + assert_eq!( + &"Sensititive Privilege Use Exceeds Threshold".to_string(), + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!( + &"Potentially indicative of Mimikatz, multiple sensitive privilege calls have been made".to_string(), + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!( + &"Username: Sec504".to_string(), + ite.next().unwrap_or(&"".to_string()) + ); + assert_eq!( + &"Domain Name: SEC504STUDENT", + ite.next().unwrap_or(&"".to_string()) + ); + } else { + assert_eq!(Option::None, msg); + } + }); + } + + // eventidが異なるので、Hitしないテスト + #[test] + fn test_sensitive_priviledge_noteq_eventid() { + let xml_str = get_sensitive_prividedge_hit().replace(r"4673", r"4674"); + let event: event::Evtx = quick_xml::de::from_str(&xml_str).unwrap(); + + let mut sec = security::Security::new(); + sec.max_total_sensitive_privuse = 6; + + let ite = [1,2,3,4,5,6,7].iter(); + ite.for_each(|_i| { + let msg = sec.sensitive_priviledge(&event.system.event_id.to_string(), &event.parse_event_data()); + assert_eq!(Option::None, msg); + }); + } + + fn get_sensitive_prividedge_hit() -> String { + return r#" + + + + 4673 + 0 + 0 + 13056 + 0 + 0x8010000000000000 + + 8936 + + + Security + Sec504Student + + + + S-1-5-21-2977773840-2930198165-1551093962-1000 + Sec504 + SEC504STUDENT + 0x1e3dd + Security + - + SeTcbPrivilege + 0x15a8 + C:\Tools\mimikatz\mimikatz.exe + + "#.to_string(); + } + + // Hitするテスト + #[test] + fn test_attempt_priviledge_hit() { + let xml_str = get_attempt_priviledge_xml(); + let event: event::Evtx = quick_xml::de::from_str(&xml_str).unwrap(); + + let mut sec = security::Security::new(); + let msg = sec.attempt_priviledge(&event.system.event_id.to_string(), &event.parse_event_data()); + + assert_ne!(Option::None, msg); + let v = msg.unwrap(); + let mut ite = v.iter(); + assert_eq!(&"Possible Hidden Service Attempt".to_string(), ite.next().unwrap_or(&"".to_string())); + assert_eq!(&"User requested to modify the Dynamic Access Control (DAC) permissions of a sevice, possibly to hide it from view".to_string(), ite.next().unwrap_or(&"".to_string())); + assert_eq!(&"User: Sec504".to_string(), ite.next().unwrap_or(&"".to_string())); + assert_eq!(&"Target service: nginx".to_string(), ite.next().unwrap_or(&"".to_string())); + assert_eq!(&"WRITE_DAC".to_string(), ite.next().unwrap_or(&"".to_string())); + assert_eq!(Option::None, ite.next()); + } + + // accessmaskが異なるので、Hitしないテスト + #[test] + fn test_attempt_priviledge_noteq_accessmask() { + let xml_str = get_attempt_priviledge_xml(); + let event: event::Evtx = quick_xml::de::from_str(&xml_str.replace(r"%%1539",r"%%1538")).unwrap(); + + let mut sec = security::Security::new(); + let msg = sec.attempt_priviledge(&event.system.event_id.to_string(), &event.parse_event_data()); + + assert_eq!(Option::None, msg); + } + + // Serviceが違うのでHitしないテスト + #[test] + fn test_attempt_priviledge_noteq_service() { + let xml_str = get_attempt_priviledge_xml(); + let event: event::Evtx = quick_xml::de::from_str(&xml_str.replace(r"C:\Windows\System32\services.exe",r"C:\Windows\System32\lsass.exe")).unwrap(); + + let mut sec = security::Security::new(); + let msg = sec.attempt_priviledge(&event.system.event_id.to_string(), &event.parse_event_data()); + + assert_eq!(Option::None, msg); + } + + // EventIDが違うのでHitしないテスト + #[test] + fn test_attempt_priviledge_noteq_eventid() { + let xml_str = get_attempt_priviledge_xml(); + let event: event::Evtx = quick_xml::de::from_str(&xml_str.replace(r"4674",r"4675")).unwrap(); + + let mut sec = security::Security::new(); + let msg = sec.attempt_priviledge(&event.system.event_id.to_string(), &event.parse_event_data()); + + assert_eq!(Option::None, msg); + } + + fn get_attempt_priviledge_xml() -> String { + return r#" + + + + 4674 + 0 + 0 + 13056 + 0 + 0x8020000000000000 + + 39406 + + + Security + Sec504Student + + + + S-1-5-21-2977773840-2930198165-1551093962-1000 + Sec504 + SEC504STUDENT + 0x99e3d + SC Manager + SERVICE OBJECT + nginx + 0xffff820cb1d95928 + %%1539 + + SeSecurityPrivilege + 0x21c + C:\Windows\System32\services.exe + + "#.to_string(); + } + + #[test] + fn test_pass_spray_hit() { + let mut sec = security::Security::new(); + // 6ユーザまでは表示されず、7ユーザー以上で表示されるようになる。 + sec.max_passspray_login = 6; + sec.max_passspray_uniquser = 6; + + test_pass_spray_hit_1cycle(&mut sec,"4648".to_string(), true); + // counterがreset確認のため、2回実行 + test_pass_spray_hit_1cycle(&mut sec,"4648".to_string(), true); + } + + + // eventid異なるので、Hitしないはず + #[test] + fn test_pass_spray_noteq_eventid() { + let mut sec = security::Security::new(); + // 6ユーザまでは表示されず、7ユーザー以上で表示されるようになる。 + sec.max_passspray_login = 6; + sec.max_passspray_uniquser = 6; + + test_pass_spray_hit_1cycle(&mut sec,"4649".to_string(), false); + // counterがreset確認のため、2回実行 + test_pass_spray_hit_1cycle(&mut sec,"4649".to_string(), false); + } + + fn test_pass_spray_hit_1cycle( sec: &mut security::Security, event_id:String, is_eq:bool ) { + [1,2,3,4,5,6,7].iter().for_each(|i| { + let rep_str = format!(r#"smisenar{}"#,i); + let event_id_tag = format!("{}", event_id); + let xml_str = get_passs_pray_hit().replace(r#"smisenar"#, &rep_str).replace(r"4648", &event_id_tag); + let event: event::Evtx = quick_xml::de::from_str(&xml_str).unwrap(); + [1,2,3,4,5,6,7].iter().for_each(|k|{ + let ret = sec.pass_spray(&event.system.event_id.to_string(), &event.parse_event_data()); + if i == &7 && k == &7 && is_eq { + let v = ret.unwrap(); + let mut ret_ite = v.iter(); + assert_eq!(&"Distributed Account Explicit Credential Use (Password Spray Attack)".to_string(),ret_ite.next().unwrap()); + assert_eq!(&"The use of multiple user account access attempts with explicit credentials is ".to_string(),ret_ite.next().unwrap()); + assert_eq!(&"an indicator of a password spray attack".to_string(),ret_ite.next().unwrap()); + assert_eq!("Target Usernames: smisenar1 smisenar2 smisenar3 smisenar4 smisenar5 smisenar6 smisenar7",ret_ite.next().unwrap()); + assert_eq!(&"Accessing Username: jwrig".to_string(),ret_ite.next().unwrap()); + assert_eq!(&"Accessing Host Name: DESKTOP-JR78RLP".to_string(),ret_ite.next().unwrap()); + assert_eq!(Option::None,ret_ite.next()); + } else { + assert_eq!(Option::None,ret); + } + }); + }); + } + + fn get_passs_pray_hit() -> String { + return r#" + + + + 4648 + 0 + 0 + 12544 + 0 + 0x8020000000000000 + + 43097 + + + Security + DESKTOP-JR78RLP + + + + S-1-5-21-979008924-657238111-836329461-1002 + jwrig + DESKTOP-JR78RLP + 0x3069d + {00000000-0000-0000-0000-000000000000} + smisenar + DOMAIN + {00000000-0000-0000-0000-000000000000} + DESKTOP-JR78RLP + DESKTOP-JR78RLP + 0x4 + + 172.16.144.128 + 445 + + "#.to_string(); + } + + // 普通にHitするテスト + #[test] + fn test_audit_log_cleared_hit() { + let xml_str = get_audit_log_cleared_xml(); + let event: event::Evtx = quick_xml::de::from_str(&xml_str).unwrap(); + + let mut sec = security::Security::new(); + let msg = sec.audit_log_cleared(&event.system.event_id.to_string(), &event.user_data); + + assert_ne!(Option::None, msg); + let v = msg.unwrap(); + let mut ite = v.iter(); + assert_eq!(&"Audit Log Clear".to_string(), ite.next().unwrap_or(&"".to_string())); + assert_eq!(&"The Audit log was cleared".to_string(), ite.next().unwrap_or(&"".to_string())); + assert_eq!(&"Security ID: jwrig".to_string(), ite.next().unwrap_or(&"".to_string())); + assert_eq!(Option::None, ite.next()); + } + + // eventid違うのでHitしないはず + #[test] + fn test_audit_log_cleared_noteq_eventid() { + let xml_str = get_audit_log_cleared_xml().replace(r"1102", r"1103"); + let event: event::Evtx = quick_xml::de::from_str(&xml_str).unwrap(); + + let mut sec = security::Security::new(); + let msg = sec.audit_log_cleared(&event.system.event_id.to_string(), &event.user_data); + assert_eq!(Option::None, msg); + } + + fn get_audit_log_cleared_xml() -> String { + return r#" + + + + 1102 + 0 + 4 + 104 + 0 + 0x4020000000000000 + + 42803 + + + Security + DESKTOP-JR78RLP + + + + + S-1-5-21-979008924-657238111-836329461-1002 + jwrig + DESKTOP-JR78RLP + 0x30550 + + + "#.to_string(); + } +}