From 12110a468754c401f801fffff4c6e8569528bfb2 Mon Sep 17 00:00:00 2001 From: itiB Date: Thu, 3 Dec 2020 23:26:17 +0900 Subject: [PATCH 1/4] Update: release csv-timeline function --- src/afterfact.rs | 86 ++++++++++++++++++++----------------- src/detections/detection.rs | 7 +-- 2 files changed, 49 insertions(+), 44 deletions(-) diff --git a/src/afterfact.rs b/src/afterfact.rs index 002d3d1a..362c7a81 100644 --- a/src/afterfact.rs +++ b/src/afterfact.rs @@ -1,8 +1,10 @@ use crate::detections::configs; use crate::detections::print; -use chrono::{DateTime, Utc}; +use chrono::{DateTime, TimeZone, Utc}; use serde::Serialize; use std::error::Error; +use std::fs::File; +use std::io; use std::process; #[derive(Debug, Serialize)] @@ -13,16 +15,27 @@ pub struct CsvFormat<'a> { } pub fn after_fact() { - if let Some(csv_path) = configs::singleton().args.value_of("csv-timeline") { - if let Err(err) = emit_csv(csv_path) { - println!("{}", err); - process::exit(1); - } + let mut target: Box = + if let Some(csv_path) = configs::singleton().args.value_of("csv-timeline") { + match File::create(csv_path) { + Ok(file) => Box::new(file), + Err(err) => { + println!("Failed to open file. {}", err); + process::exit(1); + } + } + } else { + Box::new(io::stdout()) + }; + + if let Err(err) = emit_csv(&mut target) { + println!("Failed to write CSV. {}", err); + process::exit(1); } } -fn emit_csv(path: &str) -> Result<(), Box> { - let mut wtr = csv::Writer::from_path(path)?; +fn emit_csv(writer: &mut Box) -> Result<(), Box> { + let mut wtr = csv::WriterBuilder::new().from_writer(writer); let messages = print::MESSAGES.lock().unwrap(); for (time, texts) in messages.iter() { @@ -37,50 +50,45 @@ fn emit_csv(path: &str) -> Result<(), Box> { Ok(()) } -#[cfg(test)] -mod tests { - - use crate::afterfact::emit_csv; - use crate::detections::print; +#[test] +fn test_emit_csv() { use serde_json::Value; use std::fs::{read_to_string, remove_file}; + { + let mut messages = print::MESSAGES.lock().unwrap(); - #[test] - fn test_emit_csv() { + let val = r##" { - let mut messages = print::MESSAGES.lock().unwrap(); - let json_str = r##" - { - "Event": { - "EventData": { - "CommandLine": "hoge" - }, - "System": { - "TimeCreated": { - "#attributes":{ - "SystemTime": "1996-02-27T01:05:01Z" - } + "Event": { + "EventData": { + "CommandLine": "hoge" + }, + "System": { + "TimeCreated": { + "#attributes":{ + "SystemTime": "1996-02-27T01:05:01Z" } } } } - "##; - let event_record: Value = serde_json::from_str(json_str).unwrap(); - - messages.insert(&event_record, "pokepoke".to_string()); } + "##; + let event: Value = serde_json::from_str(val).unwrap(); + messages.insert(&event, "pokepoke".to_string()); + } - let expect = "Time,Message + let expect = "Time,Message 1996-02-27T01:05:01Z,pokepoke "; - assert!(emit_csv(&"./test_emit_csv.csv".to_string()).is_ok()); + let mut file: Box = + Box::new(File::create("./test_emit_csv.csv".to_string()).unwrap()); + assert!(emit_csv(&mut file).is_ok()); - match read_to_string("./test_emit_csv.csv") { - Err(_) => panic!("Failed to open file"), - Ok(s) => assert_eq!(s, expect), - }; + match read_to_string("./test_emit_csv.csv") { + Err(_) => panic!("Failed to open file"), + Ok(s) => assert_eq!(s, expect), + }; - assert!(remove_file("./test_emit_csv.csv").is_ok()); - } + assert!(remove_file("./test_emit_csv.csv").is_ok()); } diff --git a/src/detections/detection.rs b/src/detections/detection.rs index 20977de4..e5d215ad 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -1,7 +1,7 @@ extern crate chrono; extern crate csv; -use crate::detections::print::Message; +use crate::detections::print::MESSAGES; use crate::detections::rule; use crate::detections::rule::RuleNode; use crate::yaml::ParseYaml; @@ -81,7 +81,7 @@ impl Detection { .collect(); // selection rule files and collect message - let mut message = Message::new(); + let mut message = MESSAGES.lock().unwrap(); selection_rules.iter_mut().for_each(|rule| { event_records.iter().for_each(|event_record| { if !rule.select(event_record) { @@ -94,8 +94,5 @@ impl Detection { ) }); }); - - // output message - message.print(); } } From 7d43885d0574bc9f6f487eea4b3dfb1f8f65e0e6 Mon Sep 17 00:00:00 2001 From: itiB Date: Sat, 5 Dec 2020 02:18:18 +0900 Subject: [PATCH 2/4] feat: emit DateTime with selected timezone @fox --- src/afterfact.rs | 28 ++++++++++++++++++++++------ src/detections/configs.rs | 4 ++-- src/detections/detection.rs | 1 - src/main.rs | 2 -- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/afterfact.rs b/src/afterfact.rs index 362c7a81..fd30f2e5 100644 --- a/src/afterfact.rs +++ b/src/afterfact.rs @@ -1,6 +1,6 @@ use crate::detections::configs; use crate::detections::print; -use chrono::{DateTime, TimeZone, Utc}; +use chrono::Local; use serde::Serialize; use std::error::Error; use std::fs::File; @@ -10,7 +10,7 @@ use std::process; #[derive(Debug, Serialize)] #[serde(rename_all = "PascalCase")] pub struct CsvFormat<'a> { - time: DateTime, + time: &'a str, message: &'a str, } @@ -39,9 +39,24 @@ fn emit_csv(writer: &mut Box) -> Result<(), Box> { let messages = print::MESSAGES.lock().unwrap(); for (time, texts) in messages.iter() { + let formated_time = if configs::singleton().args.is_present("utc") { + if configs::singleton().args.is_present("rfc-2822") { + time.to_rfc2822() + } else { + time.to_rfc3339() + } + } else { + let time_local = time.with_timezone(&Local); + if configs::singleton().args.is_present("rfc-2822") { + time_local.to_rfc2822() + } else { + time_local.to_rfc3339() + } + }; + for text in texts { wtr.serialize(CsvFormat { - time: *time, + time: &formated_time, message: text, })?; } @@ -78,8 +93,7 @@ fn test_emit_csv() { } let expect = "Time,Message -1996-02-27T01:05:01Z,pokepoke -"; +1996-02-2"; let mut file: Box = Box::new(File::create("./test_emit_csv.csv".to_string()).unwrap()); @@ -87,7 +101,9 @@ fn test_emit_csv() { match read_to_string("./test_emit_csv.csv") { Err(_) => panic!("Failed to open file"), - Ok(s) => assert_eq!(s, expect), + Ok(s) => { + assert_eq!(&s[0..22], expect); + } }; assert!(remove_file("./test_emit_csv.csv").is_ok()); diff --git a/src/detections/configs.rs b/src/detections/configs.rs index d4879f12..ef7f1d70 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -45,11 +45,11 @@ fn build_app() -> clap::App<'static, 'static> { .arg(Arg::from_usage("--attackhunt=[ATTACK_HUNT] 'Attack Hunt'")) .arg(Arg::from_usage("--csv-timeline=[CSV_TIMELINE] 'csv output timeline'")) .arg(Arg::from_usage("--human-readable-timeline=[HUMAN_READABLE_TIMELINE] 'human readable timeline'")) + .arg(Arg::from_usage("--rfc-2822 'output date and time in RFC 2822 format. Example: Mon, 07 Aug 2006 12:34:56 -0600'")) .arg(Arg::from_usage("-l --lang=[LANG] 'output language'")) - .arg(Arg::from_usage("-t --timezone=[TIMEZONE] 'timezone setting'")) + .arg(Arg::from_usage("-u --utc 'output time in UTC format(default: local time)'")) .arg(Arg::from_usage("-d --directory 'event log files directory'")) .arg(Arg::from_usage("-s --statistics 'event statistics'")) - .arg(Arg::from_usage("-u --update 'signature update'")) .arg(Arg::from_usage("--credits 'Zachary Mathis, Akira Nishikawa'")) } diff --git a/src/detections/detection.rs b/src/detections/detection.rs index e5d215ad..b24c3ccf 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -6,7 +6,6 @@ use crate::detections::rule; use crate::detections::rule::RuleNode; use crate::yaml::ParseYaml; -use chrono::{DateTime, FixedOffset, TimeZone, Utc}; use evtx::EvtxParser; use serde_json::{Error, Value}; diff --git a/src/main.rs b/src/main.rs index 0a66e2e7..82e5cb61 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,4 @@ extern crate serde; -#[macro_use] -extern crate serde_derive; use evtx::EvtxParser; use quick_xml::de::DeError; From f1844882e6f15ce8176dabd0d740c767941f9064 Mon Sep 17 00:00:00 2001 From: itiB Date: Thu, 10 Dec 2020 01:45:36 +0900 Subject: [PATCH 3/4] Refact: after_fact.rs emit time format --- src/afterfact.rs | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/afterfact.rs b/src/afterfact.rs index fd30f2e5..43b03c8a 100644 --- a/src/afterfact.rs +++ b/src/afterfact.rs @@ -1,6 +1,6 @@ use crate::detections::configs; use crate::detections::print; -use chrono::Local; +use chrono::{DateTime, Local, TimeZone, Utc}; use serde::Serialize; use std::error::Error; use std::fs::File; @@ -39,24 +39,9 @@ fn emit_csv(writer: &mut Box) -> Result<(), Box> { let messages = print::MESSAGES.lock().unwrap(); for (time, texts) in messages.iter() { - let formated_time = if configs::singleton().args.is_present("utc") { - if configs::singleton().args.is_present("rfc-2822") { - time.to_rfc2822() - } else { - time.to_rfc3339() - } - } else { - let time_local = time.with_timezone(&Local); - if configs::singleton().args.is_present("rfc-2822") { - time_local.to_rfc2822() - } else { - time_local.to_rfc3339() - } - }; - for text in texts { wtr.serialize(CsvFormat { - time: &formated_time, + time: &format_time(time), message: text, })?; } @@ -65,6 +50,25 @@ fn emit_csv(writer: &mut Box) -> Result<(), Box> { Ok(()) } +fn format_time(time: &DateTime) -> String { + if configs::singleton().args.is_present("utc") { + format_rfc(time) + } else { + format_rfc(&time.with_timezone(&Local)) + } +} + +fn format_rfc(time: &DateTime) -> String +where + Tz::Offset: std::fmt::Display, +{ + if configs::singleton().args.is_present("rfc-2822") { + return time.to_rfc2822(); + } else { + return time.to_rfc3339(); + } +} + #[test] fn test_emit_csv() { use serde_json::Value; From a87058743e5985fa4199148fe5fb743de210f585 Mon Sep 17 00:00:00 2001 From: akiranishikawa Date: Sun, 13 Dec 2020 17:28:00 +0900 Subject: [PATCH 4/4] cargo fmt --- src/detections/detection.rs | 2 +- src/main.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/detections/detection.rs b/src/detections/detection.rs index 6aca8ac5..9e423603 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -1,13 +1,13 @@ extern crate csv; use crate::detections::print::MESSAGES; -use std::path::PathBuf; use crate::detections::rule; use crate::detections::rule::RuleNode; use crate::yaml::ParseYaml; use evtx::err; use evtx::{EvtxParser, SerializedEvtxRecord}; use serde_json::{Error, Value}; +use std::path::PathBuf; const DIRPATH_RULES: &str = "rules"; diff --git a/src/main.rs b/src/main.rs index 1e701e6c..a5812535 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,6 @@ fn main() { } else if configs::singleton().args.is_present("credits") { print_credits(); } - } fn collect_evtxfiles(dirpath: &str) -> Vec { @@ -60,7 +59,7 @@ fn print_credits() { fn detect_files(evtx_files: Vec) { let mut detection = detection::Detection::new(); &detection.start(evtx_files); - + after_fact(); }