From 2c96968b8ed2da295b3d5b24badc6883967fc232 Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Thu, 7 Nov 2024 22:40:53 +0900 Subject: [PATCH 1/5] feat: add RDS log to logon-summary --- src/timeline/metrics.rs | 277 +++++++++++++++++++++++++------------- src/timeline/timelines.rs | 90 ++++++++----- 2 files changed, 236 insertions(+), 131 deletions(-) diff --git a/src/timeline/metrics.rs b/src/timeline/metrics.rs index 0e7bc4f01..cdf4d48d1 100644 --- a/src/timeline/metrics.rs +++ b/src/timeline/metrics.rs @@ -5,10 +5,24 @@ use crate::detections::{ message::AlertMessage, utils, }; +use crate::timeline::metrics::Channel::{RdsGtw, RdsLsm, Sec}; use chrono::{DateTime, NaiveDate, NaiveDateTime, Utc}; use compact_str::CompactString; use hashbrown::{HashMap, HashSet}; +#[derive(Debug, Clone, Eq, Hash, PartialEq)] +pub struct LoginEvent { + pub channel: CompactString, + pub dst_user: CompactString, + pub dst_domain: CompactString, + pub hostname: CompactString, + pub logontype: CompactString, + pub src_user: CompactString, + pub src_domain: CompactString, + pub source_computer: CompactString, + pub source_ip: CompactString, +} + #[derive(Debug, Clone)] pub struct EventMetrics { pub total: usize, @@ -16,16 +30,7 @@ pub struct EventMetrics { pub start_time: Option>, pub end_time: Option>, pub stats_list: HashMap<(CompactString, CompactString), usize>, - pub stats_login_list: HashMap< - ( - CompactString, - CompactString, - CompactString, - CompactString, - CompactString, - ), - [usize; 2], - >, + pub stats_login_list: HashMap, } /** * Windows Event Logの統計情報を出力する @@ -37,16 +42,7 @@ impl EventMetrics { start_time: Option>, end_time: Option>, stats_list: HashMap<(CompactString, CompactString), usize>, - stats_login_list: HashMap< - ( - CompactString, - CompactString, - CompactString, - CompactString, - CompactString, - ), - [usize; 2], - >, + stats_login_list: HashMap, ) -> EventMetrics { EventMetrics { total, @@ -230,91 +226,178 @@ impl EventMetrics { evtid.as_str().unwrap().parse::().unwrap_or_default() }; - if !(idnum == 4624 || idnum == 4625) - || utils::get_serde_number_to_string( - utils::get_event_value("Channel", &record.record, eventkey_alias) - .unwrap_or(&serde_json::Value::Null), - false, - ) - .unwrap_or_else(|| "n/a".into()) - .replace(['"', '\''], "") - != "Security" - { - continue; - } - - let username = CompactString::from( - utils::get_serde_number_to_string( - utils::get_event_value("TargetUserName", &record.record, eventkey_alias) - .unwrap_or(&serde_json::Value::Null), - false, - ) - .unwrap_or_else(|| "n/a".into()) - .replace(['"', '\''], ""), - ); - let logontype = utils::get_serde_number_to_string( - utils::get_event_value("LogonType", &record.record, eventkey_alias) - .unwrap_or(&serde_json::Value::Null), - false, - ) - .unwrap_or_else(|| "n/a".into()) - .replace(['"', '\''], ""); - let hostname = CompactString::from( - utils::get_serde_number_to_string( - utils::get_event_value("Computer", &record.record, eventkey_alias) - .unwrap_or(&serde_json::Value::Null), - false, - ) - .unwrap_or_else(|| "n/a".into()) - .replace(['"', '\''], ""), - ); - - let source_computer = CompactString::from( - utils::get_serde_number_to_string( - utils::get_event_value("WorkstationName", &record.record, eventkey_alias) - .unwrap_or(&serde_json::Value::Null), - false, - ) - .unwrap_or_else(|| "n/a".into()) - .replace(['"', '\''], ""), - ); - - let source_ip = CompactString::from( - utils::get_serde_number_to_string( - utils::get_event_value("IpAddress", &record.record, eventkey_alias) - .unwrap_or(&serde_json::Value::Null), - false, - ) - .unwrap_or_else(|| "n/a".into()) - .replace(['"', '\''], ""), - ); + let channel = get_event_value_as_string("Channel", &record.record, eventkey_alias); + if let Some(channel) = is_target_event(idnum, &channel) { + let channel_name = match channel { + Sec => { + if idnum == 4624 { + CompactString::from("Sec 4624") + } else { + CompactString::from("Sec 4625") + } + } + RdsLsm => CompactString::from("RDS-LSM 21"), + RdsGtw => CompactString::from("RDS-GTW 302"), + }; + let dst_user = match channel { + Sec => get_event_value_as_string( + "TargetUserName", + &record.record, + eventkey_alias, + ), + RdsLsm => { + let user_with_domain = get_event_value_as_string( + "UserDataUser", + &record.record, + eventkey_alias, + ); + let user = user_with_domain + .rsplit('\\') + .next() + .unwrap_or(&user_with_domain); + CompactString::from(user) + } + RdsGtw => { + let user_with_domain = get_event_value_as_string( + "RdsGtwUsername", + &record.record, + eventkey_alias, + ); + let user = user_with_domain + .rsplit('\\') + .next() + .unwrap_or(&user_with_domain); + CompactString::from(user) + } + }; - let countlist: [usize; 2] = [0, 0]; - // この段階でEventIDは4624もしくは4625となるのでこの段階で対応するカウンターを取得する - let count: &mut [usize; 2] = self - .stats_login_list - .entry(( - username, - hostname, - CompactString::from( - *logontype_map - .get(&logontype.as_str()) - .unwrap_or(&logontype.as_str()), + let src_user = get_event_value_as_string( + "SubjectUserName", + &record.record, + eventkey_alias, + ); + let dst_domain = match channel { + Sec => get_event_value_as_string( + "TargetDomainName", + &record.record, + eventkey_alias, ), - source_computer, - source_ip, - )) - .or_insert(countlist); - if idnum == 4624 { - count[0] += 1; - } else if idnum == 4625 { - count[1] += 1; + RdsLsm => { + let user_with_domain = get_event_value_as_string( + "UserDataUser", + &record.record, + eventkey_alias, + ); + let domain = user_with_domain.rsplit_once('\\').map(|x| x.0); + CompactString::from(domain.unwrap_or("-")) + } + RdsGtw => { + let user_with_domain = get_event_value_as_string( + "RdsGtwUserName", + &record.record, + eventkey_alias, + ); + let domain = user_with_domain.rsplit_once('\\').map(|x| x.0); + CompactString::from(domain.unwrap_or("-")) + } + }; + let src_domain = get_event_value_as_string( + "SubjectDomainName", + &record.record, + eventkey_alias, + ); + let logontype = + get_event_value_as_string("LogonType", &record.record, eventkey_alias); + let hostname = + get_event_value_as_string("Computer", &record.record, eventkey_alias); + let source_computer = get_event_value_as_string( + "WorkstationName", + &record.record, + eventkey_alias, + ); + let source_ip = match channel { + Sec => { + get_event_value_as_string("IpAddress", &record.record, eventkey_alias) + } + RdsLsm => get_event_value_as_string( + "UserDataAddress", + &record.record, + eventkey_alias, + ), + RdsGtw => get_event_value_as_string( + "RdsGtwIpAddress", + &record.record, + eventkey_alias, + ), + }; + + let countlist: [usize; 2] = [0, 0]; + // この段階でEventIDは4624もしくは4625となるのでこの段階で対応するカウンターを取得する + let count: &mut [usize; 2] = self + .stats_login_list + .entry(LoginEvent { + channel: channel_name, + dst_user, + dst_domain, + hostname, + logontype: CompactString::from( + *logontype_map + .get(&logontype.as_str()) + .unwrap_or(&logontype.as_str()), + ), + src_user, + src_domain, + source_computer, + source_ip, + }) + .or_insert(countlist); + if idnum == 4624 || idnum == 21 || idnum == 302 { + count[0] += 1; + } else if idnum == 4625 { + count[1] += 1; + } } }; } } } +fn get_event_value_as_string( + key: &str, + record: &serde_json::Value, + eventkey_alias: &EventKeyAliasConfig, +) -> CompactString { + CompactString::from( + utils::get_serde_number_to_string( + utils::get_event_value(key, record, eventkey_alias).unwrap_or(&serde_json::Value::Null), + false, + ) + .unwrap_or_else(|| "-".into()) + .replace(['"', '\''], ""), + ) +} + +enum Channel { + Sec, + RdsLsm, + RdsGtw, +} + +fn is_target_event(idnum: i64, channel: &str) -> Option { + if (idnum == 4624 || idnum == 4625) && channel == "Security" { + return Some(Sec); + } + if idnum == 21 + && channel == "Microsoft-Windows-TerminalServices-LocalSessionManager/Operational" + { + return Some(RdsLsm); + } + if idnum == 302 && channel == "Microsoft-Windows-TerminalServices-Gateway/Operational" { + return Some(RdsGtw); + } + None +} + #[cfg(test)] mod tests { use std::path::Path; diff --git a/src/timeline/timelines.rs b/src/timeline/timelines.rs index 7922e0f40..94d328140 100644 --- a/src/timeline/timelines.rs +++ b/src/timeline/timelines.rs @@ -344,9 +344,13 @@ impl Timeline { let header_column = make_ascii_titlecase(logon_res); let header = vec![ header_column.as_str(), + "Event", "Target Account", + "Target Domain", "Target Computer", "Logon Type", + "Source Account", + "Source Domain", "Source Computer", "Source IP Address", ]; @@ -378,7 +382,8 @@ impl Timeline { logins_stats_tb .load_preset(UTF8_FULL) .apply_modifier(UTF8_ROUND_CORNERS); - logins_stats_tb.set_header(&header); + let h = &header; + logins_stats_tb.set_header([h[0], h[1], h[2], h[4], h[8], h[9]]); // 集計するログオン結果を設定 let vnum = match logon_res { "successful" => 0, @@ -388,7 +393,7 @@ impl Timeline { // 集計件数でソート let mut mapsorted: Vec<_> = self.stats.stats_login_list.iter().collect(); mapsorted.sort_by(|x, y| y.1[vnum].cmp(&x.1[vnum])); - for ((username, hostname, logontype, source_computer, source_ip), values) in &mapsorted { + for (e, values) in &mapsorted { // 件数が"0"件は表示しない if values[vnum] == 0 { continue; @@ -396,16 +401,21 @@ impl Timeline { let vnum_str = values[vnum].to_string(); let record_data = vec![ vnum_str.as_str(), - username.as_str(), - hostname.as_str(), - logontype.as_str(), - source_computer.as_str(), - source_ip.as_str(), + e.channel.as_str(), + e.dst_user.as_str(), + e.dst_domain.as_str(), + e.hostname.as_str(), + e.logontype.as_str(), + e.src_user.as_str(), + e.src_domain.as_str(), + e.source_computer.as_str(), + e.source_ip.as_str(), ]; if let Some(ref mut w) = wtr { w.write_record(&record_data).ok(); } - logins_stats_tb.add_row(record_data); + let r = record_data; + logins_stats_tb.add_row([r[0], r[1], r[2], r[4], r[8], r[9]]); } } // rowデータがない場合は、検出なしのメッセージを表示する @@ -504,6 +514,7 @@ mod tests { use hashbrown::{HashMap, HashSet}; use nested::Nested; + use crate::timeline::metrics::LoginEvent; use crate::{ detections::{ configs::{ @@ -652,34 +663,33 @@ mod tests { &false, )); - let mut expect: HashMap< - ( - CompactString, - CompactString, - CompactString, - CompactString, - CompactString, - ), - [usize; 2], - > = HashMap::new(); + let mut expect: HashMap = HashMap::new(); expect.insert( - ( - "testuser".into(), - "HAYABUSA-DESKTOP".into(), - "3 - Network".into(), - "HAYABUSA".into(), - "192.168.100.200".into(), - ), + LoginEvent { + channel: "Sec 4624".into(), + dst_user: "testuser".into(), + dst_domain: "-".into(), + hostname: "HAYABUSA-DESKTOP".into(), + logontype: "3 - Network".into(), + src_user: "-".into(), + src_domain: "-".into(), + source_computer: "HAYABUSA".into(), + source_ip: "192.168.100.200".into(), + }, [1, 0], ); expect.insert( - ( - "testuser".into(), - "HAYABUSA-DESKTOP".into(), - "0 - System".into(), - "n/a".into(), - "n/a".into(), - ), + LoginEvent { + channel: "Sec 4625".into(), + dst_user: "testuser".into(), + dst_domain: "-".into(), + hostname: "HAYABUSA-DESKTOP".into(), + logontype: "0 - System".into(), + src_user: "-".into(), + src_domain: "-".into(), + source_computer: "-".into(), + source_ip: "-".into(), + }, [0, 1], ); @@ -904,9 +914,13 @@ mod tests { timeline.tm_logon_stats_dsp_msg(&dummy_stored_static); let mut header = [ "Successful", + "Event", "Target Account", + "Target Domain", "Target Computer", "Logon Type", + "Source Account", + "Source Domain", "Source Computer", "Source IP Address", ]; @@ -914,9 +928,13 @@ mod tests { // Login Successful csv output test let expect_success_records = [[ "1", + "Sec 4624", "testuser", + "-", "HAYABUSA-DESKTOP", "3 - Network", + "-", + "-", "HAYABUSA", "192.168.100.200", ]]; @@ -938,11 +956,15 @@ mod tests { header[0] = "Failed"; let expect_failed_records = [[ "1", + "Sec 4625", "testuser", + "-", "HAYABUSA-DESKTOP", "0 - System", - "n/a", - "n/a", + "-", + "-", + "-", + "-", ]]; let expect_failed = header.join(",") + "\n" From cf023cab20348c8d2ef022076b280b7aa98730f3 Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Thu, 7 Nov 2024 22:44:50 +0900 Subject: [PATCH 2/5] chg: add --thread 1 --- .github/workflows/timeline-diff.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/timeline-diff.yml b/.github/workflows/timeline-diff.yml index 01a9bfd60..ccb75d869 100644 --- a/.github/workflows/timeline-diff.yml +++ b/.github/workflows/timeline-diff.yml @@ -29,8 +29,8 @@ jobs: - name: Run on dev branch run: | cargo run --release -- update-rules -q - cargo run --release -- csv-timeline -d ./hayabusa-sample-evtx -o dev.csv -p super-verbose -q -w -D -n -u -s - cargo run --release -- json-timeline -d ./hayabusa-sample-evtx -o dev.jsonl -L -p super-verbose -q -w -D -n -u -s + cargo run --release -- csv-timeline -t 1 -d ./hayabusa-sample-evtx -o dev.csv -p super-verbose -q -w -D -n -u -s + cargo run --release -- json-timeline -t 1 -d ./hayabusa-sample-evtx -o dev.jsonl -L -p super-verbose -q -w -D -n -u -s - name: Run on dev branch(encoded_rules) run: | @@ -41,8 +41,8 @@ jobs: curl -O https://raw.githubusercontent.com/Yamato-Security/hayabusa-encoded-rules/refs/heads/main/encoded_rules.yml curl -O https://raw.githubusercontent.com/Yamato-Security/hayabusa-encoded-rules/refs/heads/main/rules_config_files.txt ./hayabusa update-rules -q - ./hayabusa csv-timeline -d ./hayabusa-sample-evtx -o dev-encoded.csv -p super-verbose -q -w -D -n -u -s - ./hayabusa json-timeline -d ./hayabusa-sample-evtx -o dev-encoded.jsonl -L -p super-verbose -q -w -D -n -u -s + ./hayabusa csv-timeline -t 1 -d ./hayabusa-sample-evtx -o dev-encoded.csv -p super-verbose -q -w -D -n -u -s + ./hayabusa json-timeline -t 1 -d ./hayabusa-sample-evtx -o dev-encoded.jsonl -L -p super-verbose -q -w -D -n -u -s mv ../config ./ mv ../rules ./ mv encoded_rules.yml ../ @@ -64,8 +64,8 @@ jobs: curl -O https://raw.githubusercontent.com/Yamato-Security/hayabusa-encoded-rules/refs/heads/main/rules_config_files.txt cp target/release/hayabusa . ./hayabusa update-rules -q - ./hayabusa csv-timeline -d ./hayabusa-sample-evtx -o main-encoded.csv -p super-verbose -q -w -D -n -u - ./hayabusa json-timeline -d ./hayabusa-sample-evtx -o main-encoded.jsonl -L -p super-verbose -q -w -D -n -u + ./hayabusa csv-timeline -t 1 -d ./hayabusa-sample-evtx -o main-encoded.csv -p super-verbose -q -w -D -n -u + ./hayabusa json-timeline -t 1 -d ./hayabusa-sample-evtx -o main-encoded.jsonl -L -p super-verbose -q -w -D -n -u rm -rf encoded_rules.yml rules_config_files.txt hayabusa - name: Check CSV Timeline diff From cd47202dd3db5ccd1eb9aba01044a486d6d9cfd8 Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Thu, 7 Nov 2024 23:08:09 +0900 Subject: [PATCH 3/5] chg: add --thread 1 --- .github/workflows/timeline-diff.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/timeline-diff.yml b/.github/workflows/timeline-diff.yml index ccb75d869..8cef35886 100644 --- a/.github/workflows/timeline-diff.yml +++ b/.github/workflows/timeline-diff.yml @@ -52,8 +52,8 @@ jobs: run: | git checkout main cargo run --release -- update-rules -q - cargo run --release -- csv-timeline -d ./hayabusa-sample-evtx -o main.csv -p super-verbose -q -w -D -n -u - cargo run --release -- json-timeline -d ./hayabusa-sample-evtx -o main.jsonl -L -p super-verbose -q -w -D -n -u + cargo run --release -- csv-timeline -t 1 -d ./hayabusa-sample-evtx -o main.csv -p super-verbose -q -w -D -n -u + cargo run --release -- json-timeline -t 1 -d ./hayabusa-sample-evtx -o main.jsonl -L -p super-verbose -q -w -D -n -u - name: Run on main branch(encoded_rules) run: | From 22e609136db35382b7cbbda62566f597f91334cc Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Fri, 8 Nov 2024 09:29:11 +0900 Subject: [PATCH 4/5] fix: Output to error log if EventID cannot be obtained --- src/timeline/metrics.rs | 94 ++++++++++++++++++++++++--------------- src/timeline/timelines.rs | 24 +++++----- 2 files changed, 69 insertions(+), 49 deletions(-) diff --git a/src/timeline/metrics.rs b/src/timeline/metrics.rs index cdf4d48d1..109a79da6 100644 --- a/src/timeline/metrics.rs +++ b/src/timeline/metrics.rs @@ -1,4 +1,5 @@ use crate::detections::message::ERROR_LOG_STACK; +use crate::detections::utils::get_serde_number_to_string; use crate::detections::{ configs::{EventKeyAliasConfig, StoredStatic}, detection::EvtxRecordInfo, @@ -72,20 +73,15 @@ impl EventMetrics { self.stats_eventid(records, stored_static, (include_computer, exclude_computer)); } - pub fn logon_stats_start( - &mut self, - records: &[EvtxRecordInfo], - logon_summary_flag: bool, - eventkey_alias: &EventKeyAliasConfig, - ) { + pub fn logon_stats_start(&mut self, records: &[EvtxRecordInfo], stored_static: &StoredStatic) { // 引数でlogon-summaryオプションが指定されている時だけ、統計情報を出力する。 - if !logon_summary_flag { + if !stored_static.logon_summary_flag { return; } - self.stats_time_cnt(records, eventkey_alias); + self.stats_time_cnt(records, &stored_static.eventkey_alias); - self.stats_login_eventid(records, eventkey_alias); + self.stats_login_eventid(records, stored_static); } fn stats_time_cnt(&mut self, records: &[EvtxRecordInfo], eventkey_alias: &EventKeyAliasConfig) { @@ -199,11 +195,7 @@ impl EventMetrics { } } // Login event - fn stats_login_eventid( - &mut self, - records: &[EvtxRecordInfo], - eventkey_alias: &EventKeyAliasConfig, - ) { + fn stats_login_eventid(&mut self, records: &[EvtxRecordInfo], stored_static: &StoredStatic) { let logontype_map: HashMap<&str, &str> = HashMap::from([ ("0", "0 - System"), ("2", "2 - Interactive"), @@ -219,14 +211,38 @@ impl EventMetrics { ("13", "13 - CachedUnlock"), ]); for record in records.iter() { - if let Some(evtid) = utils::get_event_value("EventID", &record.record, eventkey_alias) { + if let Some(evtid) = + utils::get_event_value("EventID", &record.record, &stored_static.eventkey_alias) + { let idnum: i64 = if evtid.is_number() { evtid.as_i64().unwrap() } else { - evtid.as_str().unwrap().parse::().unwrap_or_default() + let rec_id = get_serde_number_to_string( + &record.record["Event"]["System"]["EventRecordID"], + false, + ) + .unwrap_or("n/a".into()); + let errmsg = format!( + "Failed to parse EventID from EventFile: {}, EventRecordID: {}", + &record.evtx_filepath, rec_id + ); + if stored_static.verbose_flag { + AlertMessage::alert(&errmsg).ok(); + } + if !stored_static.quiet_errors_flag { + ERROR_LOG_STACK + .lock() + .unwrap() + .push(format!("[ERROR] {errmsg}")); + } + continue; }; - let channel = get_event_value_as_string("Channel", &record.record, eventkey_alias); + let channel = get_event_value_as_string( + "Channel", + &record.record, + &stored_static.eventkey_alias, + ); if let Some(channel) = is_target_event(idnum, &channel) { let channel_name = match channel { Sec => { @@ -243,13 +259,13 @@ impl EventMetrics { Sec => get_event_value_as_string( "TargetUserName", &record.record, - eventkey_alias, + &stored_static.eventkey_alias, ), RdsLsm => { let user_with_domain = get_event_value_as_string( "UserDataUser", &record.record, - eventkey_alias, + &stored_static.eventkey_alias, ); let user = user_with_domain .rsplit('\\') @@ -261,7 +277,7 @@ impl EventMetrics { let user_with_domain = get_event_value_as_string( "RdsGtwUsername", &record.record, - eventkey_alias, + &stored_static.eventkey_alias, ); let user = user_with_domain .rsplit('\\') @@ -274,19 +290,19 @@ impl EventMetrics { let src_user = get_event_value_as_string( "SubjectUserName", &record.record, - eventkey_alias, + &stored_static.eventkey_alias, ); let dst_domain = match channel { Sec => get_event_value_as_string( "TargetDomainName", &record.record, - eventkey_alias, + &stored_static.eventkey_alias, ), RdsLsm => { let user_with_domain = get_event_value_as_string( "UserDataUser", &record.record, - eventkey_alias, + &stored_static.eventkey_alias, ); let domain = user_with_domain.rsplit_once('\\').map(|x| x.0); CompactString::from(domain.unwrap_or("-")) @@ -295,7 +311,7 @@ impl EventMetrics { let user_with_domain = get_event_value_as_string( "RdsGtwUserName", &record.record, - eventkey_alias, + &stored_static.eventkey_alias, ); let domain = user_with_domain.rsplit_once('\\').map(|x| x.0); CompactString::from(domain.unwrap_or("-")) @@ -304,30 +320,38 @@ impl EventMetrics { let src_domain = get_event_value_as_string( "SubjectDomainName", &record.record, - eventkey_alias, + &stored_static.eventkey_alias, + ); + let logontype = get_event_value_as_string( + "LogonType", + &record.record, + &stored_static.eventkey_alias, + ); + let hostname = get_event_value_as_string( + "Computer", + &record.record, + &stored_static.eventkey_alias, ); - let logontype = - get_event_value_as_string("LogonType", &record.record, eventkey_alias); - let hostname = - get_event_value_as_string("Computer", &record.record, eventkey_alias); let source_computer = get_event_value_as_string( "WorkstationName", &record.record, - eventkey_alias, + &stored_static.eventkey_alias, ); let source_ip = match channel { - Sec => { - get_event_value_as_string("IpAddress", &record.record, eventkey_alias) - } + Sec => get_event_value_as_string( + "IpAddress", + &record.record, + &stored_static.eventkey_alias, + ), RdsLsm => get_event_value_as_string( "UserDataAddress", &record.record, - eventkey_alias, + &stored_static.eventkey_alias, ), RdsGtw => get_event_value_as_string( "RdsGtwIpAddress", &record.record, - eventkey_alias, + &stored_static.eventkey_alias, ), }; diff --git a/src/timeline/timelines.rs b/src/timeline/timelines.rs index 94d328140..7edcaf6c8 100644 --- a/src/timeline/timelines.rs +++ b/src/timeline/timelines.rs @@ -76,11 +76,7 @@ impl Timeline { ), ); } else if stored_static.logon_summary_flag { - self.stats.logon_stats_start( - records, - stored_static.logon_summary_flag, - &stored_static.eventkey_alias, - ); + self.stats.logon_stats_start(records, stored_static); } else if stored_static.search_flag { self.event_search.search_start( records, @@ -536,7 +532,7 @@ mod tests { /// メトリクスコマンドの統計情報集計のテスト。 Testing of statistics aggregation for metrics commands. #[test] pub fn test_evt_logon_stats() { - let dummy_stored_static = + let mut dummy_stored_static = create_dummy_stored_static(Action::LogonSummary(LogonSummaryOption { input_args: InputOption { directory: None, @@ -572,13 +568,12 @@ mod tests { end_timeline: None, start_timeline: None, })); + dummy_stored_static.logon_summary_flag = true; *STORED_EKEY_ALIAS.write().unwrap() = Some(dummy_stored_static.eventkey_alias.clone()); let mut timeline = Timeline::default(); // レコード情報がないときにはstats_time_cntは何も行わないことをテスト - timeline - .stats - .logon_stats_start(&[], true, &dummy_stored_static.eventkey_alias); + timeline.stats.logon_stats_start(&[], &dummy_stored_static); // テスト1: 対象となるTimestamp情報がない場合 let no_timestamp_record_str = r#"{ @@ -602,7 +597,7 @@ mod tests { )); timeline .stats - .logon_stats_start(&input_datas, true, &dummy_stored_static.eventkey_alias); + .logon_stats_start(&input_datas, &dummy_stored_static); assert!(timeline.stats.start_time.is_none()); assert!(timeline.stats.end_time.is_none()); @@ -695,7 +690,7 @@ mod tests { timeline .stats - .logon_stats_start(&input_datas, true, &dummy_stored_static.eventkey_alias); + .logon_stats_start(&input_datas, &dummy_stored_static); assert_eq!( timeline.stats.start_time, Some(DateTime::::from_naive_utc_and_offset( @@ -814,7 +809,7 @@ mod tests { #[test] pub fn test_tm_logon_stats_dsp_msg() { - let dummy_stored_static = + let mut dummy_stored_static = create_dummy_stored_static(Action::LogonSummary(LogonSummaryOption { input_args: InputOption { directory: None, @@ -850,13 +845,14 @@ mod tests { end_timeline: None, start_timeline: None, })); + dummy_stored_static.logon_summary_flag = true; *STORED_EKEY_ALIAS.write().unwrap() = Some(dummy_stored_static.eventkey_alias.clone()); let mut timeline = Timeline::default(); let mut input_datas = vec![]; let tcreated_attribe_record_str = r#"{ "Event": { "System": { - "EventID": "4624", + "EventID": 4624, "Channel": "Security", "Computer":"HAYABUSA-DESKTOP", "TimeCreated_attributes": { @@ -909,7 +905,7 @@ mod tests { timeline .stats - .logon_stats_start(&input_datas, true, &dummy_stored_static.eventkey_alias); + .logon_stats_start(&input_datas, &dummy_stored_static); timeline.tm_logon_stats_dsp_msg(&dummy_stored_static); let mut header = [ From b8856200ad81aa5b2eb317b9bc7ae2d8eb596210 Mon Sep 17 00:00:00 2001 From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:10:24 +0900 Subject: [PATCH 5/5] update changelog --- CHANGELOG-Japanese.md | 10 ++++++---- CHANGELOG.md | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG-Japanese.md b/CHANGELOG-Japanese.md index b05de7c5a..b41a62eda 100644 --- a/CHANGELOG-Japanese.md +++ b/CHANGELOG-Japanese.md @@ -10,14 +10,16 @@ - `yaml-rust`クレートを`yaml-rust2`に更新した。(#461) (@yamatosecurity) - `windash`文字が、`rules/config/windash_characters.txt`から動的に読み込まれるようになった。(#1440) (@fukusuket) +- `logon-summary`コマンドがRDPイベントからのログオン情報を表示するようになった。注意: ファイルに保存する場合、Hayabusaはより詳細な情報を出力する。(#1468) (@fukusuket) **バグ修正:** -- `csv-timeline`と`json-timeline`コマンドで、結果をターミナルに出力すると、プログレスバーの後にいくつかの結果が表示されていた。 (#1459) (@fukusuket) +- logon-summary`コマンドが破損したログでクラッシュすることがあった。(#1477) (@fukusuket) +- `csv-timeline`と`json-timeline`コマンドで、結果をターミナルに出力すると、プログレスバーの後にいくつかの結果が表示されていた。(#1459) (@fukusuket) - 集計ルールのアラートの詳細フィールド値の結果がソートされていないため、`csv-timeline`と`json-timeline`は、毎回完全に正確な結果を出力しなかった。 (#1466) (@fukusuket) -- `hayabusa-evtx`クレートをバージョン`0.8.12`に更新した。 (@yamatosecurity) - - JSONフィールドの出力順序が元のXMLに従って保持されるようになった。 (omerbenamram/evtx #241) - - 属性と同じ名前を持つ複数のサブノードは上書きされ、最後の1つだけが出力されていた。 (omerbenamram/evtx #245) +- `hayabusa-evtx`クレートをバージョン`0.8.12`に更新した。(@yamatosecurity) + - JSONフィールドの出力順序が元のXMLに従って保持されるようになった。(omerbenamram/evtx #241) + - 属性と同じ名前を持つ複数のサブノードは上書きされ、最後の1つだけが出力されていた。(omerbenamram/evtx #245) ## 2.18.0 [2024/10/23] - SecTor Release diff --git a/CHANGELOG.md b/CHANGELOG.md index d8f9ab3cb..d029149a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,11 @@ - Updated the `yaml-rust` crate to `yaml-rust2`. (#461) (@yamatosecurity) - `windash` characters are now being dynamically read from `rules/config/windash_characters.txt`. (#1440) (@fukusuket) +- `logon-summary` command now displays logon information from RDP events. Note: Hayabusa will output more detailed information when saving to a file. (#1468) (@fukusuket) **Bug Fixes:** +- `logon-summary` command would sometimes crash with corrupted logs. (#1477) (@fukusuket) - Some results would be displayed after the progress bar when outputting results to the terminal with `csv-timeline` and `json-timeline`. (#1459) (@fukusuket) - The detailed field value results in aggregation rule alerts were not sorted so `csv-timeline` and `json-timeline` would not output completely exact results each time. (#1466) (@fukusuket) - Updated `hayabusa-evtx` crate to `0.8.12`. (@yamatosecurity)