diff --git a/Cargo.lock b/Cargo.lock index ba27fb23..6b63ca2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,6 +70,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + [[package]] name = "bitflags" version = "1.2.1" @@ -184,6 +190,15 @@ dependencies = [ "build_const", ] +[[package]] +name = "crc32fast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-channel" version = "0.4.4" @@ -389,6 +404,18 @@ dependencies = [ "winstructs", ] +[[package]] +name = "flate2" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da80be589a72651dcda34d8b35bcdc9b7254ad06325611074d9cc0fbb19f60ee" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide", +] + [[package]] name = "fs_extra" version = "1.2.0" @@ -1150,9 +1177,11 @@ dependencies = [ name = "yamato_event_analyzer" version = "0.1.0" dependencies = [ + "base64", "clap", "csv", "evtx", + "flate2", "quick-xml 0.17.2", "regex", "serde", diff --git a/Cargo.toml b/Cargo.toml index 406ca26c..863e7911 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,5 @@ serde_json = { version = "1.0"} clap = "*" regex = "1" csv = "1.1" +base64 = "*" +flate2 = "1.0" \ No newline at end of file diff --git a/src/detections/utils.rs b/src/detections/utils.rs index 92646e18..42090dda 100644 --- a/src/detections/utils.rs +++ b/src/detections/utils.rs @@ -1,12 +1,95 @@ +extern crate base64; extern crate csv; extern crate regex; +use flate2::read::GzDecoder; use regex::Regex; use std::env; use std::fs::File; use std::io::prelude::*; +use std::io::prelude::*; +use std::str; use std::string::String; +pub fn check_command( + event_id: usize, + commandline: &str, + minlength: usize, + servicecmd: usize, + servicename: &str, + creator: &str, +) { + let mut text = "".to_string(); + let mut base64 = "".to_string(); + + let mut f = File::open("whitelist.txt").expect("file not found"); + let mut contents = String::new(); + f.read_to_string(&mut contents); + + let mut rdr = csv::Reader::from_reader(contents.as_bytes()); + + for entry in rdr.records() { + if let Ok(_data) = entry { + if (commandline == &_data[0]) { + return; + } + } + } + if commandline.len() > minlength { + text.push_str("Long Command Line: greater than "); + text.push_str(&minlength.to_string()); + text.push_str("bytes\n"); + } + text.push_str(&check_obfu(commandline)); + text.push_str(&check_regex(commandline, 0)); + text.push_str(&check_creator(commandline, creator)); + if (Regex::new(r"\-enc.*[A-Za-z0-9/+=]{100}") + .unwrap() + .is_match(commandline)) + { + let re = Regex::new(r"^.* \-Enc(odedCommand)? ").unwrap(); + base64.push_str(&re.replace_all(commandline, "")); + } else if (Regex::new(r":FromBase64String\(") + .unwrap() + .is_match(commandline)) + { + let re = Regex::new(r"^^.*:FromBase64String\(\'*").unwrap(); + base64.push_str(&re.replace_all(commandline, "")); + let re = Regex::new(r"\'.*$").unwrap(); + base64.push_str(&re.replace_all(&base64.to_string(), "")); + } + if (!base64.is_empty()) { + if (Regex::new(r"Compression.GzipStream.*Decompress") + .unwrap() + .is_match(commandline)) + { + let decoded = base64::decode(base64).unwrap(); + let mut d = GzDecoder::new(decoded.as_slice()); + let mut uncompressed = String::new(); + d.read_to_string(&mut uncompressed).unwrap(); + println!("Decoded : {}", uncompressed); + text.push_str("Base64-encoded and compressed function\n"); + } else { + let decoded = base64::decode(base64).unwrap(); + println!("Decoded : {}", str::from_utf8(decoded.as_slice()).unwrap()); + text.push_str("Base64-encoded function\n"); + text.push_str(&check_obfu(str::from_utf8(decoded.as_slice()).unwrap())); + text.push_str(&check_regex(str::from_utf8(decoded.as_slice()).unwrap(), 0)); + } + } + if !text.is_empty() { + if servicecmd != 0 { + println!("Message : Suspicious Service Command"); + println!("Results : Service name: {}\n", servicename); + } else { + println!("Message : Suspicious Command Line"); + } + println!("command : {}", commandline); + println!("result : {}", text); + println!("EventID : {}", event_id); + } +} + fn check_obfu(string: &str) -> std::string::String { let mut obfutext = "".to_string(); let mut lowercasestring = string.to_lowercase(); @@ -24,7 +107,7 @@ fn check_obfu(string: &str) -> std::string::String { if (length > 0) { let mut percent = ((length - noalphastring.len()) / length); - if ((length / 100) as f64)< minpercent { + if ((length / 100) as f64) < minpercent { minpercent = (length / 100) as f64; } if percent < minpercent as usize { @@ -32,10 +115,10 @@ fn check_obfu(string: &str) -> std::string::String { let percent = &percent.to_string(); let caps = re.captures(percent).unwrap(); obfutext.push_str("Possible command obfuscation: only "); - obfutext.push_str(caps.get(0).unwrap().as_str()); + obfutext.push_str(caps.get(0).unwrap().as_str()); obfutext.push_str("alphanumeric and common symbols\n"); } - percent = ((nobinarystring.len() - length / length) /length); + percent = ((nobinarystring.len() - length / length) / length); let mut binarypercent = 1 - percent; if binarypercent > maxbinary as usize { re = Regex::new(r"{0:P0}").unwrap(); @@ -49,7 +132,7 @@ fn check_obfu(string: &str) -> std::string::String { return obfutext; } -fn check_regex(string: &str, r#type: &str) -> std::string::String { +fn check_regex(string: &str, r#type: usize) -> std::string::String { let mut f = File::open("regexes.txt").expect("file not found"); let mut contents = String::new(); f.read_to_string(&mut contents); @@ -58,19 +141,16 @@ fn check_regex(string: &str, r#type: &str) -> std::string::String { let mut regextext = "".to_string(); for regex in rdr.records() { - match regex { + if let Ok(_data) = regex { /* data[0] is type. data[1] is regex. data[2] is string. */ - Ok(_data) => { - if &_data[0] == r#type && &_data[1] == string { - regextext.push_str(&_data[2]); - regextext.push_str("\n"); - } + if &_data[0] == r#type.to_string() && &_data[1] == string { + regextext.push_str(&_data[2]); + regextext.push_str("\n"); } - Err(_data) => (), } } return regextext; diff --git a/whitelist.txt b/whitelist.txt new file mode 100644 index 00000000..a6165f49 --- /dev/null +++ b/whitelist.txt @@ -0,0 +1,3 @@ +regex +^"C:\\Program Files\\Google\\Chrome\\Application\\chrome\.exe" +^"C:\\Program Files\\Google\\Update\\GoogleUpdate\.exe"