Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bug] aggregation condition rule count does not show up in Events with hits #1375

Closed
fukusuket opened this issue Jun 25, 2024 · 2 comments · Fixed by #1384
Closed

[bug] aggregation condition rule count does not show up in Events with hits #1375

fukusuket opened this issue Jun 25, 2024 · 2 comments · Fixed by #1384
Assignees
Labels
bug Something isn't working
Milestone

Comments

@fukusuket
Copy link
Collaborator

fukusuket commented Jun 25, 2024

Describe the bug
aggregation condition rule count does not show up in Events with hits(and Top 5 computers)
It's probably the similar cause as #1373, but I'll create a separate issue to make it easier to understand.

Step to Reproduce
./hayabusa-2.16.0-mac-aarch64 csv-timeline -d ../hayabusa-sample-evtx -r rules/hayabusa/builtin/Security/LogonLogoff/Logon/Sec_4625_Med_LogonFail_WrongPW_PW-Guessing_Cnt.yml -w

ref: Sec_4625_Med_LogonFail_WrongPW_PW-Guessing_Cnt.yml

Actuail behavior

...
Timestamp · RuleTitle · Level · Computer · Channel · EventID · RecordID · Details · ExtraFieldInfo
2016-09-20 01:50:06.513 +09:00 · PW Guessing · med · - · - · - · - · Count: 3558 ¦ IpAddress: 192.168.198.149 · -
...
Results Summary:

Events with hits / Total events: 0 / 26,341 (Data reduction: 26,341 events (100.00%))

Total | Unique detections: 1 | 1
Total | Unique critical detections: 0 (0.00%) | 0 (0.00%)
Total | Unique high detections: 0 (0.00%) | 0 (0.00%)
Total | Unique medium detections: 1 (100.00%) | 1 (0.00%)
Total | Unique low detections: 0 (0.00%) | 0 (100.00%)
Total | Unique informational detections: 0 (0.00%) | 0 (0.00%)

Dates with most total detections:
critical: n/a, high: n/a, medium: 2016-09-20 (1), low: n/a, informational: n/a

Top 5 computers with most unique detections:
critical: n/a
high: n/a
medium: n/a
low: n/a
informational: n/a

Expected behavior

...
Timestamp · RuleTitle · Level · Computer · Channel · EventID · RecordID · Details · ExtraFieldInfo
2016-09-20 01:50:06.513 +09:00 · PW Guessing · med · - · - · - · - · Count: 3558 ¦ IpAddress: 192.168.198.149 · -
...
Results Summary:

Events with hits / Total events: 1 / 26,341 (Data reduction: 26,341 events (100.00%))

Total | Unique detections: 1 | 1
Total | Unique critical detections: 0 (0.00%) | 0 (0.00%)
Total | Unique high detections: 0 (0.00%) | 0 (0.00%)
Total | Unique medium detections: 1 (100.00%) | 1 (0.00%)
Total | Unique low detections: 0 (0.00%) | 0 (100.00%)
Total | Unique informational detections: 0 (0.00%) | 0 (0.00%)

Dates with most total detections:
critical: n/a, high: n/a, medium: 2016-09-20 (1), low: n/a, informational: n/a

Top 5 computers with most unique detections:
critical: n/a
high: n/a
medium: somehostname
low: n/a
informational: n/a

Environment

  • OS: macOS Sonoma 14.5
  • hayabusa version: 2.16.0
    • Although unconfirmed, I think this is probably a problem that has existed since the initial version

Additional context
This seems to be because the aggregation condition rule is not counted in the line below.

hayabusa/src/afterfact.rs

Lines 500 to 502 in efaca57

if !detect_info.is_condition {
afterfact_info
.detected_record_idset

@fukusuket
Copy link
Collaborator Author

fukusuket commented Jul 7, 2024

Current impl memo:

Related Struct

detection.rs#L44-L51

#[derive(Clone, Debug)]
pub struct EvtxRecordInfo {
pub evtx_filepath: String, // イベントファイルのファイルパス ログで出力するときに使う
pub record: Value, // 1レコード分のデータをJSON形式にシリアライズしたもの
pub data_string: String, //1レコード内のデータを文字列にしたもの
pub key_2_value: HashMap<String, String>, // 階層化されたキーを.でつないだデータとその値のマップ
pub recovered_record: bool, // レコードが復元されたかどうか
}

message.rs#L32-L45

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct DetectInfo {
pub detected_time: DateTime<Utc>,
pub rulepath: CompactString,
pub ruleid: CompactString,
pub ruletitle: CompactString,
pub level: CompactString,
pub computername: CompactString,
pub eventid: CompactString,
pub detail: CompactString,
pub ext_field: Vec<(CompactString, Profile)>,
pub is_condition: bool,
pub details_convert_map: HashMap<CompactString, Vec<CompactString>>,
}

count.rs#L221-L226

#[derive(Clone, Debug)]
/// countの括弧内の情報とレコードの情報を所持する構造体
pub struct AggRecordTimeInfo {
pub field_record_value: String,
pub record_time: DateTime<Utc>,
}
#[derive(Debug)]
/// timeframeに設定された情報。SIGMAルール上timeframeで複数の単位(日、時、分、秒)が複合で記載されているルールがなかったためタイプと数値のみを格納する構造体
pub struct TimeFrameInfo {
pub timetype: String,
pub timenum: Result<i64, ParseIntError>,
}

mod.rs#L30-L35

pub struct RuleNode {
pub rulepath: String,
pub yaml: Yaml,
detection: DetectionNode,
countdata: HashMap<String, Vec<AggRecordTimeInfo>>,
}

mod.rs#L375-L386

pub struct AggResult {
/// countなどの値
pub data: i64,
/// count byで指定された条件のレコード内での値
pub key: String,
/// countの括弧内指定された項目の検知されたレコード内での値の配列。括弧内で指定がなかった場合は長さ0の配列となる
pub field_values: Vec<String>,
///検知したブロックの最初のレコードの時間
pub start_timedate: DateTime<Utc>,
///条件式の情報
pub condition_op_num: String,
}

Related Sequence

count up

rule/mod.rs#L85-L104

pub fn select(
&mut self,
event_record: &EvtxRecordInfo,
verbose_flag: bool,
quiet_errors_flag: bool,
json_input_flag: bool,
eventkey_alias: &EventKeyAliasConfig,
) -> bool {
let result = self.detection.select(event_record, eventkey_alias);
if result && self.has_agg_condition() {
count::count(
self,
&event_record.record,
verbose_flag,
quiet_errors_flag,
json_input_flag,
);
}
result
}
/// aggregation conditionが存在するかを返す関数
pub fn has_agg_condition(&self) -> bool {
self.detection.aggregation_condition.is_some()
}
/// Aggregation Conditionの結果を配列で返却する関数
pub fn judge_satisfy_aggcondition(&self, stored_static: &StoredStatic) -> Vec<AggResult> {

rule/count.rs#L20-L74

pub fn count(
rule: &mut RuleNode,
record: &Value,
verbose_flag: bool,
quiet_errors_flag: bool,
json_input_flag: bool,
) {
let key: String = create_count_key(
rule,
record,
verbose_flag,
quiet_errors_flag,
STORED_EKEY_ALIAS.read().unwrap().as_ref().unwrap(),
);
let binding = String::default();
let field_name = match rule.get_agg_condition() {
None => "",
Some(aggcondition) => aggcondition
._field_name
.as_ref()
.unwrap_or(&binding)
.as_str(),
};
let field_value = get_alias_value_in_record(
rule,
field_name,
record,
false,
verbose_flag,
quiet_errors_flag,
STORED_EKEY_ALIAS.read().unwrap().as_ref().unwrap(),
)
.unwrap_or_default();
let default_time = Utc.with_ymd_and_hms(1977, 1, 1, 0, 0, 0).unwrap();
countup(
rule,
key,
field_value,
message::get_event_time(record, json_input_flag).unwrap_or(default_time),
);
}
///count byの条件に合致する検知済みレコードの数を増やすための関数
pub fn countup(
rule: &mut RuleNode,
key: String,
field_value: String,
record_time_value: DateTime<Utc>,
) {
let value_map = rule.countdata.entry(key).or_default();
value_map.push(AggRecordTimeInfo {
field_record_value: field_value,
record_time: record_time_value,
});
}

main.rs#L1576

let mut log_records = detection.add_aggcondition_msges(&self.rt, stored_static);

output rule count

detection.rs#L763

fn create_agg_log_record(
rule: &RuleNode,
agg_result: AggResult,
stored_static: &StoredStatic,
) -> DetectInfo {
let tag_info: &Nested<String> = &Detection::get_tag_info(rule);
let output = Detection::create_count_output(rule, &agg_result);
let mut profile_converter: HashMap<&str, Profile> = HashMap::new();
let level = rule.yaml["level"].as_str().unwrap_or("-").to_string();
let tags_config_values: Vec<&CompactString> = TAGS_CONFIG.values().collect();
let is_json_timeline = matches!(stored_static.config.action, Some(Action::JsonTimeline(_)));
for (key, profile) in stored_static.profiles.as_ref().unwrap().iter() {

afterfact.rs#L500-L502

hayabusa/src/afterfact.rs

Lines 500 to 502 in efaca57

if !detect_info.is_condition {
afterfact_info
.detected_record_idset

@fukusuket
Copy link
Collaborator Author

fukusuket commented Jul 7, 2024

New impl memo

 #[derive(Debug, Clone, PartialEq, Eq, Default)] 
/// countなどのaggregationの結果を出力する構造体
pub struct AggResult {
    /// countなどの値
    pub data: i64,
    /// count byで指定された条件のレコード内での値
    pub key: String,
    /// countの括弧内指定された項目の検知されたレコード内での値の配列。括弧内で指定がなかった場合は長さ0の配列となる
    pub field_values: Vec<String>,
    ///検知したブロックの最初のレコードの時間とEventID
    pub id_time_pair : Vec<(String, DateTime<Utc>)>
}
 #[derive(Debug, Clone, PartialEq, Eq, Default)] 
 pub struct DetectInfo { 
     pub detected_time: DateTime<Utc>, 
     pub rulepath: CompactString, 
     pub ruleid: CompactString, 
     pub ruletitle: CompactString, 
     pub level: CompactString, 
     pub computername: CompactString, 
     pub eventid: CompactString, 
     pub detail: CompactString, 
     pub ext_field: Vec<(CompactString, Profile)>, 
     pub agg_result: Option<Aggresult>, 
     pub details_convert_map: HashMap<CompactString, Vec<CompactString>>, 
 } 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
1 participant