Skip to content

Commit

Permalink
analysis: report rule state altered by other rule
Browse files Browse the repository at this point in the history
Flowbits can make a rule such as a packet rule be treated as a stateful
rule, without actually changing the rule type.

Add a flag to allow reporting such cases via engine analysis.

Task #7456
  • Loading branch information
jufajardini committed Jan 31, 2025
1 parent 1c3694e commit 23a840e
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 5 deletions.
30 changes: 29 additions & 1 deletion src/detect-engine-analyzer.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2023 Open Information Security Foundation
/* Copyright (C) 2007-2025 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
Expand Down Expand Up @@ -1047,6 +1047,34 @@ void EngineAnalysisRules2(const DetectEngineCtx *de_ctx, const Signature *s)
break;
}

// dependencies object and its subfields only logged if we have values
if (s->init_data->is_rule_state_dependant) {
jb_open_object(ctx.js, "dependencies");
jb_open_object(ctx.js, "flowbits");
jb_open_object(ctx.js, "upstream");
if (s->init_data->rule_state_dependant_sids_size > 0) {
jb_open_object(ctx.js, "state_modifying_rules");
jb_open_array(ctx.js, "sids");
for (uint32_t i = 0; i < s->init_data->rule_state_dependant_sids_idx; i++) {
jb_append_uint(ctx.js, s->init_data->rule_state_dependant_sids_array[i]);
}
jb_close(ctx.js); // sids
jb_open_array(ctx.js, "names");
for (uint32_t i = 0; i < s->init_data->rule_state_flowbits_ids_size - 1; i++) {
if (s->init_data->rule_state_flowbits_ids_array[i] != 0) {
jb_append_string(ctx.js,
VarNameStoreSetupLookup(s->init_data->rule_state_flowbits_ids_array[i],
VAR_TYPE_FLOW_BIT));
}
}
jb_close(ctx.js); // names
jb_close(ctx.js); // state_modifying_rules
}
jb_close(ctx.js); // upstream
jb_close(ctx.js); // flowbits
jb_close(ctx.js); // dependencies
}

jb_open_array(ctx.js, "flags");
if (s->flags & SIG_FLAG_SRC_ANY) {
jb_append_string(ctx.js, "src_any");
Expand Down
4 changes: 3 additions & 1 deletion src/detect-engine-build.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2023 Open Information Security Foundation
/* Copyright (C) 2007-2025 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
Expand Down Expand Up @@ -2092,6 +2092,8 @@ static int SigMatchPrepare(DetectEngineCtx *de_ctx)
IPOnlyCIDRListFree(s->init_data->cidr_src);

SCFree(s->init_data->buffers);
SCFree(s->init_data->rule_state_dependant_sids_array);
SCFree(s->init_data->rule_state_flowbits_ids_array);
SCFree(s->init_data);
s->init_data = NULL;
}
Expand Down
66 changes: 65 additions & 1 deletion src/detect-flowbits.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2022 Open Information Security Foundation
/* Copyright (C) 2007-2025 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
Expand Down Expand Up @@ -630,6 +630,70 @@ int DetectFlowbitsAnalyze(DetectEngineCtx *de_ctx)
SCLogDebug("GET flowbit %s/%u: SID %u", varname, i, s->id);

s->init_data->init_flags |= SIG_FLAG_INIT_STATE_MATCH;
s->init_data->is_rule_state_dependant = true;

uint32_t sids_array_size = array[i].set_sids_idx;

// save information about flowbits that affect this rule's state
if (s->init_data->rule_state_dependant_sids_array == NULL) {
s->init_data->rule_state_dependant_sids_array =
SCCalloc(sids_array_size, sizeof(uint32_t));
if (s->init_data->rule_state_dependant_sids_array == NULL) {
SCLogError("Failed to allocate memory for rule_state_dependant_ids");
goto end;
}
s->init_data->rule_state_flowbits_ids_size = 1;
s->init_data->rule_state_flowbits_ids_array =
SCCalloc(s->init_data->rule_state_flowbits_ids_size, sizeof(uint32_t));
if (s->init_data->rule_state_flowbits_ids_array == NULL) {
SCLogError("Failed to allocate memory for rule_state_variable_idx");
goto end;
}
s->init_data->rule_state_dependant_sids_size = sids_array_size;
SCLogDebug("alloc'ed array for rule dependency and fbs idx array, sid %u, "
"sizes are %u and %u",
s->id, s->init_data->rule_state_dependant_sids_size,
s->init_data->rule_state_flowbits_ids_size);
} else {
uint32_t new_array_size =
s->init_data->rule_state_dependant_sids_size + sids_array_size;
void *tmp_ptr = SCRealloc(s->init_data->rule_state_dependant_sids_array,
new_array_size * sizeof(uint32_t));
if (tmp_ptr == NULL) {
SCLogError("Failed to allocate memory for rule_state_variable_idx");
goto end;
}
s->init_data->rule_state_dependant_sids_array = tmp_ptr;
s->init_data->rule_state_dependant_sids_size = new_array_size;
SCLogDebug("realloc'ed array for rule dependency, sid %u, new size is %u",
s->id, s->init_data->rule_state_dependant_sids_size);
uint32_t new_fb_array_size = s->init_data->rule_state_flowbits_ids_size + 1;
void *tmp_fb_ptr = SCRealloc(s->init_data->rule_state_flowbits_ids_array,
new_fb_array_size * sizeof(uint32_t));
s->init_data->rule_state_flowbits_ids_array = tmp_fb_ptr;
if (s->init_data->rule_state_flowbits_ids_array == NULL) {
SCLogError("Failed to reallocate memory for rule_state_variable_idx");
goto end;
}
SCLogDebug(
"realloc'ed array for flowbits ids, new size is %u", new_fb_array_size);
s->init_data->rule_state_dependant_sids_size = new_array_size;
s->init_data->rule_state_flowbits_ids_size = new_fb_array_size;
}
for (uint32_t idx = 0; idx < s->init_data->rule_state_dependant_sids_size; idx++) {
if (idx < array[i].set_sids_idx) {
s->init_data->rule_state_dependant_sids_array
[s->init_data->rule_state_dependant_sids_idx] =
de_ctx->sig_array[array[i].set_sids[idx]]->id;
s->init_data->rule_state_dependant_sids_idx++;
}
}
s->init_data
->rule_state_flowbits_ids_array[s->init_data->rule_state_flowbits_ids_size -
1] = i;
s->init_data->rule_state_flowbits_ids_size += 1;
// flowbit info saving for rule made stateful rule work finished

SCLogDebug("made SID %u stateful because it depends on "
"stateful rules that set flowbit %s", s->id, varname);
}
Expand Down
7 changes: 6 additions & 1 deletion src/detect-parse.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2021 Open Information Security Foundation
/* Copyright (C) 2007-2025 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
Expand Down Expand Up @@ -1564,6 +1564,11 @@ Signature *SigAlloc (void)
* overwritten, we can then assign the default value of 3 */
sig->prio = -1;

/* rule interdepency is false, at start */
sig->init_data->is_rule_state_dependant = false;
/* first index is 0 */
sig->init_data->rule_state_dependant_sids_idx = 0;

sig->init_data->list = DETECT_SM_LIST_NOTSET;
return sig;
}
Expand Down
10 changes: 9 additions & 1 deletion src/detect.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2023 Open Information Security Foundation
/* Copyright (C) 2007-2025 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
Expand Down Expand Up @@ -602,6 +602,14 @@ typedef struct SignatureInitData_ {

/* highest list/buffer id which holds a DETECT_CONTENT */
uint32_t max_content_list_id;

/* inter-signature state dependency */
bool is_rule_state_dependant;
uint32_t *rule_state_dependant_sids_array;
uint32_t rule_state_dependant_sids_size;
uint32_t rule_state_dependant_sids_idx;
uint32_t *rule_state_flowbits_ids_array;
uint32_t rule_state_flowbits_ids_size;
} SignatureInitData;

/** \brief Signature container */
Expand Down

0 comments on commit 23a840e

Please sign in to comment.