pub const LOG_WIPE_COMMANDS: &[&str] = &[
"wevtutil cl",
"wevtutil cl System",
"wevtutil cl Security",
"wevtutil cl Application",
"auditpol /clear",
"Clear-EventLog",
"Remove-EventLog",
"echo > /var/log",
"truncate -s 0",
"> /var/log/auth.log",
"shred ",
"srm ",
"secure-delete",
];
pub const KNOWN_ROOTKIT_NAMES: &[&str] = &[
"reptile",
"diamorphine",
"azazel",
"necurs",
"rkperfect",
"knark",
"suterusu",
"Adore",
"rkit",
"enyelkm",
"beurk",
"jynx",
"jynx2",
"vlany",
];
pub const TIMESTOMP_INDICATORS: &[&str] = &[
"timestomp",
"touch -t",
"touch -d",
"SetFileTime",
"NtSetInformationFile",
"ChangeFileTime",
];
pub fn is_log_wipe_command(cmd: &str) -> bool {
let lower = cmd.to_ascii_lowercase();
LOG_WIPE_COMMANDS
.iter()
.any(|p| lower.contains(&p.to_ascii_lowercase()))
}
pub fn is_known_rootkit(name: &str) -> bool {
let lower = name.to_ascii_lowercase();
KNOWN_ROOTKIT_NAMES
.iter()
.any(|r| r.to_ascii_lowercase() == lower)
}
pub fn is_timestomp_indicator(s: &str) -> bool {
let lower = s.to_ascii_lowercase();
TIMESTOMP_INDICATORS
.iter()
.any(|t| lower.contains(&t.to_ascii_lowercase()))
}
pub const SECURE_DELETE_TOOLS: &[&str] = &[
"sdelete",
"sdelete64",
"sdelete.exe",
"sdelete64.exe",
"eraser",
"eraser.exe",
"freeraser",
"cipher",
"dban",
"nwipe",
"bleachbit",
"bleachbit.exe",
"wipe",
"secure-delete",
"sfill",
"smem",
"sswap",
];
pub const SHADOW_COPY_DELETION_PATTERNS: &[&str] = &[
"vssadmin delete shadows",
"vssadmin resize shadowstorage",
"wmic shadowcopy delete",
"wmic shadowcopy where",
"Get-WmiObject Win32_Shadowcopy",
"bcdedit /set recoveryenabled no",
"bcdedit /set bootstatuspolicy ignoreallfailures",
"wbadmin delete catalog",
"wbadmin delete backup",
"diskshadow /s",
];
pub fn is_secure_delete_tool(name: &str) -> bool {
let lower = name.to_ascii_lowercase();
SECURE_DELETE_TOOLS
.iter()
.any(|t| t.to_ascii_lowercase() == lower)
}
pub fn is_shadow_copy_deletion_command(cmd: &str) -> bool {
let lower = cmd.to_ascii_lowercase();
SHADOW_COPY_DELETION_PATTERNS
.iter()
.any(|p| lower.contains(&p.to_ascii_lowercase()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn log_wipe_contains_wevtutil_cl() {
assert!(LOG_WIPE_COMMANDS.contains(&"wevtutil cl"));
}
#[test]
fn log_wipe_contains_clear_eventlog() {
assert!(LOG_WIPE_COMMANDS.contains(&"Clear-EventLog"));
}
#[test]
fn log_wipe_contains_shred() {
assert!(LOG_WIPE_COMMANDS.contains(&"shred "));
}
#[test]
fn rootkit_names_contains_reptile() {
assert!(KNOWN_ROOTKIT_NAMES.contains(&"reptile"));
}
#[test]
fn rootkit_names_contains_diamorphine() {
assert!(KNOWN_ROOTKIT_NAMES.contains(&"diamorphine"));
}
#[test]
fn timestomp_indicators_contains_timestomp() {
assert!(TIMESTOMP_INDICATORS.contains(&"timestomp"));
}
#[test]
fn timestomp_indicators_contains_touch_t() {
assert!(TIMESTOMP_INDICATORS.contains(&"touch -t"));
}
#[test]
fn detects_wevtutil_cl_system() {
assert!(is_log_wipe_command("wevtutil cl System"));
}
#[test]
fn detects_clear_eventlog_cmdlet() {
assert!(is_log_wipe_command("Clear-EventLog -LogName Application"));
}
#[test]
fn detects_truncate_s_zero() {
assert!(is_log_wipe_command("truncate -s 0 /var/log/auth.log"));
}
#[test]
fn detects_shred_log() {
assert!(is_log_wipe_command("shred /var/log/syslog"));
}
#[test]
fn detects_case_insensitive_clear_eventlog() {
assert!(is_log_wipe_command("CLEAR-EVENTLOG -logname security"));
}
#[test]
fn does_not_flag_benign_command() {
assert!(!is_log_wipe_command(
"Get-EventLog -LogName Application -Newest 10"
));
}
#[test]
fn empty_string_not_log_wipe() {
assert!(!is_log_wipe_command(""));
}
#[test]
fn detects_reptile_exact() {
assert!(is_known_rootkit("reptile"));
}
#[test]
fn detects_diamorphine_uppercase() {
assert!(is_known_rootkit("DIAMORPHINE"));
}
#[test]
fn detects_azazel() {
assert!(is_known_rootkit("azazel"));
}
#[test]
fn does_not_flag_legitimate_module() {
assert!(!is_known_rootkit("ext4"));
}
#[test]
fn empty_string_not_rootkit() {
assert!(!is_known_rootkit(""));
}
#[test]
fn detects_timestomp_tool() {
assert!(is_timestomp_indicator(
"timestomp C:\\secret.doc -z \"01/01/2000 00:00:00\""
));
}
#[test]
fn detects_touch_t() {
assert!(is_timestomp_indicator("touch -t 200001010000 /etc/passwd"));
}
#[test]
fn detects_setfiletime_api() {
assert!(is_timestomp_indicator("SetFileTime(hFile, &ct, &at, &wt)"));
}
#[test]
fn does_not_flag_regular_touch() {
assert!(!is_timestomp_indicator("touch newfile.txt"));
}
#[test]
fn empty_string_not_timestomp() {
assert!(!is_timestomp_indicator(""));
}
#[test]
fn secure_delete_tools_contains_sdelete() {
assert!(SECURE_DELETE_TOOLS.contains(&"sdelete"));
}
#[test]
fn secure_delete_tools_contains_bleachbit() {
assert!(SECURE_DELETE_TOOLS.contains(&"bleachbit"));
}
#[test]
fn detects_sdelete_exact() {
assert!(is_secure_delete_tool("sdelete"));
}
#[test]
fn detects_sdelete64_exe() {
assert!(is_secure_delete_tool("sdelete64.exe"));
}
#[test]
fn detects_bleachbit_case_insensitive() {
assert!(is_secure_delete_tool("BLEACHBIT.EXE"));
}
#[test]
fn detects_eraser() {
assert!(is_secure_delete_tool("eraser"));
}
#[test]
fn does_not_flag_del_as_secure_delete() {
assert!(!is_secure_delete_tool("del"));
}
#[test]
fn empty_string_not_secure_delete() {
assert!(!is_secure_delete_tool(""));
}
#[test]
fn shadow_patterns_contains_vssadmin() {
assert!(SHADOW_COPY_DELETION_PATTERNS.contains(&"vssadmin delete shadows"));
}
#[test]
fn shadow_patterns_contains_wmic_shadowcopy() {
assert!(SHADOW_COPY_DELETION_PATTERNS.contains(&"wmic shadowcopy delete"));
}
#[test]
fn shadow_patterns_contains_bcdedit() {
assert!(SHADOW_COPY_DELETION_PATTERNS.contains(&"bcdedit /set recoveryenabled no"));
}
#[test]
fn detects_vssadmin_delete() {
assert!(is_shadow_copy_deletion_command(
"vssadmin delete shadows /all /quiet"
));
}
#[test]
fn detects_wmic_shadowcopy_delete() {
assert!(is_shadow_copy_deletion_command(
"wmic shadowcopy delete /nointeractive"
));
}
#[test]
fn detects_bcdedit_recovery_shadow() {
assert!(is_shadow_copy_deletion_command(
"bcdedit /set recoveryenabled no"
));
}
#[test]
fn detects_wbadmin_delete_catalog() {
assert!(is_shadow_copy_deletion_command(
"wbadmin delete catalog -quiet"
));
}
#[test]
fn does_not_flag_vssadmin_list() {
assert!(!is_shadow_copy_deletion_command("vssadmin list shadows"));
}
#[test]
fn empty_string_not_shadow_deletion() {
assert!(!is_shadow_copy_deletion_command(""));
}
}