diff --git a/config/eventkey_alias.txt b/config/eventkey_alias.txt index be9beeac..12fb0ef3 100644 --- a/config/eventkey_alias.txt +++ b/config/eventkey_alias.txt @@ -2,6 +2,7 @@ alias,event_key EventID,Event.System.EventID Channel,Event.System.Channel CommandLine,Event.EventData.CommandLine +ParentProcessName,Event.EventData.ParentProcessName Signed,Event.EventData.Signed ProcessName,Event.EventData.ProcessName AccessMask,Event.EventData.AccessMask @@ -12,4 +13,7 @@ ServiceName,Event.EventData.ServiceName ImagePath,Event.EventData.ImagePath ContextInfo,Event.EventData.ContextInfo Path,Event.EventData.Path -ScriptBlockText,Event.EventData.ScriptBlockText#Name \ No newline at end of file +ScriptBlockText,Event.EventData.ScriptBlockText#Name +MemberName,Event.EventData.SubjectUserName +MemberSid,Event.EventData.SubjectUserSid +TargetSid,Event.EventData.TargetSid diff --git a/src/detections/common.rs b/src/detections/common.rs deleted file mode 100644 index bd8782ef..00000000 --- a/src/detections/common.rs +++ /dev/null @@ -1,44 +0,0 @@ -use crate::models::event; -use std::collections::HashMap; - -#[derive(Debug)] -pub struct Common { - record_id: u64, - date: String, - record_id_list: HashMap, -} - -impl Common { - pub fn new() -> Common { - Common { - record_id: 0, - date: "".to_string(), - record_id_list: HashMap::new(), - } - } - - pub fn disp(&self) { - for (record_id, date) in self.record_id_list.iter() { - println!("date:{:?} record-id: {:?}", date, record_id); - } - } - - pub fn detection(&mut self, system: &event::System, event_data: &HashMap) { - self.check_record_id(system); - } - - // - // Record IDがシーケンスになっているかチェック - // - fn check_record_id(&mut self, system: &event::System) { - let event_record_id: u64 = system.event_record_id.parse().unwrap(); - if self.record_id > 0 && event_record_id - self.record_id > 1 { - self.record_id_list.insert( - self.record_id.to_string() + " - " + &system.event_record_id.to_string(), - self.date.to_string() + " - " + &system.time_created.system_time.to_string(), - ); - } - self.record_id = event_record_id; - self.date = system.time_created.system_time.to_string(); - } -} diff --git a/src/detections/configs.rs b/src/detections/configs.rs index 4404b147..d4879f12 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -53,7 +53,7 @@ fn build_app() -> clap::App<'static, 'static> { .arg(Arg::from_usage("--credits 'Zachary Mathis, Akira Nishikawa'")) } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct EventKeyAliasConfig { key_to_eventkey: HashMap, } diff --git a/src/detections/detection.rs b/src/detections/detection.rs index 2546b2bf..a62cb6dd 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -83,29 +83,28 @@ impl Detection { // selection rule files and collect message let mut message = Message::new(); selection_rules.iter_mut().for_each(|rule| { - &event_records - .iter() - .filter(|event_record| rule.select(event_record)) - .for_each(|event_record| { - let event_time = Detection::get_event_time(event_record); - // TODO ログから日付がとれない場合に本当は時刻不明という感じで表示したい。 - // しかし、Messageクラスのinsertメソッドが、UTCクラスのインスタンスを必ず渡すようなインタフェースになっているので、 - // やむなくUtc.ymd(1970, 1, 1).and_hms(0, 0, 0)を渡している。 + event_records.iter().for_each(|event_record| { + if !rule.select(event_record) { + return; + } - // Messageクラスのinsertメソッドの引数をDateTimeからOption>に変更して、 - // insertメソッドでOption::Noneが渡された場合に時刻不明だと分かるように表示させるような実装にした方がいいかも - let utc_event_time = event_time - .and_then(|datetime| { - let utc = Utc.from_local_datetime(&datetime.naive_utc()).unwrap(); - return Option::Some(utc); - }) - .or(Option::Some(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0))); - message.insert(utc_event_time.unwrap(), event_record.to_string()) - }); + let event_time = Detection::get_event_time(event_record); + let utc_event_time = event_time + .and_then(|datetime| { + let utc = Utc.from_local_datetime(&datetime.naive_utc()).unwrap(); + return Option::Some(utc); + }) + .or(Option::None); + message.insert( + utc_event_time, + event_record, + Some(rule.yaml["output"].as_str().unwrap().to_string()), + ) + }); }); // output message - message.debug(); + message.print(); } fn get_event_time(event_record: &Value) -> Option> { diff --git a/src/detections/mod.rs b/src/detections/mod.rs index 2c864e71..48034d62 100644 --- a/src/detections/mod.rs +++ b/src/detections/mod.rs @@ -1,4 +1,3 @@ -mod common; pub mod configs; pub mod detection; pub mod print; diff --git a/src/detections/print.rs b/src/detections/print.rs index 793737e4..1ae3c316 100644 --- a/src/detections/print.rs +++ b/src/detections/print.rs @@ -1,8 +1,12 @@ extern crate chrono; extern crate lazy_static; +use crate::detections::configs; use chrono::{DateTime, TimeZone, Utc}; use lazy_static::lazy_static; +use regex::Regex; +use serde_json::Value; use std::collections::BTreeMap; +use std::collections::HashMap; use std::sync::Mutex; #[derive(Debug)] @@ -21,18 +25,75 @@ impl Message { } /// メッセージを設定 - pub fn insert(&mut self, time: DateTime, message: String) { - match self.map.get_mut(&time) { + pub fn insert( + &mut self, + mut time: Option>, + event_record: &Value, + output: Option, + ) { + if Option::None == output { + return; + } + + let message = &self.parse_message(event_record, output); + + if Option::None == time { + time = Option::Some(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0)); + } + + match self.map.get_mut(&time.unwrap()) { Some(v) => { v.push(message.to_string()); } None => { let m = vec![message.to_string(); 1]; - self.map.insert(time, m); + self.map.insert(time.unwrap(), m); } } } + fn parse_message(&mut self, event_record: &Value, output: Option) -> String { + if Option::None == output { + return "".to_string(); + } + + let mut return_message: String = output.unwrap(); + let mut hash_map: HashMap = HashMap::new(); + let re = Regex::new(r"%[a-zA-Z0-9-_]+%").unwrap(); + for caps in re.captures_iter(&return_message) { + let full_target_str = &caps[0]; + let target_length = full_target_str.chars().count() - 2; // The meaning of 2 is two percent + let target_str = full_target_str + .chars() + .skip(1) + .take(target_length) + .collect::(); + + if let Some(array_str) = configs::singleton() + .event_key_alias_config + .get_event_key(target_str.to_string()) + { + let split: Vec<&str> = array_str.split(".").collect(); + let mut tmp_event_record: &Value = event_record.into(); + for s in split { + if let Some(record) = tmp_event_record.get(s) { + tmp_event_record = record; + } + } + hash_map.insert( + full_target_str.to_string(), + tmp_event_record.as_str().unwrap_or("").to_string(), + ); + } + } + + for (k, v) in &hash_map { + return_message = return_message.replace(k, v); + } + + return_message + } + /// メッセージを返す pub fn get(&self, time: DateTime) -> Vec { match self.map.get(&time) { @@ -45,6 +106,15 @@ impl Message { pub fn debug(&self) { println!("{:?}", self.map); } + + /// 最後に表示を行う + pub fn print(&self) { + for (key, values) in self.map.iter() { + for value in values.iter() { + println!("{} : {}", key, value); + } + } + } } #[test] @@ -53,11 +123,39 @@ fn test_create_and_append_message() { let poke = Utc.ymd(1996, 2, 27).and_hms(1, 5, 1); let taka = Utc.ymd(2000, 1, 21).and_hms(9, 6, 1); - message.insert(poke, "TEST".to_string()); - message.insert(poke, "TEST2".to_string()); - message.insert(taka, "TEST3".to_string()); + let json_str = r#" + { + "Event": { + "EventData": { + "CommandLine": "hoge" + } + } + } + "#; + let event_record: Value = serde_json::from_str(json_str).unwrap(); + + message.insert( + Some(poke), + &event_record, + Some("CommandLine1: %CommandLine%".to_string()), + ); + message.insert( + Some(poke), + &event_record, + Some("CommandLine2: %CommandLine%".to_string()), + ); + message.insert( + Some(taka), + &event_record, + Some("CommandLine3: %CommandLine%".to_string()), + ); + message.insert( + Option::None, + &event_record, + Some("CommandLine4: %CommandLine%".to_string()), + ); let display = format!("{}", format_args!("{:?}", message)); - let expect = "Message { map: {1996-02-27T01:05:01Z: [\"TEST\", \"TEST2\"], 2000-01-21T09:06:01Z: [\"TEST3\"]} }"; + let expect = "Message { map: {1970-01-01T00:00:00Z: [\"CommandLine4: hoge\"], 1996-02-27T01:05:01Z: [\"CommandLine1: hoge\", \"CommandLine2: hoge\"], 2000-01-21T09:06:01Z: [\"CommandLine3: hoge\"]} }"; assert_eq!(display, expect); } diff --git a/src/lib.rs b/src/lib.rs index d9abfe41..450cf595 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ pub mod detections; -pub mod models; pub mod omikuji; pub mod yaml; diff --git a/src/main.rs b/src/main.rs index c2e44c76..8b4d3264 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,12 +8,7 @@ use yamato_event_analyzer::detections::detection; use yamato_event_analyzer::omikuji::Omikuji; fn main() -> Result<(), DeError> { - let filepath: String = configs::singleton() - .args - .value_of("filepath") - .unwrap_or("") - .to_string(); - if filepath != "" { + if let Some(filepath) = configs::singleton().args.value_of("filepath") { parse_file(&filepath); } diff --git a/src/models/event.rs b/src/models/event.rs deleted file mode 100644 index aaea9312..00000000 --- a/src/models/event.rs +++ /dev/null @@ -1,133 +0,0 @@ -extern crate serde; -use serde::Deserialize; -use std::collections::HashMap; - -#[derive(Debug, Deserialize, PartialEq)] -pub struct Data { - #[serde(rename = "Name")] - pub name: Option, - #[serde(rename = "$value")] - pub text: Option, -} - -#[derive(Debug, Deserialize, PartialEq)] -pub struct TimeCreated { - #[serde(rename = "SystemTime")] - pub system_time: String, -} - -#[derive(Debug, Deserialize, PartialEq)] -struct Execution { - #[serde(rename = "ProcessID")] - process_id: i32, - #[serde(rename = "ThreadID")] - thread_id: i32, -} - -#[derive(Debug, Deserialize, PartialEq)] -pub struct Provider { - #[serde(rename = "Name")] - pub name: Option, - #[serde(rename = "Guid")] - guid: Option, -} - -#[derive(Debug, Deserialize, PartialEq)] -pub struct System { - #[serde(rename = "Provider")] - pub provider: Provider, - #[serde(rename = "EventID")] - pub event_id: String, - #[serde(rename = "Version")] - version: Option, - #[serde(rename = "Level")] - level: String, - #[serde(rename = "Task")] - task: String, - #[serde(rename = "Opcode")] - opcode: Option, - #[serde(rename = "Keywords")] - keywords: String, - #[serde(rename = "TimeCreated")] - pub time_created: TimeCreated, - #[serde(rename = "EventRecordID")] - pub event_record_id: String, - #[serde(rename = "Correlation")] - correlation: Option, - #[serde(rename = "Execution")] - execution: Option, - #[serde(rename = "Channel")] - pub channel: String, // Security, System, Application ...etc - #[serde(rename = "Computer")] - computer: String, - #[serde(rename = "Security")] - security: String, - #[serde(rename = "Message")] - pub message: Option, -} - -#[derive(Debug, Deserialize, PartialEq)] -pub struct EventData { - #[serde(rename = "Data")] - pub data: Option>, -} - -#[derive(Debug, Deserialize, PartialEq)] -pub struct UserData { - #[serde(rename = "LogFileCleared")] - pub log_file_cleared: Option, -} - -#[derive(Debug, Deserialize, PartialEq)] -pub struct LogFileCleared { - #[serde(rename = "SubjectUserSid")] - pub subject_user_sid: Option, - #[serde(rename = "SubjectUserName")] - pub subject_user_name: Option, - #[serde(rename = "SubjectDomainName")] - pub subject_domain_name: Option, - #[serde(rename = "SubjectLogonId")] - pub subject_logon_id: Option, -} - -#[derive(Debug, Deserialize, PartialEq)] -pub struct Evtx { - #[serde(rename = "System")] - pub system: System, - #[serde(rename = "EventData")] - pub event_data: Option, - #[serde(rename = "UserData")] - pub user_data: Option, -} - -impl Evtx { - // - // 文字列データを取得する - // - fn get_string(v: &Data) -> String { - let mut ret = "".to_string(); - if let Some(text) = &v.text { - ret = text.to_string(); - } - return ret; - } - - // - // EventDataをHashMapとして取得する - // - pub fn parse_event_data(&self) -> HashMap { - let mut values = HashMap::new(); - - if let Some(event_data) = &self.event_data { - if let Some(data) = &event_data.data { - for v in data.iter() { - if let Some(name) = &v.name { - values.insert(name.to_string(), Evtx::get_string(v)); - } - } - } - } - - values - } -} diff --git a/src/models/mod.rs b/src/models/mod.rs deleted file mode 100644 index 53f11265..00000000 --- a/src/models/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod event;