Event event;
event.sig_info = const_cast<SigInfo*>(&otn->sigInfo);
+ event.buffs_to_dump = otn->buffer_setters;
event.ref_time.tv_sec = p->pkth->ts.tv_sec;
event.ref_time.tv_usec = p->pkth->ts.tv_usec;
event.update_event_id_and_ref(p->context->conf->get_event_log_id());
#include "detect_trace.h"
#include "ips_context.h"
#include "rule_option_types.h"
+#include "treenodes.h"
class Continuation
{
result = detection_option_node_evaluate(root_node, data, cursor);
}
+ if (data.leaf_reached and !data.otn->sigInfo.file_id)
+ {
+ data.p->context->matched_buffers.emplace_back(cursor.get_name(), cursor.buffer(), cursor.size());
+ debug_logf(detection_trace, TRACE_BUFFER, data.p, "Collecting \"%s\" buffer of size %u on continuation root\n",
+ cursor.get_name(), cursor.size());
+ snort::pc.buf_dumps++;
+ }
+
clear_trace_cursor_info();
if (result)
Continuation::recall(state, p);
}
+ if ( eval_data.leaf_reached and !eval_data.otn->sigInfo.file_id and
+ node->option_type != RULE_OPTION_TYPE_LEAF_NODE and
+ ((IpsOption*)node->option_data)->is_buffer_setter() )
+ {
+ debug_logf(detection_trace, TRACE_BUFFER, p, "Collecting \"%s\" buffer of size %u\n",
+ cursor.get_name(), cursor.size());
+ p->context->matched_buffers.emplace_back(cursor.get_name(), cursor.buffer(), cursor.size());
+ pc.buf_dumps++;
+ }
+
// Don't need to reset since it's only checked after we've gone
// through the loop at least once and the result will have
// been set again already
unsigned len;
};
+struct MatchedBuffer
+{
+ MatchedBuffer(const char* const n, const uint8_t* const d, unsigned s) :
+ name(n), data(d), size(s)
+ {}
+ MatchedBuffer() = delete;
+
+ const char* const name = nullptr;
+ const uint8_t* const data = nullptr;
+ unsigned size = 0;
+};
+
// FIXIT-RC event trace should be placed in its own files
void EventTrace_Init();
void EventTrace_Term();
the next PDU. Additionally, if an inspector calls for detection on a single data block (like, a full
attachment in HTTP), continuations can be disabled by providing 'no_flow' flag to file_data buffer or
any other buffer to indicate that the block is complete and no continuations needed.
+
+
+*Matched Buffers Dumps*
+
+Buffers that have been matched during detection are collected and stored on IPS context
+until logging takes place.
+Amount of collected buffers is represented with "detection.buf_dumps" peg count;
+
+Caveats:
+
+1. Only buffers included in the rules explicitly will be collected.
+
+2. For multiple detection cases, all found instances of a matched buffer will be logged,
+ even ones that may not be related to the rule.
}
remove_gadget = false;
assert(post_callbacks.empty());
+ matched_buffers.clear();
}
void IpsContext::set_context_data(unsigned id, IpsContextData* cd)
// Only 5 inspectors currently use the ips context data.
static constexpr unsigned max_ips_id = 8;
+ std::vector<MatchedBuffer> matched_buffers;
+
private:
FlowSnapshot flow = {};
std::vector<IpsContextData*> data;
snort_free(detection_filter);
delete sigInfo.body;
+ delete[] buffer_setters;
delete[] state;
}
OptFpList* opt_func = nullptr;
OutputSet* outputFuncs = nullptr; /* per sid enabled output functions */
snort::IpsOption* agent = nullptr;
+ const char** buffer_setters = nullptr;
OptFpList* normal_fp_only = nullptr;
OptFpList* offload_fp_only = nullptr;
struct sf_timeval32 ref_time = { 0, 0 }; /* reference time for the event reference */
const char* alt_msg = nullptr;
std::string action_string;
+ const char** buffs_to_dump = nullptr;
Event() = default;
Event(SigInfo& si)
{ "packet", Parameter::PT_BOOL, nullptr, "false",
"output packet dump with alert" },
+ { "buffers", Parameter::PT_BOOL, nullptr, "false",
+ "output IPS buffer dump" },
+
+ { "buffers_depth", Parameter::PT_INT, "0:maxSZ", "0",
+ "number of IPS buffer bytes to dump per buffer (0 is unlimited)" },
+
{ "limit", Parameter::PT_INT, "0:maxSZ", "0",
"set maximum size in MB before rollover (0 is unlimited)" },
public:
size_t limit = 0;
+ size_t buffers_depth = 0;
bool file = false;
bool packet = false;
+ bool buffers = false;
};
bool FastModule::set(const char*, Value& v, SnortConfig*)
else if ( v.is("packet") )
packet = v.get_bool();
+ else if ( v.is("buffers") )
+ buffers = v.get_bool();
+
else if ( v.is("limit") )
limit = v.get_size() * 1024 * 1024;
+ else if ( v.is("buffers_depth") )
+ buffers_depth = v.get_size();
+
return true;
}
file = false;
limit = 0;
packet = false;
+ buffers = false;
+ buffers_depth = 0;
return true;
}
LogNetData(log, (const uint8_t*)buf.c_str(), len, p, buf_name, ins_name);
}
+static bool should_dump_buffer(const char* buf_name, const char** buffs_to_dump)
+{
+ if ( !buf_name )
+ return false;
+
+ size_t cmp_idx = 0;
+
+ while ( buffs_to_dump[cmp_idx] )
+ if ( !strcmp(buf_name, buffs_to_dump[cmp_idx++]) )
+ return true;
+
+ return false;
+}
+
+static void log_ips_buffers(Packet* p, const char** buffs_to_dump, unsigned long depth)
+{
+ if ( !buffs_to_dump or !buffs_to_dump[0] )
+ return;
+
+ auto& all_buffs = p->context->matched_buffers;
+ std::vector<MatchedBuffer*> to_dump;
+
+ for ( auto& b : all_buffs )
+ if ( should_dump_buffer(b.name, buffs_to_dump) and to_dump.cend() ==
+ find_if(to_dump.begin(), to_dump.end(), [b](MatchedBuffer*& cmp_b)
+ {
+ bool same_buffers = cmp_b->name == b.name and cmp_b->data == b.data;
+ if ( same_buffers and cmp_b->size < b.size )
+ {
+ cmp_b->size = b.size;
+ return true;
+ }
+
+ return same_buffers;
+ }) )
+ to_dump.push_back(&b);
+
+ for ( auto b : to_dump )
+ {
+ const char* buf_name = b->name;
+
+ if ( !buf_name )
+ continue;
+
+ int log_depth = depth && depth < b->size ? depth : b->size;
+ ObfuscateLogNetData(fast_log, b->data, log_depth, p, buf_name, buf_name, "detection");
+ }
+}
+
using BufferIds = std::vector<unsigned>;
//-------------------------------------------------------------------------
private:
string file;
unsigned long limit;
+ unsigned long buffers_depth;
bool packet;
+ bool log_buffers;
static std::vector<unsigned> req_ids;
static std::vector<unsigned> rsp_ids;
std::vector<unsigned> FastLogger::req_ids;
std::vector<unsigned> FastLogger::rsp_ids;
-FastLogger::FastLogger(FastModule* m) : file(m->file ? F_NAME : "stdout"), limit(m->limit), packet(m->packet)
+FastLogger::FastLogger(FastModule* m) : file(m->file ? F_NAME : "stdout"), limit(m->limit),
+ buffers_depth(m->buffers_depth), packet(m->packet), log_buffers(m->buffers)
{ }
//-----------------------------------------------------------------
if ( buf.len and event.sig_info->gid != 116 )
LogNetData(fast_log, buf.data, buf.len, p, "alt");
+
+ if ( log_buffers )
+ log_ips_buffers(p, event.buffs_to_dump, buffers_depth);
}
//-------------------------------------------------------------------------
static std::string s_type;
static std::string s_body;
+static std::list<const char*> buffer_setters;
+
static bool action_file_id = false;
static bool fp_file_id = false;
static bool only_file_data = true;
fp_file_id = true;
}
+ // only add unique buffer setters
+ if ( !action_file_id and ips and ips->is_buffer_setter() and
+ std::find(buffer_setters.cbegin(), buffer_setters.cend(), ips->get_name()) == buffer_setters.cend() )
+ buffer_setters.emplace_back(ips->get_name());
+
if ( type != OPT_TYPE_META )
otn->num_detection_opts++;
s_body = "(";
buf_is_set = false;
+ buffer_setters.clear();
+
return otn;
}
if ( otn->sigInfo.message.empty() )
otn->sigInfo.message = "\"no msg in rule\"";
+ if ( !otn->sigInfo.file_id and buffer_setters.size() )
+ {
+ otn->buffer_setters = new const char*[buffer_setters.size() + 1];
+
+ size_t idx = 0;
+ for (auto& setter : buffer_setters)
+ {
+ otn->buffer_setters[idx++] = setter;
+ }
+ otn->buffer_setters[idx] = nullptr;
+ }
+
OptFpList* fpl = AddOptFuncToList(nullptr, otn);
fpl->type = RULE_OPTION_TYPE_LEAF_NODE;
{ CountType::MAX, "cont_max_num", "peak number of simultaneous continuations per flow" },
{ CountType::SUM, "cont_match_distance", "total number of bytes jumped over by matched continuations"},
{ CountType::SUM, "cont_mismatch_distance", "total number of bytes jumped over by mismatched continuations"},
+ { CountType::SUM, "buf_dumps", "total number of IPS buffers collected from matched rules" },
{ CountType::END, nullptr, nullptr }
};
PegCount cont_max_num;
PegCount cont_match_distance;
PegCount cont_mismatch_distance;
+ PegCount buf_dumps;
};
struct ProcessCount